Shared services account VPC

Setting up the VPC

Enhance the Makefile in the infrastructure git project with the following content:

AWS_REGION               := eu-central-1
SHARED_SERVICES_VPC_CIDR := 172.16.64.0/18
TRANSIT_GW_ID            := tgw-abcde

###############################################################################################

.PHONY: shared-services-vpc
shared-services-vpc:
	aws cloudformation deploy \
		--no-fail-on-empty-changeset \
		--template-file shared-services/vpc.yml \
		--stack-name vpc \
		--parameter-overrides VpcCidr=$(SHARED_SERVICES_VPC_CIDR) \
		                      TransitGatewayId=$(TRANSIT_GW_ID) \
		--region $(AWS_REGION)

Make sure to use the correct cidr in the SHARED_SERVICES_VPC_CIDR and the correct transit gateway id in the TRANSIT_GW_ID variable.

Create a new cloudformation template in shared-services/vpc.yml and define the VPC in there:

---
AWSTemplateFormatVersion: 2010-09-09
Description: VPC setup for account

Parameters:
  VpcName:
    Description: 'Name of the VPC'
    Type: String
    Default: 'MainVPC'
  VpcCidr:
    Description: 'VPC CIDR (eg. 172.16.0.0/18)'
    Type: String
    Default: '172.16.0.0/18'
  TransitGatewayId:
    Description: 'Id of the transit gateway'
    Type: String
  TransitGatewayCidr:
    Description: 'CIDR of transit gateway network'
    Type: String
    Default: '172.16.0.0/12'

Resources:
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Ref VpcName
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: !Ref VpcName
  VPCGatewayAttachment:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  SubnetA:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Select [ 0, !Cidr [ !GetAtt VPC.CidrBlock, 8, 9 ]]
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} A private'
  SubnetB:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Select [ 1, !Cidr [ !GetAtt VPC.CidrBlock, 8, 9 ]]
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} B private'
  SubnetC:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select [2, !GetAZs '']
      CidrBlock: !Select [ 2, !Cidr [ !GetAtt VPC.CidrBlock, 8, 9 ]]
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} C private'
  SubnetAPublic:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Select [ 3, !Cidr [ !GetAtt VPC.CidrBlock, 8, 9 ]]
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} A public'
  SubnetBPublic:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Select [ 4, !Cidr [ !GetAtt VPC.CidrBlock, 8, 9 ]]
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} B public'
  SubnetCPublic:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select [2, !GetAZs '']
      CidrBlock: !Select [ 5, !Cidr [ !GetAtt VPC.CidrBlock, 8, 9 ]]
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} C public'
  RouteTableA:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} A private'
  RouteTableB:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} B private'
  RouteTableC:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} C private'
  RouteTableAPublic:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} A public'
  RouteTableBPublic:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} B public'
  RouteTableCPublic:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} C public'
  RouteTableAssociationA:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref SubnetA
      RouteTableId: !Ref RouteTableA
  RouteTableAssociationB:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref SubnetB
      RouteTableId: !Ref RouteTableB
  RouteTableAssociationC:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref SubnetC
      RouteTableId: !Ref RouteTableC
  RouteTableAssociationAPublic:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref SubnetAPublic
      RouteTableId: !Ref RouteTableAPublic
  RouteTableAssociationBPublic:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref SubnetBPublic
      RouteTableId: !Ref RouteTableBPublic
  RouteTableAssociationCPublic:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref SubnetCPublic
      RouteTableId: !Ref RouteTableCPublic
  RouteTablePublicAInternetRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableAPublic
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGateway
  RouteTablePublicBInternetRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableBPublic
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGateway
  RouteTablePublicCInternetRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableCPublic
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGateway
  RouteTablePublicATransitGatewayRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableAPublic
      DestinationCidrBlock: !Ref TransitGatewayCidr
      TransitGatewayId: !Ref TransitGatewayId
  RouteTablePublicBTransitGatewayRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableBPublic
      DestinationCidrBlock: !Ref TransitGatewayCidr
      TransitGatewayId: !Ref TransitGatewayId
  RouteTablePublicCTransitGatewayRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableCPublic
      DestinationCidrBlock: !Ref TransitGatewayCidr
      TransitGatewayId: !Ref TransitGatewayId
  RouteTableATransitGatewayRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableA
      DestinationCidrBlock: !Ref TransitGatewayCidr
      TransitGatewayId: !Ref TransitGatewayId
  RouteTableBTransitGatewayRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableB
      DestinationCidrBlock: !Ref TransitGatewayCidr
      TransitGatewayId: !Ref TransitGatewayId
  RouteTableCTransitGatewayRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTableC
      DestinationCidrBlock: !Ref TransitGatewayCidr
      TransitGatewayId: !Ref TransitGatewayId
  NetworkAcl:
    Type: 'AWS::EC2::NetworkAcl'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} private'
  NetworkAclPublic:
    Type: 'AWS::EC2::NetworkAcl'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${VpcName} public'
  SubnetNetworkAclAssociationA:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref SubnetA
      NetworkAclId: !Ref NetworkAcl
  SubnetNetworkAclAssociationB:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref SubnetB
      NetworkAclId: !Ref NetworkAcl
  SubnetNetworkAclAssociationC:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref SubnetC
      NetworkAclId: !Ref NetworkAcl
  SubnetNetworkAclAssociationAPublic:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref SubnetAPublic
      NetworkAclId: !Ref NetworkAclPublic
  SubnetNetworkAclAssociationBPublic:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref SubnetBPublic
      NetworkAclId: !Ref NetworkAclPublic
  SubnetNetworkAclAssociationCPublic:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref SubnetCPublic
      NetworkAclId: !Ref NetworkAclPublic
  NetworkAclEntryInAllowVPC:
    Type: 'AWS::EC2::NetworkAclEntry'
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 99
      Protocol: -1
      RuleAction: allow
      Egress: false
      CidrBlock: '0.0.0.0/0'
  NetworkAclEntryOutAllowVPC:
    Type: 'AWS::EC2::NetworkAclEntry'
    Properties:
      NetworkAclId: !Ref NetworkAcl
      RuleNumber: 99
      Protocol: -1
      RuleAction: allow
      Egress: true
      CidrBlock: '0.0.0.0/0'
  NetworkAclEntryInPublicAllowAll:
    Type: 'AWS::EC2::NetworkAclEntry'
    Properties:
      NetworkAclId: !Ref NetworkAclPublic
      RuleNumber: 99
      Protocol: -1
      RuleAction: allow
      Egress: false
      CidrBlock: '0.0.0.0/0'
  NetworkAclEntryOutPublicAllowAll:
    Type: 'AWS::EC2::NetworkAclEntry'
    Properties:
      NetworkAclId: !Ref NetworkAclPublic
      RuleNumber: 99
      Protocol: -1
      RuleAction: allow
      Egress: true
      CidrBlock: '0.0.0.0/0'
  TransitGatewayAttachment:
    Type: 'AWS::EC2::TransitGatewayAttachment'
    Properties:
      SubnetIds:
        - !Ref SubnetA
        - !Ref SubnetB
        - !Ref SubnetC
      TransitGatewayId: !Ref TransitGatewayId
      VpcId: !Ref VPC
Outputs:
  SubnetsPrivate:
    Description: 'Subnets.'
    Value: !Join [',', [!Ref SubnetA, !Ref SubnetB, !Ref SubnetC]]
    Export:
      Name: !Sub '${AWS::StackName}-SubnetsPrivate'
  SubnetsPublic:
    Description: 'Subnets public.'
    Value: !Join [',', [!Ref SubnetAPublic, !Ref SubnetBPublic, !Ref SubnetCPublic]]
    Export:
      Name: !Sub '${AWS::StackName}-SubnetsPublic'
  SubnetA:
    Description: 'Subnet A.'
    Value: !Ref SubnetA
    Export:
      Name: !Sub '${AWS::StackName}-SubnetA'
  SubnetB:
    Description: 'Subnet B.'
    Value: !Ref SubnetB
    Export:
      Name: !Sub '${AWS::StackName}-SubnetB'
  SubnetC:
    Description: 'Subnet C.'
    Value: !Ref SubnetC
    Export:
      Name: !Sub '${AWS::StackName}-SubnetC'
  VPC:
    Description: 'VPC.'
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-VPC'
  VPCCIDRBlock:
    Description: 'VPC CIDR block.'
    Value: !GetAtt VPC.CidrBlock
    Export:
      Name: !Sub '${AWS::StackName}-VPCCIDRBlock'
  RouteTableAPrivate:
    Description: 'Route table A private.'
    Value: !Ref RouteTableA
    Export:
      Name: !Sub '${AWS::StackName}-RouteTableAPrivate'
  RouteTableBPrivate:
    Description: 'Route table B private.'
    Value: !Ref RouteTableB
    Export:
      Name: !Sub '${AWS::StackName}-RouteTableBPrivate'
  RouteTableCPrivate:
    Description: 'Route table C private.'
    Value: !Ref RouteTableC
    Export:
      Name: !Sub '${AWS::StackName}-RouteTableCPrivate'

Create the Cloudformation stack with the following command in the shared-services AWS account

AWS_PROFILE=myorg-shared-services make shared-services-vpc

The VPC should be created and assigned to the transit gateway.