Provision Highly Available VPC Architecture Using CloudFormation

In my previous Blog post , I have explained in detail on How to Create VPC with Public and Private Subnets , Check here.

Related Articles

In this guide , We will learn How to Provision Highly Available VPC Architecture using CloudFormation

What Is AWS CloudFormation?

AWS CloudFormation is a service which helps us to setup AWS resources such as EC2 , RDS instances in a very less time So that we can focus more on applications.

We have to just create a template with the details of AWS resources such as EC2 , RDS , SNS etc .that needs to be launched to setup applications.

Once the template is created , We can import it to Cloudformation and AWS CloudFormation will take care of provisioning those resources , Configure them and map them if required.

AWS CloudFormation helps us to,

  • Quickly replicate the exiting Infrastructure.
  • Simplify infrastructure management.
  • Easily control and track changes to the infrastructure.

Core Concepts Of CloudFormation

In CloudFormation , We mostly work with these components.

  • Templates

Templates in cloudformation are written in JSON or YAML format.In this template , You can describe the resources with their properties that needs to be created.For example : EC2 Instance with t2.medium ,Then using this template the cloudformation will create those resources.

  • Stacks

A stack is a collection of AWS resources which we can manage as a single unit.All the resources in a stack are defined by cloudformation template.

  • Change Sets

If any changes required in the existing infrastructure , We can update the stack.Before making changes we can create a change set (a summary of proposed changes) and the change set will let us know how the changes will impact the existing resources.

What Is VPC?

Amazon Virtual Private Cloud is the AWS service where you can create resources such as databases , Servers in the virtual network.

For each Account and Each Region will have one VPC created by default.

Each VPC is logically isolated from Other VPC’s in the Cloud.

In other words , Amazon VPC operates like a own data center.

UnderStanding Subnets

We are going to Create Four subnets ,

  • Two Subnets across Availability Zones as Public Subnets Where we create our Web Servers.
  • Another Two Subnets across Availability Zones as Private Subnets where we will create the Databases.

Points To Remember

  • A Subnet associated with a Route Table having Internet Gateway attached with it , is called as Public Subnet.
  • A Subnet associated with a Route Table having NAT Gateway attached with it , is called as private Subnet.

Provisioning VPC Using CloudFormation Template

Before creating a template for creating the VPC and Subnets , Let us Understand the architecture we are going to implement on this guide.

We will create the following Resources.

  • VPC with CIDR : 10.0.0.0/16
  • 2 Public Subnets in Each AZ.

PublicSubnet1 : 10.0.10.0/24

PublicSubnet2 : 10.0.11.0/24

  • 2 Private Subnets in Each AZ.

PrivateSubnet1 : 10.0.20.0/24

PrivateSubnet2 : 10.0.21.0/24

  • 1 Route Table for Public Subnets
  • 2 Route Tables , One for each Private Subnet
  • Internet Gateway – Will be attached with Public Route Table
  • 2 NAT Gateway in each AZ and will be attached with each private Route Tables.

CloudFormation Template

Let us create a file named VPC.yaml and add the below attached template to the file.

Description: Template creates VPC with Public & Private Subnets across AvailabilityZone. Also creates Internet Gateway, with a default route on the public subnets and deploys a pair of NAT gateways (one in each AZ),and default routes for them in the private subnets.

Parameters:
   EnvironmentName:
     Description: Each Resource name will be prefixed with Environment Name
     Type: String

   VpcCIDR:
     Description: Eenter CIDR notation VPC
     Type: String
     Default: 10.0.0.0/16

   PublicSubnet1CIDR:
     Description: Enter CIDR notation for Public subnet in the First Availability Zone
     Type: String
     Default: 10.0.10.0/24
   
   PublicSubnet2CIDR:
     Description: Enter CIDR notation for Public subnet in the Second Availability Zone
     Type: String
     Default: 10.0.11.0/24

   PrivateSubnet1CIDR:
     Description: Enter CIDR notation for Private subnet in the First Availability Zone
     Type: String
     Default: 10.0.20.0/24

   PrivateSubnet2CIDR:
     Description: Enter CIDR notation for Private subnet in the Second Availability Zone
     Type: String
     Default: 10.0.21.0/24

Resources:
   VPC:
     Type: AWS::EC2::VPC
     Properties:
        CidrBlock: !Ref VpcCIDR
        EnableDnsSupport: true
        EnableDnsHostnames: true
        Tags:
          - Key: Name
            Value: !Ref EnvironmentName

   InternetGateway:
     Type: AWS::EC2::InternetGateway
     Properties:
        Tags:
          - Key: Name
            Value: !Ref EnvironmentName

   InternetGatewayAttachment:
     Type: AWS::EC2::VPCGatewayAttachment
     Properties:
        InternetGatewayId: !Ref InternetGateway
        VpcId: !Ref VPC

   PublicSubnet1:
     Type: AWS::EC2::Subnet
     Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 0, !GetAZs '' ]
        CidrBlock: !Ref PublicSubnet1CIDR
        MapPublicIpOnLaunch: true
        Tags:
          - Key: Name
            Value: !Sub ${EnvironmentName} Public Subnet (AZ1)

   PublicSubnet2:
     Type: AWS::EC2::Subnet
     Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 1, !GetAZs '' ]
        CidrBlock: !Ref PublicSubnet2CIDR
        MapPublicIpOnLaunch: true
        Tags:
          - Key: Name
            Value: !Sub ${EnvironmentName} Public Subnet (AZ2)

   PrivateSubnet1:
     Type: AWS::EC2::Subnet
     Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 0, !GetAZs '' ]
        CidrBlock: !Ref PrivateSubnet1CIDR
        MapPublicIpOnLaunch: false
        Tags:
          - Key: Name
            Value: !Sub ${EnvironmentName} Private Subnet (AZ1)

   PrivateSubnet2:
     Type: AWS::EC2::Subnet
     Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 1, !GetAZs '' ]
        CidrBlock: !Ref PrivateSubnet2CIDR
        MapPublicIpOnLaunch: false
        Tags:
          - Key: Name
            Value: !Sub ${EnvironmentName} Private Subnet (AZ2)

   NatGateway1EIP:
     Type: AWS::EC2::EIP
     DependsOn: InternetGatewayAttachment
     Properties:
       Domain: vpc

   NatGateway2EIP:
     Type: AWS::EC2::EIP
     DependsOn: InternetGatewayAttachment
     Properties:
       Domain: vpc

   NatGateway1:
     Type: AWS::EC2::NatGateway
     Properties:
       AllocationId: !GetAtt NatGateway1EIP.AllocationId
       SubnetId: !Ref PublicSubnet1

   NatGateway2:
     Type: AWS::EC2::NatGateway
     Properties:
       AllocationId: !GetAtt NatGateway2EIP.AllocationId
       SubnetId: !Ref PublicSubnet2

   PublicRouteTable:
     Type: AWS::EC2::RouteTable
     Properties:
       VpcId: !Ref VPC
       Tags:
         - Key: Name
           Value: !Sub ${EnvironmentName} Public Routes

   DefaultPublicRoute:
     Type: AWS::EC2::Route
     DependsOn: InternetGatewayAttachment
     Properties:
       RouteTableId: !Ref PublicRouteTable
       DestinationCidrBlock: 0.0.0.0/0
       GatewayId: !Ref InternetGateway

   PublicSubnet1RouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       RouteTableId: !Ref PublicRouteTable
       SubnetId: !Ref PublicSubnet1

   PublicSubnet2RouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       RouteTableId: !Ref PublicRouteTable
       SubnetId: !Ref PublicSubnet2

   PrivateRouteTable1:
     Type: AWS::EC2::RouteTable
     Properties:
       VpcId: !Ref VPC
       Tags:
         - Key: Name
           Value: !Sub ${EnvironmentName} Private Routes (AZ1)

   DefaultPrivateRoute1:
     Type: AWS::EC2::Route
     Properties:
       RouteTableId: !Ref PrivateRouteTable1
       DestinationCidrBlock: 0.0.0.0/0
       NatGatewayId: !Ref NatGateway1

   PrivateSubnet1RouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       RouteTableId: !Ref PrivateRouteTable1
       SubnetId: !Ref PrivateSubnet1

   PrivateRouteTable2:
     Type: AWS::EC2::RouteTable
     Properties:
       VpcId: !Ref VPC
       Tags:
         - Key: Name
           Value: !Sub ${EnvironmentName} Private Routes (AZ2)

   DefaultPrivateRoute2:
     Type: AWS::EC2::Route
     Properties:
       RouteTableId: !Ref PrivateRouteTable2
       DestinationCidrBlock: 0.0.0.0/0
       NatGatewayId: !Ref NatGateway2

   PrivateSubnet2RouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       RouteTableId: !Ref PrivateRouteTable2
       SubnetId: !Ref PrivateSubnet2

   NoIngressSecurityGroup:
     Type: AWS::EC2::SecurityGroup
     Properties:
       GroupName: "no-ingress-sg"
       GroupDescription: "Security group with no ingress rule"
       VpcId: !Ref VPC

Outputs:
   VPC:
    Description: A reference to the created VPC
    Value: !Ref VPC

   PublicSubnets:
     Description: A list of the public subnets
     Value: !Join [ ",", [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]]

   PrivateSubnets:
     Description: A list of the private subnets
     Value: !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]]

   PublicSubnet1:
     Description: A reference to the public subnet in the 1st Availability Zone
     Value: !Ref PublicSubnet1

   PublicSubnet2:
     Description: A reference to the public subnet in the 2nd Availability Zone
     Value: !Ref PublicSubnet2

   PrivateSubnet1:
     Description: A reference to the private subnet in the 1st Availability Zone
     Value: !Ref PrivateSubnet1

   PrivateSubnet2:
     Description: A reference to the private subnet in the 2nd Availability Zone
     Value: !Ref PrivateSubnet2

   NoIngressSecurityGroup:
     Description: Security group with no ingress rule
     Value: !Ref NoIngressSecurityGroup

Let us try to understand the above template.

  • First is the Parameters Section

We have used parameters to pass the values to the template when creating or updating a stack.

Parameters will help us to provide a custom value while creating or updating a stack.

First We are passing parameters and the default value for resources such as VPC , Public & Private Subnets.

  • Then comes Resources Section

Here We refer the Type of resources we’re going to create.The resources such as VPC , Internet Gateway , NAT gateway , Route Tables , Public and Private Subnets.

!Ref – it returns the value of the specified resource from the parameters.

!Select and !GetAZs – are the functions used by the AvailabilityZone to retrieve the list of availability zones for a Specified region and reference first element (0) from the list.

EnableDnsHostnames – The instances launched in the public subnet will get Public DNS Hostnames if the EnableDnsHostnames is set to true.

  • Finally the Output Section

To retrieve the attribute of the resources , !GetAtt function is used.

This function calls all the resource for their attributes and store them as an output with actual values.

Now Its time to Provison the VPC using the template that we built.

Login to CloudFormation Console.Click Create stack

On the Create stack page , Choose Template is ready and then

For Specify template , Choose Upload a template file (The VPC.yaml file) and then click Choose file and upload it.

and click Next , Enter the Name for the Stack

Under Parameters , Provide the EnvironmentName

And the Values for the VPC and Subnets will be automatically Populated from the template.

and click Next , On Configure stack options page , Leave it to default.

and Click Next , Review the Configurations and Click Create stack

The Stack creation should be initiated.

We can track all the events such as resources creation from the Events tab.

After the resources are created , We can check the lists of resources created by the stack from Resources tab.

The resources creation is completed.

From the Output tab , We can check all the resources and the values for the same.

Lets go to VPC Console and verify the Stack creation.

Choose Your VPCs , You can find the VPC created with the Environment Name.

Choose Subnets and verify Whether 2 Public and Private Subnets are created across Availability Zones.

Choose Route Tables , You can find 1 Public Route table and 2 Private Route Tables.

You can find 2 public subnets associated with a Public route Table.

If you click Routes , you can find the Internet gateway attached with the Public Route Table.

Same way , If we check the Private Route Tables , One subnet and One NAT Gateway should be attached with each Private Route Tables.

Choose Internet gateways , A Internet gateway should be created and attached with the VPC.

Choose NAT Gateways , You can find two NAT Gateways with Elastic IP addresses attached with it.

As we have confirmed that all the VPC resources required to be created is successfully created by the CloudFormation template.

Updating Or Modifying Existing Infra

Lets say You want to change or add to the existing infrastructure , In this case We can create a Change set and Execute the changes.

First We need to modify the VPC.yaml file.

I am adding a third Public subnet to the VPC. For that adding the below template to VPC.yaml file.

Under Parameters Section,

PublicSubnet3CIDR:
  Description: Enter CIDR notation for Public subnet in the Second Availability Zone
  Type: String
  Default: 10.0.12.0/24

Under Resources Section,

PublicSubnet3:
  Type: AWS::EC2::Subnet
  Properties:
    VpcId: !Ref VPC
    AvailabilityZone: !Select [ 0, !GetAZs '' ]
    CidrBlock: !Ref PublicSubnet3CIDR
    MapPublicIpOnLaunch: true
    Tags:
      - Key: Name
        Value: !Sub ${EnvironmentName} Public Subnet (AZ3)

After adding the above template to the VPC.yaml file.

Go to CloudFormation Console , Click the Stack which we have already created.

Click Change sets

And click Create change set , Click Replace current template , Then Choose upload a template file , here we need to upload the updated VPC.yaml file.

and then click Next , here you can find a New PublicSubnet Added.

Review the values and then click Next and finally choose Create change set

Provide a name for the Change set and then click Create change set

And Finally click Execute for the changes to take effect.

you can track the process from the Events section.

You will get the final response as shown below.

Lets go to VPC console and verify whether the new PublicSubnet is created.

You can see that the new public subnet is added to the VPC successfully.

Lets clean up the stack we have created.

Deleting Stack

If you wish to vanish the entire stack that is created using the CloudFormation template , Go to CloudFormation console.

Choose the Stack and then click Delete

and then Choose Delete stack.

Conclusion

We have successfully Provisioned Highly Available VPC architecture using CloudFormation.

Hope you find it helpful.Thanks for reading.

Please do check out my other articles.