How to Create VPC using CloudFormation [Public & Private Subnet]

How to Create VPC with Public and Private Subnet using CloudFormation

How to Create VPC using CloudFormation

Dear Reader, I hope you are doing well. In this post, I am sharing with you how to create VPC using CloudFormation on AWS.

When it comes to creating Amazon Virtual Private Cloud or VPC, you can use AWS Management Console, AWS CLI, AWS SDK etc. Although using the console’s VPC wizard looks like the easiest option but imagine if you have to do the same in multiple accounts and with the same configuration, I am sure it won’t be fun.

And, that’s when automation comes into the picture to make your life easier. You can make use of automation tools like CloudFormation or Terraform to create your VPC. This way you can reuse your template to create the same configuration in multiple accounts/environment

In this post, we’ll learn how to create VPC using CloudFormation. We will be creating this VPC with public and private subnets both. And we’ll also ensure connectivity is working perfectly after the setup.

In this tutorial, we’ll create:-

  • 1 VPC
  • 2 Public Subnet and 2 Private Subnet
  • 1 Internet Gateway and attach it to VPC
  • 1 Public Route table for all the public connectivity – all public traffic goes through the internet gateway
  • Associate both public subnets to public route table
  • 2 elastic IPs will be used by 2 NAT gateway
  • Create two private route tables one for each private subnet
  • 2 NAT Gateway to provide internet connectivity to our private subnet
  • Route private traffic of each subnet in the route table via the respective NAT gateway
  • Associate private subnet to the private route table
  • And we will have a full-function VPC with internet connection in private as well as public subnet

This is what it looks like –

How to Create VPC with Public and Private Subnet using CloudFormation designer

Prerequisites:

  • An AWS Account
  • Basic knowledge of JSON/YAML
  • Basic Knowledge of VPC
  • Basic Knowledge of CloudFormation

Basic Overview of Amazon VPC or Virtual Private Cloud

Amazon VPC or virtual private network is a locally isolated section of AWS where you can deploy your AWS resources like EC2. Ideally, it’s a virtual network on the cloud that you define and is isolated from other networks.

Every AWS account comes with a default VPC in each region which is used to launch resources when you don’t specify VPC explicitly. However, when you need a custom VPC, you can add various components to create a full function VPC.

Usually, it comprises of public subnets where you can keep your publicly accessible resource such as web servers and private subnets where you can keep private resources like your database.

Our example VPC:

vpc

Steps to Create VPC with Public and Private Subnet using CloudFormation

Let’s see the step-by-step instruction to create highly available and fault-tolerant Amazon VPC using CloudFormation.

  • Step 1: Provide proper permission
  • Step 2: Prepare a template
  • Step3: Create a Stack using the prepared template

Step 1: Provide proper permission

If you are not an admin user, you should explicitly provide vpc:* permission for your user/role. Additionally, you will also need cloudformation:* as well to be able to do CloudFormation stack creation, updation etc.

Note: For simplicity, I am using ‘*’ in the permission above. However, it is recommended to use the least privilege for security reasons.

Step 2: Prepare a template

You can use YAML or JSON for your template. I prefer YAML for writing my templates. But don’t worry, If you want it in JSON, I will provide a JSON template as well.

You can also learn to do it yourself using my previous tutorial Convert a CloudFormation Template from YAML to JSON and Vice Versa

To create a simple VPC, all you need is a AWS::EC2::VPC resource like below.

MyVPC: 
  Type: AWS::EC2::VPC
  Properties: 
    CidrBlock: String
    EnableDnsHostnames: Boolean
    EnableDnsSupport: Boolean
    InstanceTenancy: String
    Ipv4IpamPoolId: String
    Ipv4NetmaskLength: Integer
    Tags: 
      - Tag

Few things you should note there –

  • CidrBlock is the range of IP addresses that you choose for your private network.
  • EnableDnsHostnames determines if public DNS is assigned to the instance with public IP
  • EnableDnsSupport must be true if EnableDnsHostnames is true
  • InstanceTenancy by default is shared

Ideally, the template to create a simple VPC looks like below.

  Resources:
    DemoVPC:
      Type: AWS::EC2::VPC
      Properties:
        EnableDnsSupport: true
        EnableDnsHostnames: true
        CidrBlock: !Ref VPCCidr
        Tags:
          -
            Key: Name
            Value: !Ref VPCName

Subnets

  • A subnet is a range of IP addresses in your VPC
  • You launch your resources in a subnet
  • A subnet can not span multiple availability zones
  • You can create the public, private or VPN-only subnet
  • You use a public subnet for resources that needs internet connectivity and a private subnet for resources that are private
  • Each subnet must be associated with a route table which determines how outgoing traffic will be routed

Here is how to create a subnet using CloudFormation:

    PrivateSubnetB:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref DemoVPC
        AvailabilityZone: !Ref AvailabilityZoneB
        CidrBlock: !Ref PrivateSubnetBCidr
        Tags:
          -
            Key: Name
            Value: !Sub '${VPCName}-PrivateSubnetB'
  • It is taking vpcId, the availability zone in which it will be, and CidrBlock which is the IP range for it. The tag is just for naming purposes here.

The rest of the resources I am not describing here but a complete template is here below for your reference. Feel free to reach out to me if you face any problems.

Template to Create VPC using CloudFormation: YAML

  AWSTemplateFormatVersion: 2010-09-09
  Description: Custom VPC Creation With Private and Public Subnet

  Parameters:
    VPCName:
      Description: CIDR range for our VPC
      Type: String
      Default: DemoCustomVPC 
    VPCCidr:
      Description: CIDR range for our VPC
      Type: String
      Default: 10.0.0.0/16
    PrivateSubnetACidr:
      Description: Private Subnet IP Range
      Type: String
      Default: 10.0.0.0/24
    PrivateSubnetBCidr:
      Description: Private Subnet IP Range
      Type: String
      Default: 10.0.1.0/24
    PublicSubnetACidr:
      Description: Public Subnet IP Range
      Type: String
      Default: 10.0.2.0/24
    PublicSubnetBCidr:
      Description: Public Subnet IP Range
      Type: String
      Default: 10.0.3.0/24
    AvailabilityZoneA:
      Description: Avaibalbility Zone 1
      Type: String
      Default: ap-south-1a
    AvailabilityZoneB:
      Description: Avaibalbility Zone 2
      Type: String
      Default: ap-south-1b     
  Resources:
    DemoVPC:
      Type: AWS::EC2::VPC
      Properties:
        EnableDnsSupport: true
        EnableDnsHostnames: true
        CidrBlock: !Ref VPCCidr
        Tags:
          -
            Key: Name
            Value: !Ref VPCName

    PrivateSubnetA:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref DemoVPC
        AvailabilityZone: !Ref AvailabilityZoneA
        CidrBlock: !Ref PrivateSubnetACidr 
        Tags:
          -
            Key: Name
            Value: !Sub '${VPCName}-PrivateSubnetA'       

    PrivateSubnetB:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref DemoVPC
        AvailabilityZone: !Ref AvailabilityZoneB
        CidrBlock: !Ref PrivateSubnetBCidr
        Tags:
          -
            Key: Name
            Value: !Sub '${VPCName}-PrivateSubnetB'
        
    PublicSubnetA:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref DemoVPC
        AvailabilityZone: !Ref AvailabilityZoneA
        CidrBlock: !Ref PublicSubnetACidr
        MapPublicIpOnLaunch: true 
        Tags:
          -
            Key: Name
            Value: !Sub '${VPCName}-PublicSubnetA'        

    PublicSubnetB:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref DemoVPC
        AvailabilityZone: !Ref AvailabilityZoneB
        CidrBlock: !Ref PublicSubnetBCidr
        MapPublicIpOnLaunch: true
        Tags:
          -
            Key: Name
            Value: !Sub '${VPCName}-PublicSubnetB'


    InternetGateway:
      Type: AWS::EC2::InternetGateway

    VPCGatewayAttachment:
      Type: "AWS::EC2::VPCGatewayAttachment"
      Properties:
        VpcId: !Ref DemoVPC
        InternetGatewayId: !Ref InternetGateway

    PublicRouteTable:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref DemoVPC

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

    PublicSubnetRouteTableAssociationA:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PublicSubnetA
        RouteTableId: !Ref PublicRouteTable

    PublicSubnetRouteTableAssociationB:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PublicSubnetB
        RouteTableId: !Ref PublicRouteTable

    ElasticIPA:
      Type: AWS::EC2::EIP
      Properties:
        Domain: vpc

    ElasticIPB:
      Type: AWS::EC2::EIP
      Properties:
        Domain: vpc

    NATGatewayA:
      Type: AWS::EC2::NatGateway
      Properties:
        AllocationId: !GetAtt ElasticIPA.AllocationId
        SubnetId: !Ref PublicSubnetA

    NATGatewayB:
      Type: AWS::EC2::NatGateway
      Properties:
        AllocationId: !GetAtt ElasticIPB.AllocationId
        SubnetId: !Ref PublicSubnetB

    PrivateRouteTableA:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref DemoVPC

    PrivateRouteTableB:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref DemoVPC

    PrivateRouteToInternetA:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PrivateRouteTableA
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId: !Ref NATGatewayA

    PrivateRouteToInternetB:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PrivateRouteTableB
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId: !Ref NATGatewayB

    PrivateSubnetRouteTableAssociationA:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PrivateSubnetA
        RouteTableId: !Ref PrivateRouteTableA

    PrivateSubnetRouteTableAssociationB:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref PrivateSubnetB
        RouteTableId: !Ref PrivateRouteTableB
        
    #By this time, we have internet- lets launch an instance
    DemoInstance:
      Type: AWS::EC2::Instance
      Properties: 
        ImageId: ami-08df646e18b182346
        InstanceType: t2.micro
        AvailabilityZone: !Ref AvailabilityZoneA
        SubnetId: !Ref PublicSubnetA
        KeyName: MyDemoEC2eyPair
        SecurityGroupIds: 
          - !Ref DemoSecurityGroup
        UserData:
          Fn::Base64: 
            !Sub |
               #!/bin/bash
               yum update -y
               yum install -y httpd.x86_64
               systemctl start httpd.service
               systemctl enable httpd.service
               echo ?Hello World from $(hostname -f)? > /var/www/html/index.html
    DemoSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        VpcId: !Ref DemoVPC
        GroupDescription: SG to allow SSH and HTTP
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: '22'
            ToPort: '22'
            CidrIp: '0.0.0.0/0'
          - IpProtocol: tcp
            FromPort: 80
            ToPort: 80
            CidrIp: 0.0.0.0/0
        Tags:
          - Key: Name
            Value: EC2-SG
  Outputs:
    VPCId:
      Description: vpc id 
      Value: !Ref DemoVPC
    PublicSubnetA:
      Description: SubnetId of public subnet A
      Value: !Ref PublicSubnetA
    PublicSubnetB:
      Description: SubnetId of public subnet B
      Value: !Ref PublicSubnetB 
    PrivateSubnetA:
      Description: SubnetId of private subnet A
      Value: !Ref PrivateSubnetA
    PrivateSubnetB:
      Description: SubnetId of private subnet B
      Value: !Ref PublicSubnetB 
    DemoInstanceId:
       Description: Demo Instance Id 
       Value: !Ref DemoInstance

Template to Create VPC using CloudFormation: JSON

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Custom VPC Creation With Private and Public Subnet",
    "Parameters": {
        "VPCName": {
            "Description": "CIDR range for our VPC",
            "Type": "String",
            "Default": "DemoCustomVPC"
        },
        "VPCCidr": {
            "Description": "CIDR range for our VPC",
            "Type": "String",
            "Default": "10.0.0.0/16"
        },
        "PrivateSubnetACidr": {
            "Description": "Private Subnet IP Range",
            "Type": "String",
            "Default": "10.0.0.0/24"
        },
        "PrivateSubnetBCidr": {
            "Description": "Private Subnet IP Range",
            "Type": "String",
            "Default": "10.0.1.0/24"
        },
        "PublicSubnetACidr": {
            "Description": "Public Subnet IP Range",
            "Type": "String",
            "Default": "10.0.2.0/24"
        },
        "PublicSubnetBCidr": {
            "Description": "Public Subnet IP Range",
            "Type": "String",
            "Default": "10.0.3.0/24"
        },
        "AvailabilityZoneA": {
            "Description": "Avaibalbility Zone 1",
            "Type": "String",
            "Default": "ap-south-1a"
        },
        "AvailabilityZoneB": {
            "Description": "Avaibalbility Zone 2",
            "Type": "String",
            "Default": "ap-south-1b"
        }
    },
    "Resources": {
        "DemoVPC": {
            "Type": "AWS::EC2::VPC",
            "Properties": {
                "EnableDnsSupport": true,
                "EnableDnsHostnames": true,
                "CidrBlock": {
                    "Ref": "VPCCidr"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Ref": "VPCName"
                        }
                    }
                ]
            }
        },
        "PrivateSubnetA": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                },
                "AvailabilityZone": {
                    "Ref": "AvailabilityZoneA"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnetACidr"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Fn::Sub": "${VPCName}-PrivateSubnetA"
                        }
                    }
                ]
            }
        },
        "PrivateSubnetB": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                },
                "AvailabilityZone": {
                    "Ref": "AvailabilityZoneB"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnetBCidr"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Fn::Sub": "${VPCName}-PrivateSubnetB"
                        }
                    }
                ]
            }
        },
        "PublicSubnetA": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                },
                "AvailabilityZone": {
                    "Ref": "AvailabilityZoneA"
                },
                "CidrBlock": {
                    "Ref": "PublicSubnetACidr"
                },
                "MapPublicIpOnLaunch": true,
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Fn::Sub": "${VPCName}-PublicSubnetA"
                        }
                    }
                ]
            }
        },
        "PublicSubnetB": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                },
                "AvailabilityZone": {
                    "Ref": "AvailabilityZoneB"
                },
                "CidrBlock": {
                    "Ref": "PublicSubnetBCidr"
                },
                "MapPublicIpOnLaunch": true,
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Fn::Sub": "${VPCName}-PublicSubnetB"
                        }
                    }
                ]
            }
        },
        "InternetGateway": {
            "Type": "AWS::EC2::InternetGateway"
        },
        "VPCGatewayAttachment": {
            "Type": "AWS::EC2::VPCGatewayAttachment",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                },
                "InternetGatewayId": {
                    "Ref": "InternetGateway"
                }
            }
        },
        "PublicRouteTable": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                }
            }
        },
        "PublicRoute": {
            "Type": "AWS::EC2::Route",
            "DependsOn": "VPCGatewayAttachment",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PublicRouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "GatewayId": {
                    "Ref": "InternetGateway"
                }
            }
        },
        "PublicSubnetRouteTableAssociationA": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PublicSubnetA"
                },
                "RouteTableId": {
                    "Ref": "PublicRouteTable"
                }
            }
        },
        "PublicSubnetRouteTableAssociationB": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PublicSubnetB"
                },
                "RouteTableId": {
                    "Ref": "PublicRouteTable"
                }
            }
        },
        "ElasticIPA": {
            "Type": "AWS::EC2::EIP",
            "Properties": {
                "Domain": "vpc"
            }
        },
        "ElasticIPB": {
            "Type": "AWS::EC2::EIP",
            "Properties": {
                "Domain": "vpc"
            }
        },
        "NATGatewayA": {
            "Type": "AWS::EC2::NatGateway",
            "Properties": {
                "AllocationId": {
                    "Fn::GetAtt": [
                        "ElasticIPA",
                        "AllocationId"
                    ]
                },
                "SubnetId": {
                    "Ref": "PublicSubnetA"
                }
            }
        },
        "NATGatewayB": {
            "Type": "AWS::EC2::NatGateway",
            "Properties": {
                "AllocationId": {
                    "Fn::GetAtt": [
                        "ElasticIPB",
                        "AllocationId"
                    ]
                },
                "SubnetId": {
                    "Ref": "PublicSubnetB"
                }
            }
        },
        "PrivateRouteTableA": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                }
            }
        },
        "PrivateRouteTableB": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                }
            }
        },
        "PrivateRouteToInternetA": {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateRouteTableA"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGatewayA"
                }
            }
        },
        "PrivateRouteToInternetB": {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateRouteTableB"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGatewayB"
                }
            }
        },
        "PrivateSubnetRouteTableAssociationA": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnetA"
                },
                "RouteTableId": {
                    "Ref": "PrivateRouteTableA"
                }
            }
        },
        "PrivateSubnetRouteTableAssociationB": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnetB"
                },
                "RouteTableId": {
                    "Ref": "PrivateRouteTableB"
                }
            }
        },
        "DemoInstance": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": "ami-08df646e18b182346",
                "InstanceType": "t2.micro",
                "AvailabilityZone": {
                    "Ref": "AvailabilityZoneA"
                },
                "SubnetId": {
                    "Ref": "PublicSubnetA"
                },
                "KeyName": "MyDemoEC2eyPair",
                "SecurityGroupIds": [
                    {
                        "Ref": "DemoSecurityGroup"
                    }
                ],
                "UserData": {
                    "Fn::Base64": {
                        "Fn::Sub": "#!/bin/bash\nyum update -y\nyum install -y httpd.x86_64\nsystemctl start httpd.service\nsystemctl enable httpd.service\necho ?Hello World from $(hostname -f)? > /var/www/html/index.html\n"
                    }
                }
            }
        },
        "DemoSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "VpcId": {
                    "Ref": "DemoVPC"
                },
                "GroupDescription": "SG to allow SSH and HTTP",
                "SecurityGroupIngress": [
                    {
                        "IpProtocol": "tcp",
                        "FromPort": "22",
                        "ToPort": "22",
                        "CidrIp": "0.0.0.0/0"
                    },
                    {
                        "IpProtocol": "tcp",
                        "FromPort": 80,
                        "ToPort": 80,
                        "CidrIp": "0.0.0.0/0"
                    }
                ],
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "EC2-SG"
                    }
                ]
            }
        }
    },
    "Outputs": {
        "VPCId": {
            "Description": "vpc id",
            "Value": {
                "Ref": "DemoVPC"
            }
        },
        "PublicSubnetA": {
            "Description": "SubnetId of public subnet A",
            "Value": {
                "Ref": "PublicSubnetA"
            }
        },
        "PublicSubnetB": {
            "Description": "SubnetId of public subnet B",
            "Value": {
                "Ref": "PublicSubnetB"
            }
        },
        "PrivateSubnetA": {
            "Description": "SubnetId of private subnet A",
            "Value": {
                "Ref": "PrivateSubnetA"
            }
        },
        "PrivateSubnetB": {
            "Description": "SubnetId of private subnet B",
            "Value": {
                "Ref": "PublicSubnetB"
            }
        },
        "DemoInstanceId": {
            "Description": "Demo Instance Id",
            "Value": {
                "Ref": "DemoInstance"
            }
        }
    }
}

Step3: Create a Stack using the prepared template

Now, we know the basics and we have the template so let’s go and create the stack.

  1. Grab the YAML or JSON template from above at your convenience.
  2. Save the template with .yml or .json as per the choice of template and follow the below steps.
  3. Login to AWS Management Console, navigate to CloudFormation and click on Create stack
  4. Click on “Upload a template file”, upload your saved .yml  or .json file and click Next
  5. Enter the stack name and click on Next. In the configuration, keep everything as default and click on Next.
  6. In the events tab of stack, you can view the status.
  7. Once stack is successfully created, you can go to VPC service and verify your VPC.
  8. Also, you can check resource tab of your CloudFormation stack to view all the resources that are created. You can navigate to those resources from here itself.
How to Create VPC with Public and Private Subnet using CloudFormation 2

Clean Up

If you are creating this VPC for learning purpose. Don’t forget to delete your CloudFormation stack so that your VPC and related components are deleted and you don’t bear any cost.

Select your stack and click on the Delete button as shown below to delete your stack.

How to Create VPC with Public and Private Subnet using CloudFormation 2

Note: Please note that, this template contains NAT gateways and EC2 and if left undestroyed, you are gonna bear cost. So please go ahead and delete the stack if you don’t need it.

Happy Learning !!!

Conclusion:

In this post, we learnt how to create VPC using CloudFormation

  • We created a fully functional and highly available VPC with public and private subnets
  • Used internet gateway and NAT gateway for internet connectivity in public and private subnets respectively
  • Created an EC2 instance using created VPC and accessed it over the internet

I hope you found this post helpful.

Enjoyed the content?

Subscribe to our newsletter below to get awesome AWS learning materials delivered straight to your inbox.

Don’t forget to motivate me by-

  • Adding a comment below on what you liked and what can be improved.
  • Follow us on
  • Subscribe to our newsletter to get notified each time we post new content
  • Share this post with your friends

Suggested Read:

One thought on “How to Create VPC using CloudFormation [Public & Private Subnet]

Leave a Reply

Your email address will not be published. Required fields are marked *