Bridging VPCs: A Practical Guide to VPC Peering in AWS

Kevin Kiruri
16 min readOct 26, 2023

--

Navigating the vast ecosystem of Amazon Web Services (AWS) can be a daunting task, especially when it comes to building interconnected and efficient cloud infrastructure. VPC Peering, a feature provided by AWS, is the key to unlocking this potential. It allows you to establish direct, secure connections between Virtual Private Clouds (VPCs), enabling them to function as a unified network while maintaining their own isolated environments. In this blog, our primary focus is to provide a comprehensive, step-by-step guide on how to create VPC Peering connections in AWS. Whether you’re a seasoned cloud architect or an AWS novice, this blog will equip you with the knowledge and tools needed to harness the power of VPC Peering, transforming your cloud infrastructure into a well-connected, dynamic ecosystem. Join us on this journey to become a VPC Peering pro and elevate your AWS network to new heights.

Prerequisites

  1. Have an AWS account. If you don’t have one, sign up here and enjoy the benefits of the Free-Tier Account
  2. View project files in my GitHub portfolio

Networking Prerequisites

Install the following CloudFormation template to create IAM roles for EC2 instances and Flow Logs and an S3 Bucket for endpoint policy tests

  1. Create the following .yaml file on your computer (We will be uploading it shortly). Alternatively, download the file from here
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create IAM roles for EC2 instances and Flow Logs and an S3 Bucket for endpoint policy tests"

Resources:
# Account Level Resources
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: "NetworkingWorkshopEC2Role"
Path: "/"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
- arn:aws:iam::aws:policy/AmazonS3FullAccess
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole

EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: "NetworkingWorkshopInstanceProfile"
Path: "/"
Roles:
- !Ref EC2Role

FlowLogsRole:
Type: AWS::IAM::Role
Properties:
RoleName: NetworkingWorkshopFlowLogsRole
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- vpc-flow-logs.amazonaws.com
Action:
- 'sts:AssumeRole'
Description: Role to allow VPC Flow Logs to write to CloudWatch logs
Policies:
- PolicyName: CloudWatchLogsWrite
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
- "logs:DescribeLogGroups"
- "logs:DescribeLogStreams"
Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:NetworkingWorkshopFlowLogsGroup:*'

GatewayEndpointBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'networking-day-${AWS::Region}-${AWS::AccountId}'
LifecycleConfiguration:
Rules:
- ExpirationInDays: 3
Status: Enabled

2. On the AWS Console, search for ‘CloudFormation’ and open the service

3. Click on ‘Create stack’

4. Under ‘Prepare template’, select ‘Template is ready’, the select ‘Upload a template file’. Click on ‘Choose file’ and browse your system to upload the file we created or downloaded in Step 1. Then click ‘Next’

5. Give the stack a name then click on ‘Next’ at the bottom

6. On the ‘Configure stack options’ page, scroll down and click on ‘Next’

7. On the review page, scroll down and click on ‘Submit’

8. The template starts running to create the resources with the status ‘CREATE_IN_PROGRESS’

9. Wait for the Stack to hava a status of ‘CREATE_COMPLETE’

Creating 3 VPCs

We will start with creating 3 VPCs. We shall create the 3 VPCs using a CloudFormation template for speed and efficiency. Cheers to Infrastructure as Code (IaC) 😉

  1. Create the following .yaml file on your computer (We will upload it shortly). Alternatively, you can download it from here. The template creates VPC A, VPC B and VPC C each with an IGW, NATGW and public and private subnets in 2AZs using 10.0.0.0/16, 10.1.0.0/16 and 10.2.0.0/16
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create VPC A, VPC B and VPC C each with an IGW, NATGW and public and private subnets in 2AZs using 10.0.0.0/16, 10.1.0.0/16 and 10.2.0.0/16"

Metadata:
"AWS::CloudFormation::Interface":
ParameterGroups:
- Label:
default: "VPC Parameters"
Parameters:
- AvailabilityZoneA
- AvailabilityZoneB

Parameters:
AvailabilityZoneA:
Description: Availability Zone 1
Type: AWS::EC2::AvailabilityZone::Name
Default: us-east-1a
AvailabilityZoneB:
Description: Availability Zone 2
Type: AWS::EC2::AvailabilityZone::Name
Default: us-east-1b
AMI:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Description: 'The ID of the AMI.'
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64

Resources:
# VPC A Resources
VpcA:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsSupport: "true"
EnableDnsHostnames: "true"
InstanceTenancy: default
Tags:
- Key: Name
Value: "VPC A"

VpcAPublicSubnet1:
Type: AWS::EC2::Subnet
DependsOn:
- VpcA
Properties:
AvailabilityZone: !Ref AvailabilityZoneA
CidrBlock: "10.0.0.0/24"
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: "VPC A Public Subnet AZ1"
VpcId: !Ref VpcA

VpcAPublicSubnet2:
Type: AWS::EC2::Subnet
DependsOn:
- VpcA
Properties:
AvailabilityZone: !Ref AvailabilityZoneB
CidrBlock: "10.0.2.0/24"
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: "VPC A Public Subnet AZ2"
VpcId: !Ref VpcA

VpcAPublicSubnetRouteTable:
Type: AWS::EC2::RouteTable
DependsOn: VpcA
Properties:
VpcId: !Ref VpcA
Tags:
- Key: Name
Value: "VPC A Public Route Table"

VpcAPublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- VpcAPublicSubnetRouteTable
- VpcAPublicSubnet1
Properties:
RouteTableId: !Ref VpcAPublicSubnetRouteTable
SubnetId: !Ref VpcAPublicSubnet1

VpcAPublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- VpcAPublicSubnetRouteTable
- VpcAPublicSubnet2
Properties:
RouteTableId: !Ref VpcAPublicSubnetRouteTable
SubnetId: !Ref VpcAPublicSubnet2

VpcAPrivateSubnet1:
Type: AWS::EC2::Subnet
DependsOn:
- VpcA
Properties:
AvailabilityZone: !Ref AvailabilityZoneA
CidrBlock: "10.0.1.0/24"
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC A Private Subnet AZ1"
VpcId: !Ref VpcA

VpcAPrivateSubnet2:
Type: AWS::EC2::Subnet
DependsOn:
- VpcA
Properties:
AvailabilityZone: !Ref AvailabilityZoneB
CidrBlock: "10.0.3.0/24"
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC A Private Subnet AZ2"
VpcId: !Ref VpcA

VpcAPrivateSubnetRouteTable:
Type: AWS::EC2::RouteTable
DependsOn: VpcA
Properties:
VpcId: !Ref VpcA
Tags:
- Key: Name
Value: "VPC A Private Route Table"

VpcAPrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- VpcAPrivateSubnetRouteTable
- VpcAPrivateSubnet1
Properties:
RouteTableId: !Ref VpcAPrivateSubnetRouteTable
SubnetId: !Ref VpcAPrivateSubnet1

VpcAPrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- VpcAPrivateSubnetRouteTable
- VpcAPrivateSubnet2
Properties:
RouteTableId: !Ref VpcAPrivateSubnetRouteTable
SubnetId: !Ref VpcAPrivateSubnet2

VpcAWorkloadSubnetsNacl:
Type: AWS::EC2::NetworkAcl
DependsOn: VpcA
Properties:
VpcId: !Ref VpcA
Tags:
- Key: Name
Value: "VPC A Workload Subnets NACL"

VpcAWorkloadSubnetsNaclInboundRule:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref VpcAWorkloadSubnetsNacl
RuleNumber: 100
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0

VpcAWorkloadSubnetsNaclOutboundRule:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref VpcAWorkloadSubnetsNacl
RuleNumber: 100
Protocol: -1
Egress: true
RuleAction: allow
CidrBlock: 0.0.0.0/0

VpcANetworkAclAssociationPublicSubnet1:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- VpcAPublicSubnet1
- VpcAWorkloadSubnetsNacl
Properties:
SubnetId: !Ref VpcAPublicSubnet1
NetworkAclId: !Ref VpcAWorkloadSubnetsNacl

VpcANetworkAclAssociationPublicSubnet2:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- VpcAPublicSubnet2
- VpcAWorkloadSubnetsNacl
Properties:
SubnetId: !Ref VpcAPublicSubnet2
NetworkAclId: !Ref VpcAWorkloadSubnetsNacl

VpcANetworkAclAssociationPrivateSubnet1:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- VpcAPrivateSubnet1
- VpcAWorkloadSubnetsNacl
Properties:
SubnetId: !Ref VpcAPrivateSubnet1
NetworkAclId: !Ref VpcAWorkloadSubnetsNacl

VpcANetworkAclAssociationPrivateSubnet2:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- VpcAPrivateSubnet2
- VpcAWorkloadSubnetsNacl
Properties:
SubnetId: !Ref VpcAPrivateSubnet2
NetworkAclId: !Ref VpcAWorkloadSubnetsNacl

VpcAInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: "VPC A IGW"

VpcAInternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
DependsOn:
- VpcA
- VpcAInternetGateway
Properties:
VpcId: !Ref VpcA
InternetGatewayId: !Ref VpcAInternetGateway

VpcADirectInternetRoute:
Type: AWS::EC2::Route
DependsOn:
- VpcAInternetGateway
- VpcAPublicSubnetRouteTable
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref VpcAInternetGateway
RouteTableId: !Ref VpcAPublicSubnetRouteTable

VpcANatEip:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc

VpcANatGateway:
Type: "AWS::EC2::NatGateway"
DependsOn:
- VpcAPublicSubnet1
- VpcANatEip
Properties:
AllocationId:
Fn::GetAtt:
- VpcANatEip
- AllocationId
SubnetId: !Ref VpcAPublicSubnet1
Tags:
- Key: Name
Value: "VPC A NATGW"

VpcANatInternetRoutePrivate:
Type: AWS::EC2::Route
DependsOn:
- VpcANatGateway
- VpcAPrivateSubnetRouteTable
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref VpcANatGateway
RouteTableId: !Ref VpcAPrivateSubnetRouteTable

VpcAEc2SecGroup:
Type: AWS::EC2::SecurityGroup
DependsOn: VpcA
Properties:
GroupDescription: Open-up ports for ICMP
GroupName: "VPC A Security Group"
VpcId: !Ref VpcA
SecurityGroupIngress:
- IpProtocol: icmp
CidrIp: 0.0.0.0/0
FromPort: "-1"
ToPort: "-1"

VpcAPublicServerEc2:
Type: AWS::EC2::Instance
DependsOn:
- VpcAEc2SecGroup
- VpcAPublicSubnet2
Properties:
IamInstanceProfile: "NetworkingWorkshopInstanceProfile"
ImageId: !Ref AMI
InstanceType: t2.micro
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: "0"
GroupSet:
- !Ref VpcAEc2SecGroup
PrivateIpAddress: 10.0.2.100
SubnetId: !Ref VpcAPublicSubnet2
Tags:
- Key: Name
Value: "VPC A Public AZ2 Server"

VpcAPrivateServerEc2:
Type: AWS::EC2::Instance
DependsOn:
- VpcAEc2SecGroup
- VpcAPrivateSubnet1
Properties:
SubnetId: !Ref VpcAPrivateSubnet1
ImageId: !Ref AMI
InstanceType: t2.micro
PrivateIpAddress: 10.0.1.100
SecurityGroupIds:
- Ref: VpcAEc2SecGroup
IamInstanceProfile: "NetworkingWorkshopInstanceProfile"
Tags:
- Key: Name
Value: "VPC A Private AZ1 Server"

# VPC A Endpoints
EndpointS3:
Type: AWS::EC2::VPCEndpoint
DependsOn: VpcAPrivateSubnetRouteTable
Properties:
RouteTableIds:
- !Ref VpcAPrivateSubnetRouteTable
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
VpcEndpointType: 'Gateway'
VpcId: !Ref VpcA

EndpointKMS:
Type: AWS::EC2::VPCEndpoint
DependsOn:
- VpcAPrivateSubnet1
- VpcAPrivateSubnet2
Properties:
PrivateDnsEnabled: True
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.kms'
SubnetIds:
- !Ref VpcAPrivateSubnet1
- !Ref VpcAPrivateSubnet2
VpcEndpointType: 'Interface'
VpcId: !Ref VpcA

# VPC B Resources
VPCB:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.1.0.0/16"
EnableDnsSupport: "true"
EnableDnsHostnames: "true"
InstanceTenancy: default
Tags:
- Key: Name
Value: "VPC B"

PublicSubnet1VPCB:
Type: AWS::EC2::Subnet
DependsOn:
- VPCB
Properties:
VpcId:
Ref: VPCB
CidrBlock: "10.1.0.0/24"
AvailabilityZone:
Ref: AvailabilityZoneA
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: "VPC B Public Subnet AZ1"

PublicSubnet2VPCB:
Type: AWS::EC2::Subnet
DependsOn:
- VPCB
Properties:
VpcId:
Ref: VPCB
CidrBlock: "10.1.2.0/24"
AvailabilityZone:
Ref: AvailabilityZoneB
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: "VPC B Public Subnet AZ2"

PublicSubnetRouteTableVPCB:
Type: AWS::EC2::RouteTable
DependsOn: VPCB
Properties:
VpcId:
Ref: VPCB
Tags:
- Key: Name
Value: "VPC B Public Route Table"

PublicASubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PublicSubnetRouteTableVPCB
- PublicSubnet1VPCB
Properties:
RouteTableId:
Ref: PublicSubnetRouteTableVPCB
SubnetId:
Ref: PublicSubnet1VPCB

PublicBSubnetRouteTableAssociationVPCB:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PublicSubnetRouteTableVPCB
- PublicSubnet2VPCB
Properties:
RouteTableId:
Ref: PublicSubnetRouteTableVPCB
SubnetId:
Ref: PublicSubnet2VPCB

PrivateSubnet1VPCB:
Type: AWS::EC2::Subnet
DependsOn:
- VPCB
Properties:
VpcId:
Ref: VPCB
CidrBlock: "10.1.1.0/24"
AvailabilityZone:
Ref: AvailabilityZoneA
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC B Private Subnet AZ1"

PrivateSubnet2VPCB:
Type: AWS::EC2::Subnet
DependsOn:
- VPCB
Properties:
VpcId:
Ref: VPCB
CidrBlock: "10.1.3.0/24"
AvailabilityZone:
Ref: AvailabilityZoneB
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC B Private Subnet AZ2"

PrivateSubnetRouteTableVPCB:
Type: AWS::EC2::RouteTable
DependsOn: VPCB
Properties:
VpcId:
Ref: VPCB
Tags:
- Key: Name
Value: "VPC B Private Route Table"

PrivateASubnetRouteTableAssociationVPCB:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PrivateSubnetRouteTableVPCB
- PrivateSubnet1VPCB
Properties:
RouteTableId:
Ref: PrivateSubnetRouteTableVPCB
SubnetId:
Ref: PrivateSubnet1VPCB

PrivateBSubnetRouteTableAssociationVPCB:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PrivateSubnetRouteTableVPCB
- PrivateSubnet2VPCB
Properties:
RouteTableId:
Ref: PrivateSubnetRouteTableVPCB
SubnetId:
Ref: PrivateSubnet2VPCB

InternetGatewayVPCB:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: "VPC B IGW"

AttachGatewayVPCB:
Type: AWS::EC2::VPCGatewayAttachment
DependsOn:
- InternetGatewayVPCB
- VPCB
Properties:
VpcId:
Ref: VPCB
InternetGatewayId:
Ref: InternetGatewayVPCB

DirectInternetRouteVPCB:
Type: AWS::EC2::Route
DependsOn:
- AttachGatewayVPCB
- PublicSubnetRouteTableVPCB
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: InternetGatewayVPCB
RouteTableId:
Ref: PublicSubnetRouteTableVPCB

VPCBNATEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc

VPCBNATGateway:
DependsOn:
- AttachGatewayVPCB
- PublicSubnet1VPCB
Type: "AWS::EC2::NatGateway"
Properties:
AllocationId:
Fn::GetAtt:
- VPCBNATEIP
- AllocationId
SubnetId:
Ref: PublicSubnet1VPCB
Tags:
- Key: Name
Value: "VPC B NATGW"

VPCBNATInternetRoutePrivate:
Type: AWS::EC2::Route
DependsOn:
- VPCBNATGateway
- PrivateSubnetRouteTableVPCB
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId:
Ref: VPCBNATGateway
RouteTableId:
Ref: PrivateSubnetRouteTableVPCB

AttachmentSubnetAVPCB:
Type: AWS::EC2::Subnet
DependsOn:
- VPCB
Properties:
VpcId:
Ref: VPCB
CidrBlock: "10.1.5.0/28"
AvailabilityZone:
Ref: AvailabilityZoneA
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC B TGW Subnet AZ1"

AttachmentSubnetBVPCB:
Type: AWS::EC2::Subnet
DependsOn:
- VPCB
Properties:
VpcId:
Ref: VPCB
CidrBlock: "10.1.5.16/28"
AvailabilityZone:
Ref: AvailabilityZoneB
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC B TGW Subnet AZ2"

AttachmentSubnetRouteTableVPCB:
Type: AWS::EC2::RouteTable
DependsOn: VPCB
Properties:
VpcId:
Ref: VPCB
Tags:
- Key: Name
Value: "VPC B TGW Route Table"

AttachmentASubnetRouteTableAssociationVPCB:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- AttachmentSubnetRouteTableVPCB
- AttachmentSubnetAVPCB
Properties:
RouteTableId:
Ref: AttachmentSubnetRouteTableVPCB
SubnetId:
Ref: AttachmentSubnetAVPCB

AttachmentBSubnetRouteTableAssociationVPCB:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- AttachmentSubnetRouteTableVPCB
- AttachmentSubnetBVPCB
Properties:
RouteTableId:
Ref: AttachmentSubnetRouteTableVPCB
SubnetId:
Ref: AttachmentSubnetBVPCB

NetworkAclAttachmentSubnetsVPCB:
Type: AWS::EC2::NetworkAcl
DependsOn: VPCB
Properties:
VpcId: !Ref VPCB
Tags:
- Key: Name
Value: "VPC B TGW Subnet NACL"

NetworkAclAttachmentSubnetsInboundRuleVPCB:
Type: AWS::EC2::NetworkAclEntry
DependsOn: NetworkAclAttachmentSubnetsVPCB
Properties:
NetworkAclId: !Ref NetworkAclAttachmentSubnetsVPCB
RuleNumber: 100
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0

NetworkAclAttachmentSubnetsOutboundRuleVPCB:
Type: AWS::EC2::NetworkAclEntry
DependsOn: NetworkAclAttachmentSubnetsVPCB
Properties:
NetworkAclId: !Ref NetworkAclAttachmentSubnetsVPCB
RuleNumber: 100
Protocol: -1
Egress: true
RuleAction: allow
CidrBlock: 0.0.0.0/0

SubnetNetworkAclAssociationAttachmentSubnetAVPCB:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- AttachmentSubnetAVPCB
- NetworkAclAttachmentSubnetsVPCB
Properties:
SubnetId: !Ref AttachmentSubnetAVPCB
NetworkAclId: !Ref NetworkAclAttachmentSubnetsVPCB

SubnetNetworkAclAssociationAttachmentSubnetBVPCB:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- AttachmentSubnetBVPCB
- NetworkAclAttachmentSubnetsVPCB
Properties:
SubnetId: !Ref AttachmentSubnetBVPCB
NetworkAclId: !Ref NetworkAclAttachmentSubnetsVPCB

VPCBEc2SecGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Open-up ports for ICMP from 10.x.x.x
GroupName: "VPC B Security Group"
VpcId:
Ref: VPCB
SecurityGroupIngress:
- IpProtocol: icmp
CidrIp: 0.0.0.0/0
FromPort: "-1"
ToPort: "-1"
- IpProtocol: tcp
FromPort: "5201"
ToPort: "5201"
CidrIp: 10.0.0.0/8


ServerEc2VPCB:
Type: AWS::EC2::Instance
DependsOn:
- PrivateSubnet1VPCB
- VPCBEc2SecGroup
Properties:
SubnetId:
Ref: PrivateSubnet1VPCB
ImageId: !Ref AMI
InstanceType: t2.micro
PrivateIpAddress: 10.1.1.100
SecurityGroupIds:
- Ref: VPCBEc2SecGroup
IamInstanceProfile: "NetworkingWorkshopInstanceProfile"
Tags:
- Key: Name
Value: "VPC B Private AZ1 Server"

# VPC C Resources
VPCC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.2.0.0/16"
EnableDnsSupport: "true"
EnableDnsHostnames: "true"
InstanceTenancy: default
Tags:
- Key: Name
Value: "VPC C"

PublicSubnet1VPCC:
Type: AWS::EC2::Subnet
DependsOn:
- VPCC
Properties:
VpcId:
Ref: VPCC
CidrBlock: "10.2.0.0/24"
AvailabilityZone:
Ref: AvailabilityZoneA
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: "VPC C Public Subnet AZ1"

PublicSubnet2VPCC:
Type: AWS::EC2::Subnet
DependsOn:
- VPCC
Properties:
VpcId:
Ref: VPCC
CidrBlock: "10.2.2.0/24"
AvailabilityZone:
Ref: AvailabilityZoneB
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: "VPC C Public Subnet AZ2"

PublicSubnetRouteTableVPCC:
Type: AWS::EC2::RouteTable
DependsOn: VPCC
Properties:
VpcId:
Ref: VPCC
Tags:
- Key: Name
Value: "VPC C Public Route Table"

PublicASubnetRouteTableAssociationVPCC:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PublicSubnetRouteTableVPCC
- PublicSubnet1VPCC
Properties:
RouteTableId:
Ref: PublicSubnetRouteTableVPCC
SubnetId:
Ref: PublicSubnet1VPCC

PublicBSubnetRouteTableAssociationVPCC:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PublicSubnetRouteTableVPCC
- PublicSubnet2VPCC
Properties:
RouteTableId:
Ref: PublicSubnetRouteTableVPCC
SubnetId:
Ref: PublicSubnet2VPCC

PrivateSubnet1VPCC:
Type: AWS::EC2::Subnet
DependsOn:
- VPCC
Properties:
VpcId:
Ref: VPCC
CidrBlock: "10.2.1.0/24"
AvailabilityZone:
Ref: AvailabilityZoneA
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC C Private Subnet AZ1"

PrivateSubnet2VPCC:
Type: AWS::EC2::Subnet
DependsOn:
- VPCC
Properties:
VpcId:
Ref: VPCC
CidrBlock: "10.2.3.0/24"
AvailabilityZone:
Ref: AvailabilityZoneB
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC C Private Subnet AZ2"

PrivateSubnetRouteTableVPCC:
Type: AWS::EC2::RouteTable
DependsOn: VPCC
Properties:
VpcId:
Ref: VPCC
Tags:
- Key: Name
Value: "VPC C Private Route Table"

PrivateASubnetRouteTableAssociationVPCC:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PrivateSubnetRouteTableVPCC
- PrivateSubnet1VPCC
Properties:
RouteTableId:
Ref: PrivateSubnetRouteTableVPCC
SubnetId:
Ref: PrivateSubnet1VPCC

PrivateBSubnetRouteTableAssociationVPCC:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- PrivateSubnetRouteTableVPCC
- PrivateSubnet2VPCC
Properties:
RouteTableId:
Ref: PrivateSubnetRouteTableVPCC
SubnetId:
Ref: PrivateSubnet2VPCC

VPCCInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: "VPC C IGW"

AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
DependsOn:
- VPCC
- VPCCInternetGateway
Properties:
VpcId:
Ref: VPCC
InternetGatewayId:
Ref: VPCCInternetGateway

VPCCDirectInternetRoute:
Type: AWS::EC2::Route
DependsOn:
- AttachGateway
- PublicSubnetRouteTableVPCC
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: VPCCInternetGateway
RouteTableId:
Ref: PublicSubnetRouteTableVPCC

VPCCNATEIP:
Type: "AWS::EC2::EIP"
Properties:
Domain: vpc

VPCCNATGateway:
Type: "AWS::EC2::NatGateway"
DependsOn:
- AttachGateway
- PublicSubnet1VPCC
Properties:
AllocationId:
Fn::GetAtt:
- VPCCNATEIP
- AllocationId
SubnetId:
Ref: PublicSubnet1VPCC
Tags:
- Key: Name
Value: "VPC C NATGW"

VPCCNATInternetRoutePrivate:
Type: AWS::EC2::Route
DependsOn:
- VPCCNATGateway
- PrivateSubnetRouteTableVPCC
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId:
Ref: VPCCNATGateway
RouteTableId:
Ref: PrivateSubnetRouteTableVPCC

AttachmentSubnetAVPCC:
Type: AWS::EC2::Subnet
DependsOn:
- VPCC
Properties:
VpcId:
Ref: VPCC
CidrBlock: "10.2.5.0/28"
AvailabilityZone:
Ref: AvailabilityZoneA
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC C TGW Subnet AZ1"

AttachmentSubnetBVPCC:
Type: AWS::EC2::Subnet
DependsOn:
- VPCC
Properties:
VpcId:
Ref: VPCC
CidrBlock: "10.2.5.16/28"
AvailabilityZone:
Ref: AvailabilityZoneB
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: "VPC C TGW Subnet AZ2"

AttachmentSubnetRouteTableVPCC:
Type: AWS::EC2::RouteTable
DependsOn: VPCC
Properties:
VpcId:
Ref: VPCC
Tags:
- Key: Name
Value: "VPC C TGW Route Table"

AttachmentASubnetRouteTableAssociationVPCC:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- AttachmentSubnetRouteTableVPCC
- AttachmentSubnetAVPCC
Properties:
RouteTableId:
Ref: AttachmentSubnetRouteTableVPCC
SubnetId:
Ref: AttachmentSubnetAVPCC

AttachmentBSubnetRouteTableAssociationVPCC:
Type: AWS::EC2::SubnetRouteTableAssociation
DependsOn:
- AttachmentSubnetRouteTableVPCC
- AttachmentSubnetBVPCC
Properties:
RouteTableId:
Ref: AttachmentSubnetRouteTableVPCC
SubnetId:
Ref: AttachmentSubnetBVPCC

NetworkAclAttachmentSubnetsVPCC:
Type: AWS::EC2::NetworkAcl
DependsOn: VPCC
Properties:
VpcId:
Ref: VPCC
Tags:
- Key: Name
Value: "VPC C TGW Subnet NACL"

NetworkAclAttachmentSubnetsInboundRuleVPCC:
Type: AWS::EC2::NetworkAclEntry
DependsOn: NetworkAclAttachmentSubnetsVPCC
Properties:
NetworkAclId: !Ref NetworkAclAttachmentSubnetsVPCC
RuleNumber: 100
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0

NetworkAclAttachmentSubnetsOutboundRuleVPCC:
Type: AWS::EC2::NetworkAclEntry
DependsOn: NetworkAclAttachmentSubnetsVPCC
Properties:
NetworkAclId: !Ref NetworkAclAttachmentSubnetsVPCC
RuleNumber: 100
Protocol: -1
Egress: true
RuleAction: allow
CidrBlock: 0.0.0.0/0

SubnetNetworkAclAssociationAttachmentSubnetAVPCC:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- AttachmentSubnetAVPCC
- NetworkAclAttachmentSubnetsVPCC
Properties:
SubnetId:
Ref: AttachmentSubnetAVPCC
NetworkAclId:
Ref: NetworkAclAttachmentSubnetsVPCC

SubnetNetworkAclAssociationAttachmentSubnetBVPCC:
Type: AWS::EC2::SubnetNetworkAclAssociation
DependsOn:
- AttachmentSubnetBVPCC
- NetworkAclAttachmentSubnetsVPCC
Properties:
SubnetId:
Ref: AttachmentSubnetBVPCC
NetworkAclId:
Ref: NetworkAclAttachmentSubnetsVPCC

VPCCEc2SecGroup:
Type: AWS::EC2::SecurityGroup
DependsOn: VPCC
Properties:
GroupDescription: Open-up ports for ICMP from 10.x.x.x
GroupName: "VPC C Security Group"
VpcId:
Ref: VPCC
SecurityGroupIngress:
- IpProtocol: icmp
CidrIp: 0.0.0.0/0
FromPort: "-1"
ToPort: "-1"

ServerEc2VPCC:
Type: AWS::EC2::Instance
DependsOn:
- PrivateSubnet1VPCC
- VPCCEc2SecGroup
Properties:
SubnetId:
Ref: PrivateSubnet1VPCC
ImageId: !Ref AMI
InstanceType: t2.micro
PrivateIpAddress: 10.2.1.100
SecurityGroupIds:
- Ref: VPCCEc2SecGroup
IamInstanceProfile: "NetworkingWorkshopInstanceProfile"
Tags:
- Key: Name
Value: "VPC C Private AZ1 Server"

Outputs:
# VPC A Outputs
VpcA:
Description: "The Created VPC"
Value: !Ref VpcA
Export:
Name: !Sub "VpcAId"
VpcAPublicServerPublicIp:
Description: "VPC A Public EC2 Public IP address"
Value: !GetAtt VpcAPublicServerEc2.PublicIp
Export:
Name: !Sub "VpcAPublicServerPublicIP"
VpcAPublicServerPrivateIp:
Description: "VPC A Public EC2 Private IP address"
Value: !GetAtt VpcAPublicServerEc2.PrivateIp
Export:
Name: !Sub "VpcAPublicServerPrivateIP"
VpcAPrivateServer:
Description: "VPC A Private EC2 IP address"
Value: !GetAtt ServerEc2VPCB.PrivateIp
Export:
Name: !Sub "VpcAServerIP"
VpcAPublicSubnet1:
Description: "VPC A Public Subnet AZ1"
Value: !Ref VpcAPublicSubnet1
Export:
Name: !Sub "VpcAPublicSubnetAZ1"
VpcAPublicSubnet2:
Description: "VPC A Public Subnet AZ2"
Value: !Ref VpcAPublicSubnet2
Export:
Name: !Sub "VpcAPublicSubnetAZ2"
VpcAPrivateSubnet1:
Description: "VPC A Private Subnet AZ1"
Value: !Ref VpcAPrivateSubnet1
Export:
Name: !Sub "VpcAPrivateSubnetAZ1"
VpcAPrivateSubnet2:
Description: "VPC A Private Subnet AZ2"
Value: !Ref VpcAPrivateSubnet2
Export:
Name: !Sub "VpcAPrivateSubnetAZ2"
VpcAPrivateSubnetRouteTable:
Description: "VPC A Private Route Table"
Value: !Ref VpcAPrivateSubnetRouteTable
Export:
Name: !Sub "VpcAPrivateSubnetRouteTable"
VpcAPublicSubnetRouteTable:
Description: "VPC A Public Route Table"
Value: !Ref VpcAPublicSubnetRouteTable
Export:
Name: !Sub "VpcAPublicRouteTable"

# VPC B Outputs
VPCB:
Description: "The Created VPC"
Value: !Ref VPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-VPC"
ServerVPCB:
Description: "VPCB1 EC2 IP address"
Value: !GetAtt ServerEc2VPCB.PrivateIp
Export:
Name: !Sub "VPCB-${AWS::StackName}-ServerIP"
PublicSubnet1VPCB:
Description: "VPCB Public Subnet A"
Value: !Ref PublicSubnet1VPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-PublicSubnet1"
PublicSubnet2VPCB:
Description: "VPCB Public Subnet B"
Value: !Ref PublicSubnet2VPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-PublicSubnet2"
PrivateSubnet1VPCB:
Description: "VPCB Private Subnet A"
Value: !Ref PrivateSubnet1VPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-PrivateSubnet1"
PrivateSubnet2VPCB:
Description: "VPCB Private Subnet B"
Value: !Ref PrivateSubnet2VPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-PrivateSubnet2"
PrivateSubnetRouteTableVPCB:
Description: "VPCB Private Route Table"
Value: !Ref PrivateSubnetRouteTableVPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-PrivateRouteTable"
PublicSubnetRouteTableVPCB:
Description: "VPCB Public Route Table"
Value: !Ref PublicSubnetRouteTableVPCB
Export:
Name: !Sub "VPCB-${AWS::StackName}-PublicRouteTable"
# VPC C Outputs
VPCC:
Description: "Datacenter Services VPC"
Value: !Ref VPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-VPC"
ServerVPCC:
Description: "VPCC EC2 IP address"
Value: !GetAtt ServerEc2VPCC.PrivateIp
Export:
Name: !Sub "VPCC-${AWS::StackName}-ServerIP"
PublicSubnet1VPCC:
Description: "VPCC Public Subnet A"
Value: !Ref PublicSubnet1VPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-PublicSubnet1"
PublicSubnet2VPCC:
Description: "VPCC Public Subnet B"
Value: !Ref PublicSubnet2VPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-PublicSubnet2"
PrivateSubnet1VPCC:
Description: "VPCC Private Subnet A"
Value: !Ref PrivateSubnet1VPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-PrivateSubnet1"
PrivateSubnet2VPCC:
Description: "VPCC Private Subnet B"
Value: !Ref PrivateSubnet2VPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-PrivateSubnet2"
PrivateSubnetRouteTableVPCC:
Description: "VPCC Private Route Table"
Value: !Ref PrivateSubnetRouteTableVPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-PrivateRouteTable"
PublicSubnetRouteTableVPCC:
Description: "VPCC Public Route Table"
Value: !Ref PublicSubnetRouteTableVPCC
Export:
Name: !Sub "VPCC-${AWS::StackName}-PublicRouteTable"

2. On the AWS Console, search for ‘CloudFormation’ and open the service

3. Click on ‘Create stack’

4. Under ‘Prepare template’, select ‘Template is ready’, the select ‘Upload a template file’. Click on ‘Choose file’ and browse your system to upload the file we created or downloaded in Step 1. Then click ‘Next’

5. Give the stack a name then click on ‘Next’ at the bottom

6. On the ‘Configure stack options’ page, scroll down and click on ‘Next’

7. On the review page, scroll down and click on ‘Submit’

8. The template starts running to create the resources with the status ‘CREATE_IN_PROGRESS’

9. Wait for the Stack to have the status, ‘CREATE_COMPLETE’

10. Go to the VPC resources section and check the created VPCs

11. Note that VPC A, VPC B and VPC C were created

12. Each VPC has an EC2 instance in the private subnet located in the us-east-1a availability zone

13. VPC A is missing the Transit Gateway (TGW) subnets. We will be creating these shortly

VPC Peering

A VPC peering connection is a networking connection between two VPCs that enables you to route traffic between them using private IPv4 addresses or IPv6 addresses.

We will establish VPC peering connections between VPC A and VPC B, as well as between VPC A and VPC C and show that traffic flows between only those VPCs with direct peering links.

Setup VPC A and VPC B Peering

  1. On the VPC dashboard, click on ‘Peering connections’ on the left navigation plane

2. Click on ‘Create peering connection’

3. Give the VPC peering connection a Name. Select the requester to be VPC A and the Accepter to be VPC B. Scroll to the bottom and click on ‘Create peering connection’

4. To accept the request, click on ‘Actions’ then ‘Accept request’

5. On the pop-up that appears, click on ‘Accept request’

6. Click on Modify my route tables now on the success banner that appears

7. Edit the VPC A Private Route Table by selecting it, moving to the Routes tab then clicking on Edit routes

8. Add the entry for ‘VPC B’ using the CIDR range 10.1.0.0/16 and select the peering connection as the target. Click on Save changes

9. Go back to the Route tables dashboard and update the route table for VPC B

10. Click on Add route. Add the VPC A ip address to the destination and make the VPC peering connection the target. Click on Save changes

Setup VPC A and VPC C Peering

Repeat the process we did for peering between VPC A and VPC B but now do it with VPC A and VPC C. Also edit the route tables in VPC A and VPC C accordingly.

Check Connectivity

  1. Proceed to the EC2 console

2. Select the VPC A Private AZ1 Server and click the Connect button

3. Click on Connect in the Session manager tab

4. On the terminal ping the instance in VPC B and the instance in VPC C

The pings are successful proving that the VPCs are connected

5. To ascertain the connection even more. Ping the instance in VPC A from VPC B and from VPC C

Clean-up

To delete the VPC Peering Connections:

  1. On the VPC dashboard, navigate to Peering connections. Select VPC A <> VPC B. Click on Actions then select Delete peering connection

2. On the pop-up, select Delete related route table entries, type delete on the confirm box then click on Delete at the bottom of the page

3. Repeat the procedure for VPC A <> VPC C

4. Try pinging the instances again via the session manager and you will note that the pings are not successful

Conclusion

In conclusion, VPC Peering in AWS represents a vital tool for establishing seamless connectivity between Virtual Private Clouds, enabling efficient data exchange and communication within a secure and isolated environment. The essence of VPC Peering lies in its ability to bridge the gap between separate networks, fostering collaboration and flexibility in cloud infrastructure. Our step-by-step guide has equipped you with the knowledge to create these connections effortlessly, optimizing your AWS resources for a dynamic and well-connected cloud environment.

However, it’s important to note that while VPC Peering is a powerful solution for interconnecting multiple VPCs, managing numerous point-to-point connections can become unwieldy as your cloud infrastructure scales. To address this, a more scalable and efficient approach is to transition to AWS Transit Gateway. The AWS Transit Gateway offers a centralized and simplified solution for managing network connections at scale, making it an ideal choice for evolving cloud architectures. Stay tuned for our next blog, where we’ll explore the implementation of Transit Gateway and further enhance the connectivity and manageability of our AWS cloud network.

--

--