{
  "AWSTemplateFormatVersion": "2010-09-09",

  "Description":
    "Some Example VPC that has two public subnets and two private subnets in different AZs NATs to enable instances in private subnets to access the internet and bastion hosts to moderate SSH access",

  "Parameters": {
    "KeyName": {
      "Description":
        "Name of an existing EC2 KeyPair to enable SSH access to the bastion host",
      "Type": "String",
      "MinLength": "1",
      "MaxLength": "64",
      "AllowedPattern": "[-_ a-zA-Z0-9]*",
      "ConstraintDescription":
        "can contain only alphanumeric characters, spaces, dashes and underscores.",
      "Default": "test-key-pair"
    },

    "BastionInstanceType": {
      "Description": "Bastion instance type",
      "Type": "String",
      "Default": "t2.micro",
      "AllowedValues": ["t2.micro", "m4.small", "m4.medium", "m4.large"],
      "ConstraintDescription": "must be a valid and allowed EC2 instance type."
    },
    "TestName": {
      "Description": "TestName",
      "Type": "String"
    },
    "TestName2":{
      "Description": "TestName2",
      "Type": "String"
    }
  },

  "Mappings": {
    "AWSInstanceType2Arch": {
      "t2.micro": { "Arch": "64" },
      "m4.small": { "Arch": "64" },
      "m4.medium": { "Arch": "64" },
      "m4.large": { "Arch": "64" }
    },

    "AWSNATAMI": {
      "eu-west-1": { "AMI": "ami-785db401" }
    },

    "AWSRegionArch2AMI": {
      "eu-west-1": { "64": "ami-785db401" }
    }
  },

  "Resources": {
    "VPC": {
      "Type": "AWS::EC2::VPC",
      "Properties": {
        "CidrBlock": "10.0.0.0/16",
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Public" }
        ]
      }
    },

    "PublicSubnet1": {
      "Type": "AWS::EC2::Subnet",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "CidrBlock": "10.0.0.0/24",
        "AvailabilityZone": {
          "Fn::Select": ["0", { "Fn::GetAZs": { "Ref": "AWS::Region" } }]
        },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Public" }
        ]
      }
    },

    "PublicSubnet2": {
      "Type": "AWS::EC2::Subnet",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "CidrBlock": "10.0.2.0/24",
        "AvailabilityZone": {
          "Fn::Select": ["1", { "Fn::GetAZs": { "Ref": "AWS::Region" } }]
        },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Public" }
        ]
      }
    },

    "InternetGateway": {
      "Type": "AWS::EC2::InternetGateway",
      "Properties": {
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Public" }
        ]
      }
    },

    "GatewayToInternet": {
      "Type": "AWS::EC2::VPCGatewayAttachment",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "InternetGatewayId": { "Ref": "InternetGateway" }
      }
    },

    "PublicRouteTable": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Public" }
        ]
      }
    },

    "PublicRoute": {
      "Type": "AWS::EC2::Route",
      "DependsOn": "GatewayToInternet",
      "Properties": {
        "RouteTableId": { "Ref": "PublicRouteTable" },
        "DestinationCidrBlock": "0.0.0.0/0",
        "GatewayId": { "Ref": "InternetGateway" }
      }
    },

    "PublicSubnetRouteTableAssociation1": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PublicSubnet1" },
        "RouteTableId": { "Ref": "PublicRouteTable" }
      }
    },

    "PublicSubnetRouteTableAssociation2": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PublicSubnet2" },
        "RouteTableId": { "Ref": "PublicRouteTable" }
      }
    },

    "PublicNetworkAcl": {
      "Type": "AWS::EC2::NetworkAcl",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Public" }
        ]
      }
    },

    "InboundHTTPPublicNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PublicNetworkAcl" },
        "RuleNumber": "100",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "false",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "80", "To": "80" }
      }
    },

    "InboundHTTPSPublicNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PublicNetworkAcl" },
        "RuleNumber": "101",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "false",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "443", "To": "443" }
      }
    },

    "InboundSSHPublicNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PublicNetworkAcl" },
        "RuleNumber": "102",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "false",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "22", "To": "22" }
      }
    },

    "InboundDynamicPortsPublicNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PublicNetworkAcl" },
        "RuleNumber": "103",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "false",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "1024", "To": "65535" }
      }
    },

    "OutboundPublicNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PublicNetworkAcl" },
        "RuleNumber": "100",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "true",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "0", "To": "65535" }
      }
    },

    "PublicSubnetNetworkAclAssociation1": {
      "Type": "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PublicSubnet1" },
        "NetworkAclId": { "Ref": "PublicNetworkAcl" }
      }
    },

    "PublicSubnetNetworkAclAssociation2": {
      "Type": "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PublicSubnet2" },
        "NetworkAclId": { "Ref": "PublicNetworkAcl" }
      }
    },

    "PrivateSubnet1": {
      "Type": "AWS::EC2::Subnet",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "CidrBlock": "10.0.10.0/24",
        "AvailabilityZone": {
          "Fn::Select": ["0", { "Fn::GetAZs": { "Ref": "AWS::Region" } }]
        },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Private" }
        ]
      }
    },

    "PrivateSubnet2": {
      "Type": "AWS::EC2::Subnet",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "CidrBlock": "10.0.3.0/24",
        "AvailabilityZone": {
          "Fn::Select": ["1", { "Fn::GetAZs": { "Ref": "AWS::Region" } }]
        },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Private" }
        ]
      }
    },

    "PrivateRouteTable1": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Private" }
        ]
      }
    },

    "PrivateRouteTable2": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Private" }
        ]
      }
    },

    "PrivateSubnetRouteTableAssociation1": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PrivateSubnet1" },
        "RouteTableId": { "Ref": "PrivateRouteTable1" }
      }
    },

    "PrivateSubnetRouteTableAssociation2": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PrivateSubnet2" },
        "RouteTableId": { "Ref": "PrivateRouteTable2" }
      }
    },

    "PrivateRoute1": {
      "Type": "AWS::EC2::Route",
      "Properties": {
        "RouteTableId": { "Ref": "PrivateRouteTable1" },
        "DestinationCidrBlock": "0.0.0.0/0",
        "InstanceId": { "Ref": "NATDevice1" }
      }
    },

    "PrivateRoute2": {
      "Type": "AWS::EC2::Route",
      "Properties": {
        "RouteTableId": { "Ref": "PrivateRouteTable2" },
        "DestinationCidrBlock": "0.0.0.0/0",
        "InstanceId": { "Ref": "NATDevice2" }
      }
    },

    "PrivateNetworkAcl": {
      "Type": "AWS::EC2::NetworkAcl",
      "Properties": {
        "VpcId": { "Ref": "VPC" },
        "Tags": [
          { "Key": "Application", "Value": { "Ref": "AWS::StackId" } },
          { "Key": "Network", "Value": "Private" }
        ]
      }
    },

    "InboundPrivateNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PrivateNetworkAcl" },
        "RuleNumber": "100",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "false",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "0", "To": "65535" }
      }
    },

    "OutboundPrivateNetworkAclEntry": {
      "Type": "AWS::EC2::NetworkAclEntry",
      "Properties": {
        "NetworkAclId": { "Ref": "PrivateNetworkAcl" },
        "RuleNumber": "100",
        "Protocol": "6",
        "RuleAction": "allow",
        "Egress": "true",
        "CidrBlock": "0.0.0.0/0",
        "PortRange": { "From": "0", "To": "65535" }
      }
    },

    "PrivateSubnetNetworkAclAssociation1": {
      "Type": "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PrivateSubnet1" },
        "NetworkAclId": { "Ref": "PrivateNetworkAcl" }
      }
    },

    "PrivateSubnetNetworkAclAssociation2": {
      "Type": "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties": {
        "SubnetId": { "Ref": "PrivateSubnet2" },
        "NetworkAclId": { "Ref": "PrivateNetworkAcl" }
      }
    },

    "NATIPAddress1": {
      "Type": "AWS::EC2::EIP",
      "DependsOn": "GatewayToInternet",
      "Properties": {
        "Domain": "vpc",
        "InstanceId": { "Ref": "NATDevice1" }
      }
    },

    "NATIPAddress2": {
      "Type": "AWS::EC2::EIP",
      "DependsOn": "GatewayToInternet",
      "Properties": {
        "Domain": "vpc",
        "InstanceId": { "Ref": "NATDevice2" }
      }
    },

    "NATDevice1": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "InstanceType": "t1.micro",
        "KeyName": { "Ref": "KeyName" },
        "SubnetId": { "Ref": "PublicSubnet1" },
        "SourceDestCheck": "false",
        "ImageId": {
          "Fn::FindInMap": ["AWSNATAMI", { "Ref": "AWS::Region" }, "AMI"]
        },
        "SecurityGroupIds": [{ "Ref": "NATSecurityGroup" }],
        "Tags": [{ "Key": "Name", "Value": "NAT Device" }]
      }
    },

    "NATDevice2": {
      "Type": "AWS::EC2::Instance",
      "Properties": {
        "InstanceType": "t1.micro",
        "KeyName": { "Ref": "KeyName" },
        "SubnetId": { "Ref": "PublicSubnet2" },
        "SourceDestCheck": "false",
        "ImageId": {
          "Fn::FindInMap": ["AWSNATAMI", { "Ref": "AWS::Region" }, "AMI"]
        },
        "SecurityGroupIds": [{ "Ref": "NATSecurityGroup" }],
        "Tags": [{ "Key": "Name", "Value": "NAT Device" }]
      }
    },

    "NATSecurityGroup": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "Enable internal access to the NAT device",
        "VpcId": { "Ref": "VPC" },
        "SecurityGroupIngress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "80",
            "ToPort": "80",
            "CidrIp": "0.0.0.0/0"
          },
          {
            "IpProtocol": "tcp",
            "FromPort": "443",
            "ToPort": "443",
            "CidrIp": "0.0.0.0/0"
          },
          {
            "IpProtocol": "tcp",
            "FromPort": "22",
            "ToPort": "22",
            "SourceSecurityGroupId": { "Ref": "BastionSecurityGroup" }
          },
          {
            "IpProtocol": "tcp",
            "FromPort": "1",
            "ToPort": "65535",
            "SourceSecurityGroupId": { "Ref": "InternalSshSecurityGroup" }
          }
        ],
        "SecurityGroupEgress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "1",
            "ToPort": "65535",
            "CidrIp": "0.0.0.0/0"
          }
        ]
      }
    },

    "BastionHost1": {
      "Type": "AWS::EC2::Instance",
      "DependsOn": "GatewayToInternet",
      "Properties": {
        "InstanceType": { "Ref": "BastionInstanceType" },
        "KeyName": { "Ref": "KeyName" },
        "ImageId": {
          "Fn::FindInMap": [
            "AWSRegionArch2AMI",
            { "Ref": "AWS::Region" },
            {
              "Fn::FindInMap": [
                "AWSInstanceType2Arch",
                { "Ref": "BastionInstanceType" },
                "Arch"
              ]
            }
          ]
        },
        "NetworkInterfaces": [
          {
            "GroupSet": [{ "Ref": "BastionSecurityGroup" }],
            "AssociatePublicIpAddress": "true",
            "DeviceIndex": "0",
            "DeleteOnTermination": "true",
            "SubnetId": { "Ref": "PublicSubnet1" }
          }
        ],
        "Tags": [
          { "Key": "Name", "Value": "Bastion Host" },
          { "Key": "Bastion", "Value": "true" }
        ]
      }
    },

    "BastionHost2": {
      "Type": "AWS::EC2::Instance",
      "DependsOn": "GatewayToInternet",
      "Properties": {
        "InstanceType": { "Ref": "BastionInstanceType" },
        "KeyName": { "Ref": "KeyName" },
        "ImageId": {
          "Fn::FindInMap": [
            "AWSRegionArch2AMI",
            { "Ref": "AWS::Region" },
            {
              "Fn::FindInMap": [
                "AWSInstanceType2Arch",
                { "Ref": "BastionInstanceType" },
                "Arch"
              ]
            }
          ]
        },
        "NetworkInterfaces": [
          {
            "GroupSet": [{ "Ref": "BastionSecurityGroup" }],
            "AssociatePublicIpAddress": "true",
            "DeviceIndex": "0",
            "DeleteOnTermination": "true",
            "SubnetId": { "Ref": "PublicSubnet2" }
          }
        ],
        "Tags": [
          { "Key": "Name", "Value": "Bastion Host" },
          { "Key": "Bastion", "Value": "true" }
        ]
      }
    },

    "BastionSecurityGroup": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "Enable access to the bastion host",
        "VpcId": { "Ref": "VPC" },
        "SecurityGroupIngress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "22",
            "ToPort": "22",
            "CidrIp": "0.0.0.0/0"
          }
        ]
      }
    },

    "InternalSshSecurityGroup": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "Allow ssh access from bastion",
        "VpcId": { "Ref": "VPC" },
        "SecurityGroupIngress": [
          {
            "IpProtocol": "tcp",
            "FromPort": "22",
            "ToPort": "22",
            "SourceSecurityGroupId": { "Ref": "BastionSecurityGroup" }
          }
        ],
        "SecurityGroupEgress": []
      }
    }
  },

  "Outputs": {
    "VpcId": {
      "Description": "VPC",
      "Value": { "Ref": "VPC" }
    },
    "PublicSubnets": {
      "Description": "Public subnet",
      "Value": {
        "Fn::Join": [
          ",",
          [{ "Ref": "PublicSubnet1" }, { "Ref": "PublicSubnet2" }]
        ]
      }
    },
    "PrivateSubnets": {
      "Description": "Private subnet",
      "Value": {
        "Fn::Join": [
          ",",
          [{ "Ref": "PrivateSubnet1" }, { "Ref": "PrivateSubnet2" }]
        ]
      }
    },
    "AZs": {
      "Description": "Availability zones",
      "Value": {
        "Fn::Join": [
          ",",
          [
            { "Fn::GetAtt": ["PrivateSubnet1", "AvailabilityZone"] },
            { "Fn::GetAtt": ["PrivateSubnet2", "AvailabilityZone"] }
          ]
        ]
      }
    },
    "InternalSshSecurityGroupId": {
      "Description": "Internal ssh security group",
      "Value": { "Ref": "InternalSshSecurityGroup" }
    }
  }
}
