Description: Central Egress VPC holding on-premise connectivity, vpc endpoints and internet egress

Parameters:
  Identifier:
    Type: String
  PrivateSubnetCidr:
    Type: String
  PublicSubnetCidr:
    Type: String
  PrivateSubnetPrefixLength:
    Type: String
  PublicSubnetPrefixLength:
    Type: String

Mappings:
  Cidr:
    PrefixLength:
      "16": 16
      "17": 15
      "18": 14
      "19": 13
      "20": 12
      "21": 11
      "22": 10
      "23": 9
      "24": 8
      "25": 7
      "26": 6
      "27": 5
      "28": 4
Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref PrivateSubnetCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: !Ref Identifier
  PublicVpcCidr:
    Type: AWS::EC2::VPCCidrBlock
    Properties: 
      CidrBlock: !Ref PublicSubnetCidr
      VpcId: !Ref Vpc
  DnsVpcDhcpOptions:
    Type: AWS::EC2::DHCPOptions
    Properties:
      DomainName: !Sub 'egress.${AWS::Region}.{{resolve:ssm:/org/network/domain}}'
      DomainNameServers:
      - AmazonProvidedDNS
      Tags:
      - Key: Name
        Value: !Ref Identifier
  DnsVpcDhcpOptionsAssociation:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    Properties:
      DhcpOptionsId: !Ref DnsVpcDhcpOptions
      VpcId: !Ref Vpc
  TransitGatewayAttachment:
    Type: AWS::EC2::TransitGatewayAttachment
    Properties:
      SubnetIds:
      - !Ref PrivateSubnetA
      - !Ref PrivateSubnetB
      - !Ref PrivateSubnetC
      TransitGatewayId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/tgw-id}}'
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: egress
      - Key: Name
        Value: !Ref Identifier
      - Key: Account
        Value: !Ref AWS::AccountId
  TgwRouteTableAssociation:
    Type: AWS::EC2::TransitGatewayRouteTableAssociation
    Properties:
      TransitGatewayAttachmentId: !Ref TransitGatewayAttachment
      TransitGatewayRouteTableId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/route-table/hub}}'
  RouteDevToEgressVpc:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayAttachmentId: !Ref TransitGatewayAttachment
      TransitGatewayRouteTableId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/route-table/hub}}'
  PrivateRouteTableA:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: private
      - Key: Name
        Value: !Sub ${Identifier}-private-a
  PrivateRouteTableB:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: private
      - Key: Name
        Value: !Sub ${Identifier}-private-b
  PrivateRouteTableC:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: private
      - Key: Name
        Value: !Sub ${Identifier}-private-c
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Select [0, !Cidr [!Ref PrivateSubnetCidr, 4, !FindInMap [Cidr, PrefixLength, !Ref PrivateSubnetPrefixLength]]]
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: private
      - Key: Name
        Value: !Sub ${Identifier}-private-a
  PrivateSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableA
      SubnetId: !Ref PrivateSubnetA
  PrivateSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, !GetAZs ""]
      CidrBlock: !Select [1, !Cidr [!Ref PrivateSubnetCidr, 4, !FindInMap [Cidr, PrefixLength, !Ref PrivateSubnetPrefixLength]]]
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: private
      - Key: Name
        Value: !Sub ${Identifier}-private-b
  PrivateSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableB
      SubnetId: !Ref PrivateSubnetB
  PrivateSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Select [2, !Cidr [!Ref PrivateSubnetCidr, 4, !FindInMap [Cidr, PrefixLength, !Ref PrivateSubnetPrefixLength]]]
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: private
      - Key: Name
        Value: !Sub ${Identifier}-private-c
  PrivateSubnetCRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableC
      SubnetId: !Ref PrivateSubnetC
  RouteToTransitGatewayA:
    Type: AWS::EC2::Route
    DependsOn: TransitGatewayAttachment
    Properties:
      RouteTableId: !Ref PrivateRouteTableA
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/tgw-id}}'
  RouteToNatGatewayA:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableA
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayA
  RouteToTransitGatewayB:
    Type: AWS::EC2::Route
    DependsOn: TransitGatewayAttachment
    Properties:
      RouteTableId: !Ref PrivateRouteTableB
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/tgw-id}}'
  RouteToNatGatewayB:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableB
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayB
  RouteToTransitGatewayC:
    Type: AWS::EC2::Route
    DependsOn: TransitGatewayAttachment
    Properties:
      RouteTableId: !Ref PrivateRouteTableC
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/tgw-id}}'
  RouteToNatGatewayC:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableC
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayC
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: public
      - Key: Name
        Value: !Sub ${Identifier}-public
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties: 
      Tags: 
      - Key: Name
        Value: !Sub ${Identifier}-igw
  IgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref Vpc
  RoutePublicSubnetToTransitGateway:
    Type: AWS::EC2::Route
    DependsOn: TransitGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Sub '{{resolve:ssm:/networking/${AWS::Region}/tgw-id}}'
  RouteToInternetGateway:
    DependsOn: IgwAttachment
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnetA:
    DependsOn: PublicVpcCidr
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      CidrBlock: !Select [0, !Cidr [!Ref PublicSubnetCidr, 4, !FindInMap [Cidr, PrefixLength, !Ref PublicSubnetPrefixLength]]]
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: public
      - Key: Name
        Value: !Sub ${Identifier}-public-a
  NatGatewayElasticIpA:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      Tags:
      - Key: Name
        Value: !Sub ${Identifier}-a
  NatGatewayA:
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId: !GetAtt NatGatewayElasticIpA.AllocationId
      ConnectivityType: public
      SubnetId: !Ref PublicSubnetA
      Tags:
      - Key: Name
        Value: !Sub ${Identifier}-a
  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetA
  PublicSubnetB:
    DependsOn: PublicVpcCidr
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [1, !GetAZs ""]
      CidrBlock: !Select [1, !Cidr [!Ref PublicSubnetCidr, 4, !FindInMap [Cidr, PrefixLength, !Ref PublicSubnetPrefixLength]]]
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: public
      - Key: Name
        Value: !Sub ${Identifier}-public-b
  NatGatewayElasticIpB:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      Tags:
      - Key: Name
        Value: !Sub ${Identifier}-b
  NatGatewayB:
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId: !GetAtt NatGatewayElasticIpB.AllocationId
      ConnectivityType: public
      SubnetId: !Ref PublicSubnetB
      Tags:
      - Key: Name
        Value: !Sub ${Identifier}-b
  PublicSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetB
  PublicSubnetC:
    DependsOn: PublicVpcCidr
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [2, !GetAZs ""]
      CidrBlock: !Select [2, !Cidr [!Ref PublicSubnetCidr, 4, !FindInMap [Cidr, PrefixLength, !Ref PublicSubnetPrefixLength]]]
      VpcId: !Ref Vpc
      Tags:
      - Key: Type
        Value: public
      - Key: Name
        Value: !Sub ${Identifier}-public-c
  NatGatewayElasticIpC:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      Tags:
      - Key: Name
        Value: !Sub ${Identifier}-c
  NatGatewayC:
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId: !GetAtt NatGatewayElasticIpC.AllocationId
      ConnectivityType: public
      SubnetId: !Ref PublicSubnetC
      Tags:
      - Key: Name
        Value: !Sub ${Identifier}-c
  PublicSubnetCRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetC

Outputs:
  Vpc:
    Value: !Ref Vpc