AWSTemplateFormatVersion: 2010-09-09 Description: >- Template for Virtual Immersion Week Compute Laboratory # # # ############################### # Parameters section ############################### Parameters: VPCName: Description: The name of the VPC being created. Type: String Default: "ImmersionWeekLabVPC" ############################### # Mappings section ############################### Mappings: SubnetConfig: VPC: CIDR: "10.0.0.0/16" PublicSubnet1A: CIDR: "10.0.0.0/24" PublicSubnet1B: CIDR: "10.0.1.0/24" PrivateSubnet1A: CIDR: "10.0.10.0/23" PrivateSubnet1B: CIDR: "10.0.12.0/23" AZRegions: ap-east-1: AZs: ["a", "b", "c"] ap-northeast-1: AZs: ["a", "c", "d"] ap-northeast-2: AZs: ["a", "b", "c"] ap-northeast-3: AZs: ["a"] ap-south-1: AZs: ["a", "b", "c"] ap-southeast-1: AZs: ["a", "b", "c"] ap-southeast-2: AZs: ["a", "b", "c"] ca-central-1: AZs: ["a", "b"] eu-central-1: AZs: ["a", "b", "c"] eu-north-1: AZs: ["a", "b", "c"] eu-south-1: AZs: ["a", "b", "c"] eu-west-1: AZs: ["a", "b", "c"] eu-west-2: AZs: ["a", "b", "c"] eu-west-3: AZs: ["a", "b", "c"] me-south-1: AZs: ["a", "b", "c"] sa-east-1: AZs: ["a", "b", "c"] us-east-1: AZs: ["a", "b", "c", "d", "e", "f"] us-east-2: AZs: ["a", "b", "c"] us-west-1: AZs: ["b", "c"] us-west-2: AZs: ["a", "b", "c", "d"] ############################### # Resources section ############################### Resources: ## VPC VPC: Type: AWS::EC2::VPC Properties: EnableDnsSupport: true EnableDnsHostnames: true CidrBlock: Fn::FindInMap: - "SubnetConfig" - "VPC" - "CIDR" Tags: - Key: "Name" Value: !Ref 'VPCName' ## Internet Gateway InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - !Sub '-IGW' VPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway ## Public subnet 1A PublicSubnet1A: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "PublicSubnet1A" - "CIDR" MapPublicIpOnLaunch: "true" Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-public-' - !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] ## Public subnet 1B PublicSubnet1B: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "PublicSubnet1B" - "CIDR" MapPublicIpOnLaunch: "true" Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-public-' - !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] ## Private subnet 1A PrivateSubnet1A: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "PrivateSubnet1A" - "CIDR" Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-private-' - !Select [ 0, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] ## Private subnet 1B PrivateSubnet1B: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: Fn::Sub: - "${AWS::Region}${AZ}" - AZ: !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] CidrBlock: Fn::FindInMap: - "SubnetConfig" - "PrivateSubnet1B" - "CIDR" Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-private-' - !Select [ 1, !FindInMap [ "AZRegions", !Ref "AWS::Region", "AZs" ] ] ## Public route tables PublicRouteTable1A: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PublicRoute1A: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable1A DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicRouteTable1B: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PublicRoute1B: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable1B DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway ## Subnet associations for public subnets. PublicSubnetRouteTableAssociation1A: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1A RouteTableId: !Ref PublicRouteTable1A PublicSubnetRouteTableAssociation1B: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1B RouteTableId: !Ref PublicRouteTable1B PublicNetworkAcl: Type: "AWS::EC2::NetworkAcl" Properties: VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-public-nacl' # Inbound HTTP traffic NACL entry. InboundHttpPublicNetworkAclEntry: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: !Ref "PublicNetworkAcl" RuleNumber: "100" Protocol: "-1" RuleAction: "allow" Egress: "false" CidrBlock: "0.0.0.0/0" PortRange: From: "0" To: "65535" # Outbound traffic NACL entry. OutboundPublicNetworkAclEntry: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: !Ref "PublicNetworkAcl" RuleNumber: "100" Protocol: "-1" RuleAction: "allow" Egress: "true" CidrBlock: "0.0.0.0/0" PortRange: From: "0" To: "65535" # Association of the public network ACL to public subnets. PublicSubnetNetworkAclAssociation1A: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: !Ref PublicSubnet1A NetworkAclId: !Ref PublicNetworkAcl PublicSubnetNetworkAclAssociation1B: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: !Ref PublicSubnet1B NetworkAclId: !Ref PublicNetworkAcl ## NAT Gateway setup. # EIPs for NAT Gateways. NatGatewayEip1A: Type: "AWS::EC2::EIP" Properties: Domain: "vpc" NatGatewayEip1B: Type: "AWS::EC2::EIP" Properties: Domain: "vpc" # NAT Gateways (one for each public subnet). NatGateway1A: Type: "AWS::EC2::NatGateway" Properties: AllocationId: Fn::GetAtt: - "NatGatewayEip1A" - "AllocationId" SubnetId: !Ref "PublicSubnet1A" NatGateway1B: Type: "AWS::EC2::NatGateway" Properties: AllocationId: Fn::GetAtt: - "NatGatewayEip1B" - "AllocationId" SubnetId: !Ref "PublicSubnet1B" ## Private route tables PrivateRouteTable1A: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref "VPC" Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-Private-Route-Table-1' PrivateRouteToInternet1A: Type: "AWS::EC2::Route" Properties: RouteTableId: !Ref PrivateRouteTable1A DestinationCidrBlock: "0.0.0.0/0" NatGatewayId: !Ref NatGateway1A PrivateRouteTable1B: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref "VPC" Tags: - Key: "Name" Value: !Join - '' - - !Ref "VPCName" - '-Private-Route-Table-2' PrivateRouteToInternet1B: Type: "AWS::EC2::Route" Properties: RouteTableId: !Ref PrivateRouteTable1B DestinationCidrBlock: "0.0.0.0/0" NatGatewayId: !Ref NatGateway1B PrivateSubnetRouteTableAssociation1A: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnet1A RouteTableId: !Ref PrivateRouteTable1A PrivateSubnetRouteTableAssociation1B: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref PrivateSubnet1B RouteTableId: !Ref PrivateRouteTable1B ############################### # Resources - Security groups ############################### BastionSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupName: "ImmersionWeekBastionSG" GroupDescription: "Allows SSH (TCP port 22) traffic in and out of a bastion host sitting in a public subnet." VpcId: !Ref VPC SecurityGroupIngress: IpProtocol: "tcp" FromPort: 22 ToPort: 22 CidrIp: "0.0.0.0/0" SecurityGroupEgress: IpProtocol: "tcp" FromPort: 22 ToPort: 22 CidrIp: "10.0.0.0/16" LoadBalancerSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupName: "ImmersionWeekLoadBalancerSG" GroupDescription: "Allows inbound traffic to load balancers on TCP port 80 (HTTP)." VpcId: !Ref VPC SecurityGroupIngress: IpProtocol: "tcp" FromPort: 80 ToPort: 80 CidrIp: "0.0.0.0/0" AppInstancesSecurityGroup: Type: "AWS::EC2::SecurityGroup" DependsOn: ["BastionSecurityGroup", "LoadBalancerSecurityGroup"] Properties: GroupName: "ImmersionWeekAppInstancesSG" GroupDescription: "Allows inbound traffic on TCP ports 22 (SSH) from bastion hosts, and 80 (HTTP) from load balancers." VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: "tcp" FromPort: 22 ToPort: 22 SourceSecurityGroupId: !Ref BastionSecurityGroup - IpProtocol: "tcp" FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup ############################### # Outputs section ############################### Outputs: VPC: Description: VPC ID Value: !Ref VPC Export: Name: !Sub '${AWS::StackName}-VPCID' PublicSubnet1A: Description: The subnet ID to use for public web servers in AZ 1A. Value: !Ref PublicSubnet1A Export: Name: !Sub '${AWS::StackName}-PublicSubnet1A' PublicSubnet1B: Description: The subnet ID to use for public web servers in AZ 1B. Value: !Ref PublicSubnet1B Export: Name: !Sub '${AWS::StackName}-PublicSubnet1B' PrivateSubnet1A: Description: The subnet ID to use for private instances in AZ 1A. Value: !Ref PrivateSubnet1A Export: Name: !Sub '${AWS::StackName}-PrivateSubnet1A' PrivateSubnet1B: Description: The subnet ID to use for private instances in AZ 1B. Value: !Ref PrivateSubnet1B Export: Name: !Sub '${AWS::StackName}-PrivateSubnet1B' DefaultSecurityGroup: Description: "Default Security Group ID" Value: !GetAtt VPC.DefaultSecurityGroup Export: Name: !Sub "${AWS::StackName}-DefaultSecurityGroup" AvailabilityZone1: Description: "Availability Zone 1A name" Value: !GetAtt [ "PrivateSubnet1A", "AvailabilityZone" ] Export: Name: !Sub "${AWS::StackName}-PrivateSubnet1A-AZ" AvailabilityZone2: Description: "Availability Zone 1B name" Value: !GetAtt [ "PrivateSubnet1B", "AvailabilityZone" ] Export: Name: !Sub "${AWS::StackName}-PrivateSubnet1B-AZ"