AWS Cloud Formation (example 8)

  • VPC
  • Internet Gateway
  • NAT Gateway
  • Public Subnet x2
  • Private Subnet x2
  • Security Group x3
    • for Load Balancer (TCP 80)
    • for EC2 Bastion (TCP 22)
    • for EC2 Web Instances (TCP 80, TCP 22)
  • EC2 – Bastion
    • in PublicASubnet
  • Load Balancer
    • for EC2 – Web Instances
  • LaunchConfiguration
  • AutoScalingGroup
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  BastionKeyName:
    Description: The EC2 Key Pair to allow SSH access to the bastion
    Type: 'AWS::EC2::KeyPair::KeyName'
    Default: bastion
  InstanceKeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: 'AWS::EC2::KeyPair::KeyName'
    Default: instance

Resources:

# VPC -----------------------------------------------------

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.1.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value:  !Join ['', [!Ref "AWS::StackName", "-vpc" ]]

# InternetGateway -----------------------------------------------------

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    DependsOn: VPC
    Properties:
      Tags:
      - Key: Name
        Value:  !Join ['', [!Ref "AWS::StackName", "-ig" ]]

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


# NATGateway -------------------------------------------------------

  ElasticIPAddress:
    Type: AWS::EC2::EIP
    Properties:
      Domain: VPC

  NATGateway:
    Type: AWS::EC2::NatGateway
    DependsOn: PublicASubnet
    Properties:
      AllocationId: !GetAtt ElasticIPAddress.AllocationId
      SubnetId: !Ref PublicASubnet
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-nat

# Subnet --------------------------------------------------------------

  PublicASubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.10.0/24
      AvailabilityZone: !Select [ 0, !GetAZs ]
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-a

  PublicBSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.20.0/24
      AvailabilityZone: !Select [ 1, !GetAZs ]
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-b

  PrivateASubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.50.0/24
      AvailabilityZone: !Select [ 0, !GetAZs ]
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-private-a

  PrivateBSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.60.0/24
      AvailabilityZone: !Select [ 1, !GetAZs ]
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-private-b

# PublicARouteTable -------------------------------------------------------

  PublicARouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-a
  PublicARoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicARouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicASubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicASubnet
      RouteTableId: !Ref PublicARouteTable

# PublicBRouteTable -------------------------------------------------------

  PublicBRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-public-b
  PublicBRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicBRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicBSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicBSubnet
      RouteTableId: !Ref PublicBRouteTable

# PrivateARouteTable -------------------------------------------------------

  PrivateARouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-private-a
  PrivateARoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateARouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway
  PrivateASubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateASubnet
      RouteTableId: !Ref PrivateARouteTable

# PrivateBRouteTable -------------------------------------------------------

  PrivateBRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-private-b
  PrivateBRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateBRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway
  PrivateASubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateBSubnet
      RouteTableId: !Ref PrivateBRouteTable

# SecurityGroup --------------------------------------------------------------------

  LBSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPC
      GroupDescription: LB access
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-lb
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0

  BastionSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Web access
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-bastion
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp: 0.0.0.0/0

  InstanceSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPC
      GroupDescription: Instance access
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-instance
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        "SourceSecurityGroupId": {
          "Fn::GetAtt": [
            "BastionSecurityGroup",
            "GroupId"
          ]
        }

# EC2 --------------------------------------------------------------------

  ElasticIPAddressWeb1:
    Type: AWS::EC2::EIP
    Properties:
      Domain: VPC
      InstanceId: !Ref BastionE2cInstance

  BastionE2cInstance:
    Type: 'AWS::EC2::Instance'
    Properties:
      KeyName: !Ref BastionKeyName
      ImageId: 'ami-0b898040803850657' # Amazon Linux 2 AMI (HVM), SSD Volume Type
      InstanceType: 't2.micro'
      NetworkInterfaces:
      - AssociatePublicIpAddress: False
        DeviceIndex: "0"
        GroupSet:
        - !Ref BastionSecurityGroup
        SubnetId:
          !Ref PublicASubnet
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-bastion

# LaunchConfiguration ------------------------------------------------

  WebLaunchConfiguration:
    Type: 'AWS::AutoScaling::LaunchConfiguration'
    Properties:
      LaunchConfigurationName: !Sub ${AWS::StackName}-launch-configuration
      KeyName: !Ref InstanceKeyName
      ImageId: 'ami-0b898040803850657' # Amazon Linux 2 AMI (HVM), SSD Volume Type
      InstanceType: 't2.micro'
      SecurityGroups:
      - !Ref InstanceSecurityGroup
      UserData:
        Fn::Base64: | # hard to say why this shit doesn't work
          #!/bin/bash -xe
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          echo "Hello from $(hostname -f)" > /var/www/html/index.html

  WebAutoScalingGroup:
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      AutoScalingGroupName: !Sub ${AWS::StackName}-auto-scaling-group
      LaunchConfigurationName: !Ref WebLaunchConfiguration
      MinSize: 3
      MaxSize: 5
      HealthCheckType: 'ELB'
      HealthCheckGracePeriod: '600'
      TargetGroupARNs:
      - !Ref DefaultTargetGroup
      VPCZoneIdentifier:
      - !Ref PrivateASubnet
      - !Ref PrivateBSubnet

# LoadBalancer --------------------------------------------------------------------

  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Subnets:
      - !Ref PublicASubnet
      - !Ref PublicBSubnet
      SecurityGroups:
      - !Ref LBSecurityGroup
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-lb

  LoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref DefaultTargetGroup

  DefaultTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-dtg
      VpcId: !Ref VPC
      Port: 80
      Protocol: HTTP