Deployment of VPC using CloudFormation
Creating a Virtual Private Cloud (VPC) using AWS CloudFormation (CFN) allows you to automate the process of setting up a secure and isolated network infrastructure in the cloud. AWS CloudFormation is a service that helps you create and manage AWS resources in a repeatable and predictable way, using templates to define the Infrastructure as Code (IaC).
When creating a VPC using AWS CloudFormation, you can specify the networking components, such as subnets, routing tables, and security groups, as well as the resources that will be deployed in the VPC, such as EC2 instances, RDS databases, and Elastic Load Balancers. You can also specify the network topology, such as whether the VPC will be connected to an on-premises network or to another VPC.
Head over to GitHub and fork this repository. Once you’ve done that, git clone
the repo in your CLI.
Clone the GitHub repo and cd
into the repository.
In this walkthrough, we’ll deploy VPC and EC2 instances in AWS using CloudFormation. The resources which will be provisioned are as follows:
- VPC
- 02 Public Subnets in each AZ
- 02 Private Subnets in each AZ
- 02 NAT Gateways
- 01 Internet Gateway
- 01 Public Route Table for both Public Subnets
- 02 Private Route Tables for each Private Subnet
- 02 Public NACLs for each Public Subnet
- 02 Private NACLs for each Private Subnet
Prerequisites:
- AWS account
- IDE (Pycharm/ Visual Studio/ Cloud9 for example)
Now that we’ve set up our configuration code, let’s sync the files to an Artifact S3 Bucket.
Upload objects to S3 Bucket: aws s3 sync . s3://iacartifacts-devops-youtube-tutorials-us-east-1–355986150263/code/cfn/services/vpc/
Create Cfn template:
aws cloudformation create-stack -- stack-name devops-poc-dev-customVpc-us-east-1 -- template-body file://vpc-app-main.yml
Head over to your vpc-app.yml
file, as we are going to understand what resources are creating. I’ve also attached the snippets of the output along with the description of resources.
AWS::EC2::VPC
: This resource will create simply a VPC with CIDR 10.31.0.0/16. Here, the DNS Support (Amazon-provided DNS server resolves domain names to IP addresses) and DNS Hostnames (Amazon-provided DNS server resolves public DNS hostnames to IP addresses) are set to true.
# VPC with CIDR block 10.31.0.0/16
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref cidrBlock
EnableDnsSupport: !Ref dnsSupport
EnableDnsHostnames: !Ref dnsHostnames
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-vpc"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
Conditions: These conditions are used to control the creation of resources in a CloudFormation stack based on the values of certain parameters.
- ifAZ1 condition evaluates to true if the value of the parameter
publicSubnetACidrBlock1
is not an empty string. The!Ref
intrinsic function is used to retrieve the value of thepublicSubnetACidrBlock1
parameter and the!Equals
intrinsic function is used to compare it to an empty string. The!Not
intrinsic function negates the result of the comparison. - ifAZ2 condition evaluates to true if the value of the parameter
publicSubnetBCidrBlock2
is not an empty string. The!Ref
intrinsic function is used to retrieve the value of thepublicSubnetBCidrBlock2
parameter and the!Equals
intrinsic function is used to compare it to an empty string. The!Not
intrinsic function negates the result of the comparison.
Conditions:
ifAZ1: !Not [!Equals [!Ref publicSubnetACidrBlock1, '']]
ifAZ2: !Not [!Equals [!Ref publicSubnetBCidrBlock2, '']]
AWS::EC2::Subnet
: This resource will create subnets in specific Availability Zones. We’ve two AZs us-east-1a
and us-east-1b
. One Public and One Private subnet will be created in each AZ. For Public Subnets, MapPublicIpOnLaunch
is set to true. By default, it is set to false. Additionally, we have put the Condition on Subnets that, if the condition evaluates to true then the subnets associated with the AZs will be created.
# Public Subnet 1 in AZ us-east-1a with CIDR block 10.31.0.0/24
publicSubnetA:
Condition: ifAZ1
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref availabilityZone1
VpcId: !Ref vpc
CidrBlock: !Ref publicSubnetACidrBlock1
MapPublicIpOnLaunch: !Ref publicIpOnLaunchforPublicSubnet
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-publicSubnetA"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Public Subnet 2 in AZ us-east-1b with CIDR block 10.31.8.0/24
publicSubnetB:
Condition: ifAZ2
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref availabilityZone2
VpcId: !Ref vpc
CidrBlock: !Ref publicSubnetBCidrBlock2
MapPublicIpOnLaunch: !Ref publicIpOnLaunchforPublicSubnet
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-publicSubnetB"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Private Subnet 1 in AZ us-east-1a with CIDR block 10.31.16.0/24
privateSubnetA:
Condition: ifAZ1
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref availabilityZone1
VpcId: !Ref vpc
CidrBlock: !Ref privateSubnetACidrBlock1
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-privateSubnetA"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Private Subnet 2 in AZ us-east-1b with CIDR block 10.31.24.0/24
privateSubnetB:
Condition: ifAZ2
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Ref availabilityZone2
VpcId: !Ref vpc
CidrBlock: !Ref privateSubnetBCidrBlock2
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-privateSubnetB"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
AWS::EC2::InternetGateway
: This resource will create an Internet Gateway.
AWS::EC2::VPCGatewayAttachment
: This resource will attach the above-created IGW with the VPC. We’ve referenced the internetGateway resource and vpc resource to the InternetGatewayId
and VpcId
respectively.
# Internet Gateway
internetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-internetGateway"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
internetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref vpc
InternetGatewayId: !Ref internetGateway
AWS::EC2::EIP
: This resource type creates an Elastic IP address that can be associated with an EC2-instance or VPC.
We’ve created two Elastic IP addresses for two NAT Gateways.
# Elastic IP 1
natElasticIP1:
Type: AWS::EC2::EIP
DependsOn: internetGatewayAttachment
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-natElasticIP1"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Elastic IP 2
natElasticIP2:
Type: AWS::EC2::EIP
DependsOn: internetGatewayAttachment
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-natElasticIP2"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
AWS::EC2::NatGateway
: This resource type creates a Network Address Translation (NAT) gateway that enables instances in a private subnet to connect to the internet or other AWS services, while still blocking inbound traffic from the internet.
The NAT Gateway is created in the Public subnet that’s why the publicSubnet is referenced to the SubnetId
and ID of the EIP is referenced to the AllocationId
.
# NAT Gateway 1
natGateway1:
Type: AWS::EC2::NatGateway
DependsOn: natElasticIP1
Properties:
SubnetId: !Ref publicSubnetA
AllocationId: !GetAtt natElasticIP1.AllocationId
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-natGateway1"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# NAT Gateway 2
natGateway2:
Type: AWS::EC2::NatGateway
DependsOn: natElasticIP2
Properties:
SubnetId: !Ref publicSubnetA
AllocationId: !GetAtt natElasticIP2.AllocationId
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-natGateway2"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
AWS::EC2::RouteTable
: This resource type represents a route table in Amazon VPC, which is used to route network traffic between subnets in a VPC. Each route table contains a set of rules, called routes, that determine where network traffic is directed.
We’ve created a single Route Table for Public Subnets and two Route Tables for Private Subnets.
AWS::EC2::Route
: This resource type represents a route in an Amazon VPC route table. A Route Table contains a set of rules, called routes, that determine where network traffic is directed.
For Public Routes, the internetGateway is referenced to the property GatewayId
. For Private Routes, the natGateway is referenced to the property NatGatewayId
.
AWS::EC2::SubnetRouteTableAssociation
: This resource type associates a subnet with a route table, allowing you to control the traffic routing behavior for instances in the subnet. By default, each subnet in a VPC is associated with the main route table for the VPC.
# For Public Subnets
# Route Table for Public Subnets
publicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-publicRouteTable"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Routes for the Public Subnets
publicSubnetRoute:
DependsOn: internetGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref publicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref internetGateway
# Public Subnet 1 association with Public Route Table
publicSubnetRouteTableAssociationA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref publicSubnetA
RouteTableId: !Ref publicRouteTable
# Public Subnet 2 association with Public Route Table
publicSubnetRouteTableAssociationB:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref publicSubnetB
RouteTableId: !Ref publicRouteTable
# For Private Subnets
# Route Table for Private Subnet 1
privateRouteTableA:
Condition: ifAZ1
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-privateRouteTableA"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Routes for the Private Subnet 1
privateSubnetRouteA:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref privateRouteTableA
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref natGateway1
# Private Subnet 1 association with Private Route Table 1
privateSubnetRouteTableAssociationA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref privateSubnetA
RouteTableId: !Ref privateRouteTableA
# Route Table for Private Subnet 2
privateRouteTableB:
Condition: ifAZ2
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-privateRouteTableB"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
# Routes for the Private Subnet 2
privateSubnetRouteB:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref privateRouteTableB
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref natGateway2
# Private Subnet 1 association with Private Route Table 1
privateSubnetRouteTableAssociationB:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref privateSubnetB
RouteTableId: !Ref privateRouteTableB
AWS::EC2::NetworkAcl
: This resource type creates a Network Access Control List (ACL) that acts as a virtual firewall for controlling traffic in and out of one or more subnets in a VPC. By default, each subnet in a VPC is associated with the default network ACL for the VPC.
AWS::EC2::NetworkAclEntry
: This resource type in adds an entry (or rule) to a Network Access Control List (ACL) that controls traffic in and out of one or more subnets in a VPC. If we set Egress
to true, then the rule will be for outbound NACL otherwise inbound NACL.
AWS::EC2::SubnetNetworkAclAssociation
: This resource type associates a Network Access Control List (ACL) with a subnet in a VPC.
# Network ACLs for Public Subnet 1
publicNACL1:
Condition: ifAZ1
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-publicNACL1"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
publicInboundrule:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref publicNACL1
RuleNumber: 130
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0
publicOutboundrule:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref publicNACL1
RuleNumber: 140
Protocol: -1
Egress: True
RuleAction: allow
CidrBlock: 0.0.0.0/0
naclAssociationPublicSubnetA:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref publicSubnetA
NetworkAclId: !Ref publicNACL1
# Network ACLs for Public Subnet 2
publicNACL2:
Condition: ifAZ2
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-publicNACL2"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
publicInboundrule2:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref publicNACL2
RuleNumber: 130
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0
publicOutboundrule2:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref publicNACL2
RuleNumber: 140
Protocol: -1
Egress: True
RuleAction: allow
CidrBlock: 0.0.0.0/0
naclAssociationPublicSubnetB:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref publicSubnetB
NetworkAclId: !Ref publicNACL2
# Network ACLs for Private Subnet 1
privateNACL:
Condition: ifAZ1
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-privateNACL"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
privateInboundrule:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref privateNACL
RuleNumber: 230
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0
privateOutboundrule:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref privateNACL
RuleNumber: 240
Protocol: -1
Egress: True
RuleAction: allow
CidrBlock: 0.0.0.0/0
naclAssociationPrivateSubnetA:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref privateSubnetA
NetworkAclId: !Ref privateNACL
# Network ACLs for Private Subnet 2
privateNACL2:
Condition: ifAZ2
Type: AWS::EC2::NetworkAcl
Properties:
VpcId: !Ref vpc
Tags:
- Key: Name
Value: !Sub "${project}-${app}-${env}-privateNACL2"
- Key: project
Value: !Ref project
- Key: app
Value: !Ref app
- Key: environment
Value: !Ref env
privateInboundrule2:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref privateNACL2
RuleNumber: 230
Protocol: -1
RuleAction: allow
CidrBlock: 0.0.0.0/0
privateOutboundrule2:
Type: AWS::EC2::NetworkAclEntry
Properties:
NetworkAclId: !Ref privateNACL2
RuleNumber: 240
Protocol: -1
Egress: True
RuleAction: allow
CidrBlock: 0.0.0.0/0
naclAssociationPrivateSubnetA:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref privateSubnetB
NetworkAclId: !Ref privateNACL2
This is the Output section of the AWS CloudFormation Stack.
vpcId:
Description: VPC ID
Value: !Ref vpc
Export:
Name: !Sub "${project}-${app}-${env}-vpcId"
publicSubnetIdA:
Description: Public Subnet 1 ID
Value: !Ref publicSubnetA
Export:
Name: !Sub "${project}-${app}-${env}-publicSubnetIdA"
publicSubnetIdB:
Description: Public Subnet 2 ID
Value: !Ref publicSubnetB
Export:
Name: !Sub "${project}-${app}-${env}-publicSubnetIdB"
privateSubnetIdA:
Description: Private Subnet 1 ID
Value: !Ref privateSubnetA
Export:
Name: !Sub "${project}-${app}-${env}-privateSubnetIdA"
privateSubnetIdB:
Description: Private Subnet 2 ID
Value: !Ref privateSubnetB
Export:
Name: !Sub "${project}-${app}-${env}-privateSubnetIdB"
natElasticIP1:
Description: NAT Public IP 1
Value: !Ref natElasticIP1
Export:
Name: !Sub "${project}-${app}-${env}-natElasticIP1"
natElasticIP2:
Description: NAT Public IP 2
Value: !Ref natElasticIP2
Export:
Name: !Sub "${project}-${app}-${env}-natElasticIP2"
natGateway1:
Description: NAT Gateway 1 ID
Value: !Ref natGateway1
Export:
Name: !Sub "${project}-${app}-${env}-natGateway1"
natGateway2:
Description: NAT Gateway 2 ID
Value: !Ref natGateway2
Export:
Name: !Sub "${project}-${app}-${env}-natGateway2"
internetGateway:
Description: Internet Gateway ID
Value: !Ref internetGateway
Export:
Name: !Sub "${project}-${app}-${env}-internetGateway"
Delete Cfn template:
aws cloudformation delete-stack -- stack-name devops-poc-dev-customVpc-us-east-1
Don’t forget to delete-stack
your resources so you don't incur any additional AWS charges outside of the free tier.
Great job on making it to the end of this walkthrough 👏🏾🤠! Congratulations!