AWS CFN - Create VPC and subnets
This is a fundamental example of creating AWS VPC and the subnets using AWS Cloudformation(CFN). In the next post, I’ve discussed the AWS CFN - Create IGW and NAT.
The prerequisite for this post is AWS Cloudformation to create AWS VPC.
Here is the CFN YAML to create the above architecture. Please visit the reference1 I used to create this post if you want more information. As shown in the above diagram x.y prefix (at line# 4), you must pass the parameter as --parameters ParameterKey=VpcCidrPrefix,ParameterValue=10.0 when you are creating the stack.
AWSTemplateFormatVersion: "2010-09-09"
Description: My VPC example
Parameters:
  VpcCidrPrefix:
    Description: prefix for the CIDER
    Type: String
    AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})"
Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Join [ "", [!Ref VpcCidrPrefix, ".0.0/21"]]
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  subnetDmzA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".0.0/24"]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: DEV-DMZ A
        - Key: Scope
          Value: public
      VpcId: !Ref Vpc
  subnetDmzB:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".1.0/24"]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: DEV-DMZ B
        - Key: Scope
          Value: public
      VpcId: !Ref Vpc    
  subnetDmzC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".2.0/24"]]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: DEV-DMZ C
        - Key: Scope
          Value: public
      VpcId: !Ref Vpc
  subnetAppA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".3.0/24"]]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: DEV-APP B
        - Key: Scope
          Value: private
      VpcId: !Ref Vpc 
  subnetAppB:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".4.0/24"]]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: DEV-APP B
        - Key: Scope
          Value: private
      VpcId: !Ref Vpc  
  subnetAppC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".5.0/24"]]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: DEV-APP C
        - Key: Scope
          Value: private
      VpcId: !Ref Vpc 
  subnetDbA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".6.0/28"]]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: DEV-DB A
        - Key: Scope
          Value: private
      VpcId: !Ref Vpc
  subnetDbB:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".6.16/28"]]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: DEV-DB B
        - Key: Scope
          Value: private
      VpcId: !Ref Vpc  
  subnetDbC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Join ["", [!Ref VpcCidrPrefix, ".6.32/28"]]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: DEV-DB C
        - Key: Scope
          Value: private
      VpcId: !Ref Vpc 
Outputs:
  VpcId:
    Description: "VPC ID"
    Value: !Ref Vpc
    Export:
      Name: !Sub ${AWS::StackName}-VpcId
  VpcCidr:
    Description: "VPC ID"
    Value: !GetAtt Vpc.CidrBlock
    Export:
      Name: !Sub  ${AWS::StackName}-VpcCidr
  DmzSubnetAId:
    Description: "DMZ A subnet ID"
    Value: !Ref subnetDmzA
    Export:
      Name: !Sub ${AWS::StackName}-DmzSubnetAId     
  DmzSubnetBId:
    Description: "DMZ B subnet ID"
    Value: !Ref subnetDmzB
    Export:
      Name: !Sub ${AWS::StackName}-DmzSubnetBId  
  DmzSubnetCId:
    Description: "DMZ C subnet ID"
    Value: !Ref subnetDmzC
    Export:
      Name: !Sub ${AWS::StackName}-DmzSubnetCId  
  SubnetAppAId:
    Description: "App A subnet ID"
    Value: !Ref subnetAppA
    Export:
      Name: !Sub ${AWS::StackName}-SubnetAppAId              
  SubnetAppBId:
    Description: "App B subnet ID"
    Value: !Ref subnetAppB
    Export:
      Name: !Sub ${AWS::StackName}-SubnetAppBId      
  SubnetAppCId:
    Description: "App C subnet ID"
    Value: !Ref subnetAppC
    Export:
      Name: !Sub ${AWS::StackName}-SubnetAppCId     
  SubnetDbAId:
    Description: "DB A subnet ID"
    Value: !Ref subnetDbA
    Export:
      Name: !Sub ${AWS::StackName}-SubnetDbAId
  SubnetDbBId:
    Description: "DB B subnet ID"
    Value: !Ref subnetDbB
    Export:
      Name: !Sub ${AWS::StackName}-SubnetDbBId
  SubnetDbCId:
    Description: "DB C subnet ID"
    Value: !Ref subnetDbC
    Export:
      Name: !Sub ${AWS::StackName}-SubnetDbCId    
       
Validate the template:
aws cloudformation validate-template --template-body file://vpc-example-1.yaml
If there are no errors, create the stack.
Remember to pass the parameters for the
x.yshown above.
aws cloudformation create-stack --template-body file://vpc-example-1.yaml --parameters ParameterKey=VpcCidrPrefix,ParameterValue=10.0  --stack-name oj-test-stack
To list all the exports.
aws cloudformation list-exports --query 'Exports[].[Name,Value]' --output table
The output will be
------------------------------------------------------------
|                        ListExports                       |
+-----------------------------+----------------------------+
|  oj-test-stack-DmzSubnetAId |  subnet-0fb6a12ac99d3b1e6  |
|  oj-test-stack-DmzSubnetBId |  subnet-03882aa1d2eadbd78  |
|  oj-test-stack-DmzSubnetCId |  subnet-01e4c2fa5bbb158b4  |
|	 ...                        |  ...                       |
|  oj-test-stack-VpcCidr      |  10.0.0.0/21               |
|  oj-test-stack-VpcId        |  vpc-055a7ad17ccfa1fb7     |
+-----------------------------+----------------------------+
if you want information about the above VPC created as oj-test-stack:
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=oj-test-stack"
to get only the VpcId:
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=oj-test-stack" --query 'Vpcs[0].VpcId' --output text
if you want to use the above VpcId , then assign that to a variable
VPC_ID=$(aws ec2...above command)
For example to list all the subnets and its cidr block:
aws ec2 describe-subnets --filters "Name=vpc-id,Values=${VPC_ID}" --query 'Subnets[].[SubnetId,CidrBlock,Tags[?Key==`Scope`]|[0].Value]' --output table
the output will be
---------------------------------------------------------
|                    DescribeSubnets                    |
+---------------------------+----------------+----------+
|  subnet-01355rt561041ec99 |  10.0.6.16/28  |  private |
|  subnet-...               |  10.0.3.0/24   |  private |
|  subnet-...               |  10.0.6.32/28  |  private |
|  ...                      |  10.0.1.0/24   |  public  |
|  subnet-...               |  10.0.0.0/24   |  public  |
|  subnet-...               |  10.0.2.0/24   |  public  |
+---------------------------+----------------+----------+
More information such as AZ and number of availble IPs, you can get as follows:
aws ec2 describe-subnets --filters "Name=vpc-id,Values=${VPC_ID}" --query 'Subnets[].[Tags[?Key==`Name`]|[0].Value,SubnetId,CidrBlock,Tags[?Key==`Scope`]|[0].Value,AvailableIpAddressCount,AvailabilityZone]' --output table

