AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  This template creates a Multi-AZ, multi-subnet VPC infrastructure with managed NAT
  gateways in the public subnet for each Availability Zone. You can also create additional
  private subnets with dedicated custom network access control lists (ACLs). If you
  deploy the Quick Start in a region that doesn't support NAT gateways, NAT instances
  are deployed instead. **WARNING** This template creates AWS resources. You will
  be billed for the AWS resources used if you create a stack from this template. (qs-1qnnspaap)
Parameters:
  AvailabilityZone1:
    Description: Availability Zone 1 to use for the subnets in the VPC. Two
      Availability Zones are used for this deployment.
    Type: AWS::EC2::AvailabilityZone::Name
  AvailabilityZone2:
    Description: Availability Zone 2 to use for the subnets in the VPC. Two
      Availability Zones are used for this deployment.
    Type: AWS::EC2::AvailabilityZone::Name
  CreateNATGateways:
    AllowedValues:
      - 'true'
      - 'false'
    Default: 'true'
    Description: Set to false when creating only private subnets. If True, both CreatePublicSubnets and CreatePrivateSubnets must also be true.
    Type: String
  CreatePublicSubnets:
    AllowedValues:
      - 'true'
      - 'false'
    Default: 'true'
    Description: Set to false to create only private subnets. If false, CreatePrivateSubnets must be True and the CIDR parameters for ALL public subnets will be ignored
    Type: String
  CreatePrivateSubnets:
    AllowedValues:
      - 'true'
      - 'false'
    Default: 'true'
    Description: Set to false to create only public subnets. If false, the CIDR parameters
      for ALL private subnets will be ignored.
    Type: String
  
  
  PrivateSubnet1ACIDR:
    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.0.0.0/19
    Description: CIDR block for private subnet 1A located in Availability Zone 1
    Type: String
  
  PrivateSubnet2ACIDR:
    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.0.32.0/19
    Description: CIDR block for private subnet 2A located in Availability Zone 2
    Type: String

  
  PrivateSubnetATag1:
    AllowedPattern: ^([a-zA-Z0-9+\-._:/@]+=[a-zA-Z0-9+\-.,_:/@ *\\"'\[\]\{\}]*)?$
    ConstraintDescription: tags must be in format "Key=Value" keys can only contain
      [a-zA-Z0-9+\-._:/@], values can contain [a-zA-Z0-9+\-._:/@ *\\"'\[\]\{\}]
    Default: Network=Private
    Description: tag to add to private subnets A, in format Key=Value (Optional)
    Type: String
  PrivateSubnetATag2:
    AllowedPattern: ^([a-zA-Z0-9+\-._:/@]+=[a-zA-Z0-9+\-.,_:/@ *\\"'\[\]\{\}]*)?$
    ConstraintDescription: tags must be in format "Key=Value" keys can only contain
      [a-zA-Z0-9+\-._:/@], values can contain [a-zA-Z0-9+\-._:/@ *\\"'\[\]\{\}]
    Default: ''
    Description: tag to add to private subnets A, in format Key=Value (Optional)
    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])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.128.0/20
    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])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.144.0/20
    Description: CIDR block for the public DMZ subnet 2 located in Availability Zone
      2
    Type: String
  
  PublicSubnetTag1:
    AllowedPattern: ^([a-zA-Z0-9+\-._:/@]+=[a-zA-Z0-9+\-.,_:/@ *\\"'\[\]\{\}]*)?$
    ConstraintDescription: tags must be in format "Key=Value" keys can only contain
      [a-zA-Z0-9+\-._:/@], values can contain [a-zA-Z0-9+\-._:/@ *\\"'\[\]\{\}]
    Default: Network=Public
    Description: tag to add to public subnets, in format Key=Value (Optional)
    Type: String
  PublicSubnetTag2:
    AllowedPattern: ^([a-zA-Z0-9+\-._:/@]+=[a-zA-Z0-9+\-.,_:/@ *\\"'\[\]\{\}]*)?$
    ConstraintDescription: tags must be in format "Key=Value" keys can only contain
      [a-zA-Z0-9+\-._:/@], values can contain [a-zA-Z0-9+\-._:/@ *\\"'\[\]\{\}]
    Default: ''
    Description: tag to add to public subnets, in format Key=Value (Optional)
    Type: String
  
  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.0.0.0/16
    Description: CIDR block for the VPC
    Type: String
  VPCTenancy:
    AllowedValues:
      - default
      - dedicated
    Default: default
    Description: The allowed tenancy of instances launched into the VPC
    Type: String
Rules:
  NAT:
    RuleCondition: !Equals [!Ref CreateNATGateways, 'true']
    Assertions:
      - Assert: !And
          - !Equals [!Ref CreatePrivateSubnets, 'true']
          - !Equals [!Ref CreatePublicSubnets, 'true']
        AssertDescription: To enable NAT gateways you must have both CreatePrivateSubnets and CreatePublicSubnets set to 'true'
  Subnets:
    Assertions:
      - Assert: !Or
          - !Equals [!Ref CreatePrivateSubnets, 'true']
          - !Equals [!Ref CreatePublicSubnets, 'true']
        AssertDescription: At least one of CreatePublicSubnets or CreatePrivateSubnets must be set to 'true'
Conditions:
  
  PrivateSubnetNatRoute: !And
    - !Condition PrivateSubnetsCondition
    - !Condition NATGatewaysCondition

  NATGatewaysCondition: !Equals
    - !Ref 'CreateNATGateways'
    - 'true'
  NVirginiaRegionCondition: !Equals
    - !Ref 'AWS::Region'
    - us-east-1
  PrivateSubnetsCondition: !Equals
    - !Ref 'CreatePrivateSubnets'
    - 'true'
  
  PublicSubnetsCondition: !Equals
    - !Ref 'CreatePublicSubnets'
    - 'true'
  
  PrivateSubnetATag1Condition: !Not
    - !Equals
      - !Ref 'PrivateSubnetATag1'
      - ''
  PrivateSubnetATag2Condition: !Not
    - !Equals
      - !Ref 'PrivateSubnetATag2'
      - ''
  

  
  PublicSubnetTag1Condition: !Not
    - !Equals
      - !Ref 'PublicSubnetTag1'
      - ''
  PublicSubnetTag2Condition: !Not
    - !Equals
      - !Ref 'PublicSubnetTag2'
      - ''
  
Resources:
  DHCPOptions:
    Type: AWS::EC2::DHCPOptions
    Properties:
      DomainName: !If
        - NVirginiaRegionCondition
        - ec2.internal
        - !Sub '${AWS::Region}.compute.internal'
      DomainNameServers:
        - AmazonProvidedDNS
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref 'VPCCIDR'
      InstanceTenancy: !Ref 'VPCTenancy'
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref 'AWS::StackName'
  VPCDHCPOptionsAssociation:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    Properties:
      VpcId: !Ref 'VPC'
      DhcpOptionsId: !Ref 'DHCPOptions'
  InternetGateway:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref 'AWS::StackName'
  VPCGatewayAttachment:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref 'VPC'
      InternetGatewayId: !Ref 'InternetGateway'
  PrivateSubnet1A:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PrivateSubnet1ACIDR'
      AvailabilityZone: !Ref 'AvailabilityZone1'
      Tags:
        - Key: Name
          Value: Private subnet 1A
        - !If
          - PrivateSubnetATag1Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag1'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag1'
          - !Ref 'AWS::NoValue'
        - !If
          - PrivateSubnetATag2Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag2'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag2'
          - !Ref 'AWS::NoValue'
        
  PrivateSubnet2A:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PrivateSubnet2ACIDR'
      AvailabilityZone: !Ref 'AvailabilityZone2'
      Tags:
        - Key: Name
          Value: Private subnet 2A
        - !If
          - PrivateSubnetATag1Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag1'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag1'
          - !Ref 'AWS::NoValue'
        - !If
          - PrivateSubnetATag2Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag2'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PrivateSubnetATag2'
          - !Ref 'AWS::NoValue'
        
  PublicSubnet1:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PublicSubnet1CIDR'
      AvailabilityZone: !Ref 'AvailabilityZone1'
      Tags:
        - Key: Name
          Value: Public subnet 1
        - !If
          - PublicSubnetTag1Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag1'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag1'
          - !Ref 'AWS::NoValue'
        - !If
          - PublicSubnetTag2Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag2'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag2'
          - !Ref 'AWS::NoValue'
      MapPublicIpOnLaunch: true
  PublicSubnet2:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: !Ref 'PublicSubnet2CIDR'
      AvailabilityZone: !Ref 'AvailabilityZone2'
      Tags:
        - Key: Name
          Value: Public subnet 2
        - !If
          - PublicSubnetTag1Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag1'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag1'
          - !Ref 'AWS::NoValue'
        - !If
          - PublicSubnetTag2Condition
          - Key: !Select
              - '0'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag2'
            Value: !Select
              - '1'
              - !Split
                - '='
                - !Ref 'PublicSubnetTag2'
          - !Ref 'AWS::NoValue'
      MapPublicIpOnLaunch: true
  PrivateSubnet1ARouteTable:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: Private subnet 1A
        - Key: Network
          Value: Private
  PrivateSubnet1ARoute:
    Condition: PrivateSubnetNatRoute
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref 'PrivateSubnet1ARouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NATGateway1'
  PrivateSubnet1ARouteTableAssociation:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet1A'
      RouteTableId: !Ref 'PrivateSubnet1ARouteTable'
  PrivateSubnet2ARouteTable:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: Private subnet 2A
        - Key: Network
          Value: Private
  PrivateSubnet2ARoute:
    Condition: PrivateSubnetNatRoute
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref 'PrivateSubnet2ARouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref 'NATGateway2'
  PrivateSubnet2ARouteTableAssociation:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PrivateSubnet2A'
      RouteTableId: !Ref 'PrivateSubnet2ARouteTable'
  
  
 
  
  PublicSubnetRouteTable:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
      Tags:
        - Key: Name
          Value: Public Subnets
        - Key: Network
          Value: Public
  PublicSubnetRoute:
    Condition: PublicSubnetsCondition
    DependsOn: VPCGatewayAttachment
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref 'PublicSubnetRouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref 'InternetGateway'
  PublicSubnet1RouteTableAssociation:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet1'
      RouteTableId: !Ref 'PublicSubnetRouteTable'
  PublicSubnet2RouteTableAssociation:
    Condition: PublicSubnetsCondition
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref 'PublicSubnet2'
      RouteTableId: !Ref 'PublicSubnetRouteTable'
  NAT1EIP:
    Condition: NATGatewaysCondition
    DependsOn: VPCGatewayAttachment
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  NAT2EIP:
    Condition: NATGatewaysCondition
    DependsOn: VPCGatewayAttachment
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  NATGateway1:
    Condition: NATGatewaysCondition
    DependsOn: VPCGatewayAttachment
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt 'NAT1EIP.AllocationId'
      SubnetId: !Ref 'PublicSubnet1'
  NATGateway2:
    Condition: NATGatewaysCondition
    DependsOn: VPCGatewayAttachment
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt 'NAT2EIP.AllocationId'
      SubnetId: !Ref 'PublicSubnet2'
  S3VPCEndpoint:
    Condition: PrivateSubnetsCondition
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action: '*'
            Effect: Allow
            Resource: '*'
            Principal: '*'
      RouteTableIds:
        - !Ref 'PrivateSubnet1ARouteTable'
        - !Ref 'PrivateSubnet2ARouteTable'
      ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
      VpcId: !Ref 'VPC'
Outputs:
  NAT1EIP:
    Condition: NATGatewaysCondition
    Description: NAT 1 IP address
    Value: !Ref 'NAT1EIP'
  NAT2EIP:
    Condition: NATGatewaysCondition
    Description: NAT 2 IP address
    Value: !Ref 'NAT2EIP'
  PrivateSubnet1ACIDR:
    Condition: PrivateSubnetsCondition
    Description: Private subnet 1A CIDR in Availability Zone 1
    Value: !Ref 'PrivateSubnet1ACIDR'
  PrivateSubnet1AID:
    Condition: PrivateSubnetsCondition
    Description: Private subnet 1A ID in Availability Zone 1
    Value: !Ref 'PrivateSubnet1A'
 
  PrivateSubnet2ACIDR:
    Condition: PrivateSubnetsCondition
    Description: Private subnet 2A CIDR in Availability Zone 2
    Value: !Ref 'PrivateSubnet2ACIDR'
  PrivateSubnet2AID:
    Condition: PrivateSubnetsCondition
    Description: Private subnet 2A ID in Availability Zone 2
    Value: !Ref 'PrivateSubnet2A'
  
  PublicSubnet1CIDR:
    Condition: PublicSubnetsCondition
    Description: Public subnet 1 CIDR in Availability Zone 1
    Value: !Ref 'PublicSubnet1CIDR'
  PublicSubnet1ID:
    Condition: PublicSubnetsCondition
    Description: Public subnet 1 ID in Availability Zone 1
    Value: !Ref 'PublicSubnet1'
  PublicSubnet2CIDR:
    Condition: PublicSubnetsCondition
    Description: Public subnet 2 CIDR in Availability Zone 2
    Value: !Ref 'PublicSubnet2CIDR'
  PublicSubnet2ID:
    Condition: PublicSubnetsCondition
    Description: Public subnet 2 ID in Availability Zone 2
    Value: !Ref 'PublicSubnet2'
  
  S3VPCEndpoint:
    Condition: PrivateSubnetsCondition
    Description: S3 VPC Endpoint
    Value: !Ref 'S3VPCEndpoint'
  PrivateSubnet1ARouteTable:
    Condition: PrivateSubnetsCondition
    Value: !Ref 'PrivateSubnet1ARouteTable'
    Description: Private subnet 1A route table
  
  PrivateSubnet2ARouteTable:
    Condition: PrivateSubnetsCondition
    Value: !Ref 'PrivateSubnet2ARouteTable'
    Description: Private subnet 2A route table
  
  
  PublicSubnetRouteTable:
    Condition: PublicSubnetsCondition
    Value: !Ref 'PublicSubnetRouteTable'
    Description: Public subnet route table
  VPCCIDR:
    Value: !Ref 'VPCCIDR'
    Description: VPC CIDR
  VPCID:
    Value: !Ref 'VPC'
    Description: VPC ID
