AWS YAML sample CloudFormation template to create VPC

CloudFormationEarlier, I posted an example CoudFormation template that creates a simple VPC and promised an update that would also create a a VPC with an EC2 Linux jump server. That template is below — and is, surprise!, in YAML.

While I am not a fan of the tooling AWS provides for CloudFormation, now that you can use its CloudFormation Designer with YAML, there’s no reason not to use YAML. YAML is so much easier to code and debug, though I wish my favorite editor (Visual Studio Code) had better support for YAML indentation. I’m not sure which is harder: debugging JSON with missing braces and/or brackets or YAML with messed up indentation. Wait a minute….YAML is definitely easier. 🙂 And this nifty little translation tool between JSON and YAML makes conversions easy.

I was reminded just today of how useful these skeleton, but runnable, templates can be. A client asked for a management VPC to be peered to a running production VPC. Using this template, I produced a VPC to the client’s specifications in about 10 minutes — far faster than you can do it by hand. And, everything is nicely labelled via tags and clear to anyone who looks at it. I did add the VPC peering connections by hand simply because that was faster and I wanted the client to be able to respond to his internal users before lunch. That’s the power of using CloudFormation!

Here are some notes on this template:

  • It builds precisely the same VPC and subnets as the earlier post
  • It has a table for AWS Linux AMIs only in the regions I use
  • Be sure you have a valid EC2 key pair that you can specify when using the template — the name in the KeyPairName parameter is invalid.

I hope you find this as useful as I have. I have been gratified by the positive comments on previous CloudFormation sample posts and hope you’ll take a minute to comment, good or bad, on this example.

AWSTemplateFormatVersion: '2010-09-09'
Description: Creates a two-subnet VPC (public w/ NAT gateway and private) with a Linux
  bastion instance in the public subnet (c) 2017 Air11 Technology LLC -- licensed
  under the Apache OpenSource 2.0 license, https://opensource.org/licenses/Apache-2.0
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: VPC configuration parameters
      Parameters:
      - VPCCIDR
      - PublicSubnetCIDR
      - PrivateSubnetCIDR
      - SSHLocation
      - KeyPairName
    ParameterLabels:
      VPCCIDR:
        default: Enter CIDR of new VPC
      PublicSubnetCIDR:
        default: Enter CIDR of the public subnet
      PrivateSubnetCIDR:
        default: Enter CIDR of the private subnet
      SSHLocation:
        default: Subnet allowed to ssh on TCP to public subnet
      KeyPairName:
        default: Key pair for bastion host
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])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.10.0.0/16
    Description: CIDR block for entire VPC.
    Type: String
  PublicSubnetCIDR:
    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])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.10.10.0/24
    Description: CIDR block for the public subnet
    Type: String
  PrivateSubnetCIDR:
    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])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.10.20.0/24
    Description: CIDR block for the private subnet
    Type: String
  SSHLocation:
    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: CIDR block parameter must be in the form x.x.x.x/0-28
    Default: 0.0.0.0/0
    Description: Network allowed to ssh to instances in public subnet.
    Type: String
  KeyPairName:
    Description: Keypair for Linux bastion host
    Type: AWS::EC2::KeyPair::KeyName
    Default: your-keypair-name-here
Mappings:
  RegionMap:
    us-east-1:
      AMI: ami-22ce4934
    us-west-1:
      AMI: ami-9e247efe
    us-east-2:
      AMI: ami-7bfcd81e
    us-west-2:
      AMI: ami-8ca83fec
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      CidrBlock: !Ref 'VPCCIDR'
      Tags:
      - Key: Name
        Value: !Sub 'VPC ${VPCCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: IGW
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PublicSubnetCIDR'
      Tags:
      - Key: Name
        Value: !Sub 'Public ${PublicSubnetCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PrivateSubnetCIDR'
      Tags:
      - Key: Name
        Value: !Sub 'Private ${PrivateSubnetCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref 'VPC'
      InternetGatewayId: !Ref 'InternetGateway'
  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  NAT:
    DependsOn: AttachGateway
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt 'EIP.AllocationId'
      SubnetId: !Ref 'PublicSubnet'
  PublicSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
      - Key: Name
        Value: !Sub 'Public ${PublicSubnetCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref 'PublicSubnetRouteTable'
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref 'InternetGateway'
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet'
      RouteTableId: !Ref 'PublicSubnetRouteTable'
  PublicInstanceSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref 'VPC'
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp: !Ref 'SSHLocation'
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0
      Tags:
      - Key: Name
        Value: !Sub 'PublicSG ${VPCCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  PrivateSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
      - Key: Name
        Value: !Sub 'Private ${PrivateSubnetCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  PrivateSubnetRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref 'PrivateSubnetRouteTable'
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref 'NAT'
  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet'
      RouteTableId: !Ref 'PrivateSubnetRouteTable'
  PrivateSubnetInstanceSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref 'VPC'
      GroupDescription: Enable all TCP ports from instances in this VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '1'
        ToPort: '65535'
        CidrIp: !Ref 'VPCCIDR'
      Tags:
      - Key: Name
        Value: !Sub 'PrivateSG ${VPCCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
  LinuxBastionHost:
    Type: AWS::EC2::Instance
    DependsOn:
    - AttachGateway
    Properties:
      KeyName: !Ref 'KeyPairName'
      ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', AMI]
      InstanceType: t2.micro
      NetworkInterfaces:
      - AssociatePublicIpAddress: true
        DeleteOnTermination: true
        Description: ENI for bastion host
        DeviceIndex: '0'
        SubnetId: !Ref 'PublicSubnet'
        GroupSet:
        - !Ref 'PublicInstanceSG'
      Tags:
      - Key: Name
        Value: !Sub 'Linux bastion ${VPCCIDR}'
      - Key: CloudFormationStack
        Value: !Ref 'AWS::StackId'
Outputs:
  VPCId:
    Description: VPCId of the newly created VPC
    Value: !Ref 'VPC'
  NatGateway:
    Description: NAT gateway instance
    Value: !Ref 'NAT'
  EIPAddress:
    Description: EIP allocated to NAT gateway
    Value: !Ref 'EIP'
  PublicSubnet:
    Description: SubnetId of the public subnet
    Value: !Ref 'PublicSubnet'
  PublicSubnetRouteTable:
    Description: Public route table
    Value: !Ref 'PublicSubnetRouteTable'
  PublicInstanceSG:
    Description: SG for instances in public subnet
    Value: !Ref 'PublicInstanceSG'
  PrivateSubnet:
    Description: SubnetId of the public subnet
    Value: !Ref 'PrivateSubnet'
  PrivateSubnetRouteTable:
    Description: Private route table
    Value: !Ref 'PrivateSubnetRouteTable'
  PrivateSubnetInstanceSG:
    Description: SG for instances in the private subnet
    Value: !Ref 'PrivateSubnetInstanceSG'
  LinuxBastionHost:
    Description: Linux bastion instance
    Value: !Ref 'LinuxBastionHost'

 

 


Posted

in

, ,

by

Comments

Leave a Reply

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