AWS Cloudformation - Partie 2 | Agile Partner
share on

AWS Cloudformation – Part 2

By Olivier Robert, a Senior Consultant and DevOps Engineer at Agile Partner.

In Part1, we worked on a template to create a VPC. We will extend this template with subnets and route tables. We’ll have public and private subnets.

Private subnets will be used for everything we want to shield from a direct internet access.

Public subnets means we want to be able to connect to services directly over an internet connection.

Our VPC will need an internet gateway. The default routing for our public subnet will be the Intenet Gateway.

Private subnets can be accessed from public subnets or services if this is what we want, but they will have no routing to the Internet Gateway and thus not internet access inbound, or outbound. An EC2 instance deployed in a private subnet will not be able to get updates from the internet for example. To remedy this situation, we can use a NAT Gateway.

The Nat Gateway, deployed in the public subnet, will relay internet connections initiated from the private subnet to the internet via the Internet Gateway. But there is still no way to directly connect from the internet to the private subnet. This is what we want.

Routing. We will have to create route tables and default routings and then associate public and private subnets to specific routing tables.

If you have read until here, have a look at this AWS scenario. It will clear things up.

Mmmm, one more thing, we will use Availability Zones to our advantage and distribute our subnets so that they are not all created in the same AZ.

Adding an Internet Gateway is straight forward:

Resources:
[...]
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags:
        - Key: Name
          Value: "Project X"

The NAT Gateway will need an Elastic IP allocated to it.

Resources:
[...]
  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

Domain is set to vpc to allocate the address for use with instances in a VPC. In our case, the NAT Gateway.

Resources:
[...]
  NatGW:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - EIP
          - AllocationId
      SubnetId: !Ref VPCPublicSubnet1
      Tags:
        - Key: Name
          Value: "Project X"

We use the intrinsic function GetAtt to retrieve the allocation ID attribute from the Elastic IP resource we named EIP.

For the subnet in which the NAT Gateway should be deployed in, we use the Ref intrinsic function to reference a public subnet. That public subnet has not been defined yet, but we will name it « VPCPublicSubnet1 ».

For now, we’ll continue with the NAT Gateway and attach it to our VPC and specify the Internet Gateway.

Resources:
[...]
  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref IGW
      VpcId: !Ref myVPC

Notice that we use resource references.

If you wonder how resources are created, Cloudformation creates independent resources in parallel. Implicit dependencies are detected when the property of one resource is referring to another resource (for instance an Internet Gateway in a VPCGatewayAttachment). Explicit dependencies can be declared with the DependsOn attribute. When we are using Ref to reference resources, we are creating implicit dependencies. The referenced resources should be created first. Sometimes, when things get complicated, explicit dependencies can solve issues.

In public subnets, we’ll want automatic public IP allocations and as we are going to create 3 public subnets, we will distribute them over Availability Zones.

Resources:
[...]
  VPCPublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      CidrBlock: !Ref PublicSubnet1CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPub1
      VpcId: !Ref myVPC
  
  VPCPublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      CidrBlock: !Ref PublicSubnet2CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 1
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPub2
      VpcId: !Ref myVPC

  VPCPublicSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      CidrBlock: !Ref PublicSubnet3CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 2
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPub3
      VpcId: !Ref myVPC

Remember our NAT Gateway resource? Now the reference to VPCPublicSubnet1 is valid.

MapPublicIpOnLaunch is useful because instances launched in public subnets will get a public IP automatically.

The GetAZs intrisic function returns an array of Availability Zones. We just pick one in the array for each subnet with Select.

We introduced PublicSubnet<#>CIDR which does not exist yet! These are references to parameters. Let’s sort this out.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Network Configuration
      Parameters:
      - VPCCIDR
      - PublicSubnet1CIDR
      - PublicSubnet2CIDR
      - PublicSubnet3CIDR
    ParameterLabels:
      VPCCIDR:
        default: VPC CIDR
      PublicSubnet1CIDR:
        default: Public subnet 1 CIDR
      PublicSubnet2CIDR:
        default: Public subnet 2 CIDR
      PublicSubnet3CIDR:
        default: Public subnet 3 CIDR

Parameters:
[...]
  PublicSubnet1CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.1.0/24
    Description: CIDR Block for the public DMZ subnet 1 located in Availability Zone 1
    Type: String
  PublicSubnet2CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.2.0/24
    Description: CIDR Block for the public DMZ subnet 2 located in Availability Zone 2
    Type: String
  PublicSubnet3CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.3.0/24
    Description: CIDR Block for the public DMZ subnet 3 located in Availability Zone 3
    Type: String

I added parameter labels as well in the metadata section.

Next, private subnets where we do not need public adresses.

So far, our entire template looks like this:

AWSTemplateFormatVersion: 2010-09-09
Description: VPC template for Project X

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Network Configuration
      Parameters:
      - VPCCIDR
      - PublicSubnet1CIDR
      - PublicSubnet2CIDR
      - PublicSubnet3CIDR
      - PrivateSubnet1CIDR
      - PrivateSubnet2CIDR
      - PrivateSubnet3CIDR
    ParameterLabels:
      VPCCIDR:
        default: VPC CIDR
      PublicSubnet1CIDR:
        default: Public subnet 1 CIDR
      PublicSubnet2CIDR:
        default: Public subnet 2 CIDR
      PublicSubnet3CIDR:
        default: Public subnet 3 CIDR
      PrivateSubnet1CIDR:
        default: Private subnet 1 CIDR
      PrivateSubnet2CIDR:
        default: Private subnet 2 CIDR
      PrivateSubnet3CIDR:
        default: Private subnet 3 CIDR

Parameters:
  VPCCIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.0.0/16
    Description: CIDR Block for the VPC
    Type: String
  PublicSubnet1CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.1.0/24
    Description: CIDR Block for the public DMZ subnet 1 located in Availability Zone 1
    Type: String
  PublicSubnet2CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.2.0/24
    Description: CIDR Block for the public DMZ subnet 2 located in Availability Zone 2
    Type: String
  PublicSubnet3CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.3.0/24
    Description: CIDR Block for the public DMZ subnet 3 located in Availability Zone 3
    Type: String
  PrivateSubnet1CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.10.0/24
    Description: CIDR block for private subnet 1 located in Availability Zone 1
    Type: String
  PrivateSubnet2CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.11.0/24
    Description: CIDR block for private subnet 2 located in Availability Zone 2
    Type: String
  PrivateSubnet3CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
    Default: 10.0.12.0/24
    Description: CIDR block for private subnet 3 located in Availability Zone 3
    Type: String
 

Resources:
  myVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: True
      InstanceTenancy: "default"
      Tags:
        - Key: Name
          Value: "Project X"

  IGW:
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags:
        - Key: Name
          Value: "Project X"

  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  
  NatGW:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - EIP
          - AllocationId
      SubnetId: !Ref VPCPublicSubnet1
      Tags:
        - Key: Name
          Value: "Project X"

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref IGW
      VpcId: !Ref myVPC

  VPCPublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      CidrBlock: !Ref PublicSubnet1CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPub1
      VpcId: !Ref myVPC
  
  VPCPublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      CidrBlock: !Ref PublicSubnet2CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 1
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPub2
      VpcId: !Ref myVPC

  VPCPublicSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      CidrBlock: !Ref PublicSubnet3CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 2
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPub3
      VpcId: !Ref myVPC

  VPCPrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PrivateSubnet1CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPriv1
      VpcId: !Ref myVPC
  
  VPCPrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PrivateSubnet2CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 1
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPriv2
      VpcId: !Ref myVPC
  
  VPCPrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PrivateSubnet3CIDR
      AvailabilityZone: 
        Fn::Select: 
          - 2
          - Fn::GetAZs: ""
      Tags:
        - Key: Name
          Value: ProjectXPriv3
      VpcId: !Ref myVPC

Note that we have indicated default CIDR values for the subnets to avoid typing all parameters by hand in the web interface. The only thing we need to enter manually when creating the stack is a stack name.

It’s time to get the routing part done. We will create a route table and a default route. Our route table only needs a reference to a VPC.

Resources:
[...]
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties: 
      VpcId: !Ref myVPC
      Tags:
        - Key: Name
          Value: ProjectXPubRT

Our default route will route any traffic to the internet gateway:

Resources:
[...]
  PublicDefaultRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref IGW
      RouteTableId: !Ref PublicRouteTable

This public route table (containing the default route) needs to be associated with the public subnets.

Resources:
[...]
  PublicRouteSubnetAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref VPCPublicSubnet1
  
  PublicRouteSubnetAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref VPCPublicSubnet2

  PublicRouteSubnetAssociation3:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref VPCPublicSubnet3

We will do the same for the private subnet except these subnet will have a default route routing to the NAT Gateway.

Our final VPC template is this:

  1. AWSTemplateFormatVersion: 2010-09-09
  2. Description: VPC template for Project X
  3.  
  4. Metadata:
  5.   AWS::CloudFormation::Interface:
  6.     ParameterGroups:
  7.     - Label:
  8.         default: Network Configuration
  9.       Parameters:
  10.       - VPCCIDR
  11.       - PublicSubnet1CIDR
  12.       - PublicSubnet2CIDR
  13.       - PublicSubnet3CIDR
  14.       - PrivateSubnet1CIDR
  15.       - PrivateSubnet2CIDR
  16.       - PrivateSubnet3CIDR
  17.     ParameterLabels:
  18.       VPCCIDR:
  19.         default: VPC CIDR
  20.       PublicSubnet1CIDR:
  21.         default: Public subnet 1 CIDR
  22.       PublicSubnet2CIDR:
  23.         default: Public subnet 2 CIDR
  24.       PublicSubnet3CIDR:
  25.         default: Public subnet 3 CIDR
  26.       PrivateSubnet1CIDR:
  27.         default: Private subnet 1 CIDR
  28.       PrivateSubnet2CIDR:
  29.         default: Private subnet 2 CIDR
  30.       PrivateSubnet3CIDR:
  31.         default: Private subnet 3 CIDR
  32.  
  33. Parameters:
  34.   VPCCIDR:
  35.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  36.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  37.     Default: 10.0.0.0/16
  38.     Description: CIDR Block for the VPC
  39.     Type: String
  40.   PublicSubnet1CIDR:
  41.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  42.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  43.     Default: 10.0.1.0/24
  44.     Description: CIDR Block for the public DMZ subnet 1 located in Availability Zone 1
  45.     Type: String
  46.   PublicSubnet2CIDR:
  47.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  48.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  49.     Default: 10.0.2.0/24
  50.     Description: CIDR Block for the public DMZ subnet 2 located in Availability Zone 2
  51.     Type: String
  52.   PublicSubnet3CIDR:
  53.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  54.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  55.     Default: 10.0.3.0/24
  56.     Description: CIDR Block for the public DMZ subnet 3 located in Availability Zone 3
  57.     Type: String
  58.   PrivateSubnet1CIDR:
  59.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  60.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  61.     Default: 10.0.10.0/24
  62.     Description: CIDR block for private subnet 1 located in Availability Zone 1
  63.     Type: String
  64.   PrivateSubnet2CIDR:
  65.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  66.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  67.     Default: 10.0.11.0/24
  68.     Description: CIDR block for private subnet 2 located in Availability Zone 2
  69.     Type: String
  70.   PrivateSubnet3CIDR:
  71.     AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
  72.     ConstraintDescription: Must be a valid IP range in x.x.x.x/x notation
  73.     Default: 10.0.12.0/24
  74.     Description: CIDR block for private subnet 3 located in Availability Zone 3
  75.     Type: String
  76.  
  77.  
  78. Resources:
  79.   myVPC:
  80.     Type: AWS::EC2::VPC
  81.     Properties:
  82.       CidrBlock: !Ref VPCCIDR
  83.       EnableDnsSupport: true
  84.       EnableDnsHostnames: True
  85.       InstanceTenancy: "default"
  86.       Tags:
  87.         - Key: Name
  88.           Value: "Project X"
  89.  
  90.   IGW:
  91.     Type: AWS::EC2::InternetGateway
  92.     Properties: 
  93.       Tags:
  94.         - Key: Name
  95.           Value: "Project X"
  96.  
  97.   EIP:
  98.     Type: AWS::EC2::EIP
  99.     Properties:
  100.       Domain: vpc
  101.  
  102.   NatGW:
  103.     Type: AWS::EC2::NatGateway
  104.     Properties:
  105.       AllocationId:
  106.         Fn::GetAtt:
  107.           - EIP
  108.           - AllocationId
  109.       SubnetId: !Ref VPCPublicSubnet1
  110.       Tags:
  111.         - Key: Name
  112.           Value: "Project X"
  113.  
  114.   VPCGatewayAttachment:
  115.     Type: AWS::EC2::VPCGatewayAttachment
  116.     Properties: 
  117.       InternetGatewayId: !Ref IGW
  118.       VpcId: !Ref myVPC
  119.  
  120.   VPCPublicSubnet1:
  121.     Type: AWS::EC2::Subnet
  122.     Properties:
  123.       MapPublicIpOnLaunch: True
  124.       CidrBlock: !Ref PublicSubnet1CIDR
  125.       AvailabilityZone: 
  126.         Fn::Select: 
  127.           - 0
  128.           - Fn::GetAZs: ""
  129.       Tags:
  130.         - Key: Name
  131.           Value: ProjectXPub1
  132.       VpcId: !Ref myVPC
  133.  
  134.   VPCPublicSubnet2:
  135.     Type: AWS::EC2::Subnet
  136.     Properties:
  137.       MapPublicIpOnLaunch: True
  138.       CidrBlock: !Ref PublicSubnet2CIDR
  139.       AvailabilityZone: 
  140.         Fn::Select: 
  141.           - 1
  142.           - Fn::GetAZs: ""
  143.       Tags:
  144.         - Key: Name
  145.           Value: ProjectXPub2
  146.       VpcId: !Ref myVPC
  147.  
  148.   VPCPublicSubnet3:
  149.     Type: AWS::EC2::Subnet
  150.     Properties:
  151.       MapPublicIpOnLaunch: True
  152.       CidrBlock: !Ref PublicSubnet3CIDR
  153.       AvailabilityZone: 
  154.         Fn::Select: 
  155.           - 2
  156.           - Fn::GetAZs: ""
  157.       Tags:
  158.         - Key: Name
  159.           Value: ProjectXPub3
  160.       VpcId: !Ref myVPC
  161.  
  162.   VPCPrivateSubnet1:
  163.     Type: AWS::EC2::Subnet
  164.     Properties:
  165.       CidrBlock: !Ref PrivateSubnet1CIDR
  166.       AvailabilityZone: 
  167.         Fn::Select: 
  168.           - 0
  169.           - Fn::GetAZs: ""
  170.       Tags:
  171.         - Key: Name
  172.           Value: ProjectXPriv1
  173.       VpcId: !Ref myVPC
  174.  
  175.   VPCPrivateSubnet2:
  176.     Type: AWS::EC2::Subnet
  177.     Properties:
  178.       CidrBlock: !Ref PrivateSubnet2CIDR
  179.       AvailabilityZone: 
  180.         Fn::Select: 
  181.           - 1
  182.           - Fn::GetAZs: ""
  183.       Tags:
  184.         - Key: Name
  185.           Value: ProjectXPriv2
  186.       VpcId: !Ref myVPC
  187.  
  188.   VPCPrivateSubnet3:
  189.     Type: AWS::EC2::Subnet
  190.     Properties:
  191.       CidrBlock: !Ref PrivateSubnet3CIDR
  192.       AvailabilityZone: 
  193.         Fn::Select: 
  194.           - 2
  195.           - Fn::GetAZs: ""
  196.       Tags:
  197.         - Key: Name
  198.           Value: ProjectXPriv3
  199.       VpcId: !Ref myVPC
  200.  
  201.   PublicRouteTable:
  202.     Type: AWS::EC2::RouteTable
  203.     Properties: 
  204.       VpcId: !Ref myVPC
  205.       Tags:
  206.         - Key: Name
  207.           Value: ProjectXPubRT
  208.  
  209.   PublicDefaultRoute:
  210.     Type: AWS::EC2::Route
  211.     Properties:
  212.       DestinationCidrBlock: "0.0.0.0/0"
  213.       GatewayId: !Ref IGW
  214.       RouteTableId: !Ref PublicRouteTable
  215.  
  216.   PublicRouteSubnetAssociation1:
  217.     Type: AWS::EC2::SubnetRouteTableAssociation
  218.     Properties: 
  219.       RouteTableId: !Ref PublicRouteTable
  220.       SubnetId: !Ref VPCPublicSubnet1
  221.  
  222.   PublicRouteSubnetAssociation2:
  223.     Type: AWS::EC2::SubnetRouteTableAssociation
  224.     Properties: 
  225.       RouteTableId: !Ref PublicRouteTable
  226.       SubnetId: !Ref VPCPublicSubnet2
  227.  
  228.   PublicRouteSubnetAssociation3:
  229.     Type: AWS::EC2::SubnetRouteTableAssociation
  230.     Properties: 
  231.       RouteTableId: !Ref PublicRouteTable
  232.       SubnetId: !Ref VPCPublicSubnet3
  233.  
  234.   PrivateRouteTable:
  235.     Type: AWS::EC2::RouteTable
  236.     Properties: 
  237.       VpcId: !Ref myVPC
  238.       Tags:
  239.         - Key: Name
  240.           Value: ProjectXPrivRT 
  241.  
  242.   PrivateDefaultRoute:
  243.     Type: AWS::EC2::Route
  244.     Properties:
  245.       DestinationCidrBlock: "0.0.0.0/0"
  246.       NatGatewayId: !Ref NatGW
  247.       RouteTableId: !Ref PrivateRouteTable
  248.  
  249.   PrivateRouteSubnetAssociation1:
  250.     Type: AWS::EC2::SubnetRouteTableAssociation
  251.     Properties: 
  252.       RouteTableId: !Ref PrivateRouteTable
  253.       SubnetId: !Ref VPCPrivateSubnet1
  254.  
  255.   PrivateRouteSubnetAssociation2:
  256.     Type: AWS::EC2::SubnetRouteTableAssociation
  257.     Properties: 
  258.       RouteTableId: !Ref PrivateRouteTable
  259.       SubnetId: !Ref VPCPrivateSubnet2
  260.  
  261.   PrivateRouteSubnetAssociation3:
  262.     Type: AWS::EC2::SubnetRouteTableAssociation
  263.     Properties: 
  264.       RouteTableId: !Ref PrivateRouteTable
  265.       SubnetId: !Ref VPCPrivateSubnet3

If you haven’t uploaded the template to Cloudformation, here is the parameters section you’d get before launching it.

Stay tuned for Part 3 where we will deploy an EC2 bastion host in the public subnet and an EC2 instance in the private subnets to validate our configuration.

Want to know more? The experts of our Agile Software Factory are here to help you!

share on