Transit Gateway でインターネットの出口を 1 つにする

## 目的

AWS 上で複数のアカウントや VPC を使用して開発していると、各アカウントに NAT Gateway が存在し、そんなにたくさん利用しているわけでもないのに維持するためのコストだけがかさむなぁ、という気分になってきます。

そんなとき、Transit Gateway というものがあるのを知りました。
Transit Gateway を使用すると、NAT 専用の VPC を作成し、ほかの VPC の Private な Subnet からは、その NAT 専用 VPC の NAT を経由することで、NAT は 1 台になって、コストが削減されるかも、という期待を抱いて試してみました。

結論からいうと、Transit Gateway は接続されている VPC の数によって課金されるので、全く安くならなかったのですが・・・。(まさに技術の無駄遣い）
せっかく作ったの何かのご参考にどうぞ。

## 全体構成

NAT 専用 VPC: 10.255.255.0/24

- Public Subnet1: 10.255.255.0/27 (Default GW: IGW) (NAT Gateway あり)
- Public Subnet2: 10.255.255.32/27 (Default GW: IGW)
- Public Subnet3: 10.255.255.64/27 (Default GW: IGW)
- Private Subnet1: 10.255.255.96/27 (Default GW: NATGW)
- Private Subnet2: 10.255.255.128/27 (Default GW: NATGW)
- Private Subnet3: 10.255.255.160/27 (Default GW: NATGW)

NAT に接続する VPC たち: 10.0.0.0/8 の範囲に作成するとする

NAT に接続する VPC1: 10.0.0.0/24

- Private Subnet1: 10.0.0.0/27
- Private Subnet2: 10.0.0.32/27
- Private Subnet3: 10.0.0.64/27

## NAT 専用 VPC

routing に依存関係があるので nested stack にしておきます。

```yaml:root.cf.yaml
Resources:
  NATVPC:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./nat-vpc.cf.yaml

  NATVPCRouting:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./route.cf.yaml
      Parameters:
        NatVpcTGW: !GetAtt NATVPC.Outputs.NatVpcTGW
        NatVpcPublicSubnet1RouteTable: !GetAtt NATVPC.Outputs.NatVpcPublicSubnet1RouteTable
        NatVpcPublicSubnet2RouteTable: !GetAtt NATVPC.Outputs.NatVpcPublicSubnet2RouteTable
        NatVpcPublicSubnet3RouteTable: !GetAtt NATVPC.Outputs.NatVpcPublicSubnet3RouteTable
```

### VPC と Internet Gateway の作成

```yaml:nat-vpc.cf.yaml
Resources:
  NatVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.255.255.0/24
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: nat-vpc

  NatVpcIGW:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: Name
          Value: nat-vpc-IGW

  NatVpcVPCGW:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      VpcId: !Ref NatVpc
      InternetGatewayId: !Ref NatVpcIGW
```

### Public Subnet と NAT Gateway の作成 1

もったいなので、NAT Gateway は Public1 にのみ作成します。

```yaml:nat-vpc.cf.yaml続き
  NatVpcPublicSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.255.255.0/27
      VpcId: !Ref NatVpc
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet1
        - Key: "aws-cdk:subnet-name"
          Value: nat-public
        - Key: "aws-cdk:subnet-type"
          Value: Public

  NatVpcPublicSubnet1RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet1

  NatVpcPublicSubnet1RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet1RouteTable
      SubnetId: !Ref NatVpcPublicSubnet1

  NatVpcPublicSubnet1DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet1RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref NatVpcIGW
    DependsOn:
      - NatVpcVPCGW

  NatVpcPublicSubnet1EIP:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet1

  NatVpcPublicSubnet1NATGateway:
    Type: "AWS::EC2::NatGateway"
    Properties:
      AllocationId:
        "Fn::GetAtt":
          - NatVpcPublicSubnet1EIP
          - AllocationId
      SubnetId: !Ref NatVpcPublicSubnet1
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet1
```

### Public Subnet の作成 2

```yaml:nat-vpc.cf.yaml続き
  NatVpcPublicSubnet2:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.255.255.32/27
      VpcId: !Ref NatVpc
      AvailabilityZone: !Select [1, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet2
        - Key: "aws-cdk:subnet-name"
          Value: nat-public
        - Key: "aws-cdk:subnet-type"
          Value: Public

  NatVpcPublicSubnet2RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet2

  NatVpcPublicSubnet2RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet2RouteTable
      SubnetId: !Ref NatVpcPublicSubnet2

  NatVpcPublicSubnet2DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet2RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref NatVpcIGW
    DependsOn:
      - NatVpcVPCGW
```

### Public Subnet の作成 3

```yaml:nat-vpc.cf.yaml続き
  NatVpcPublicSubnet3Subnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.255.255.64/27
      VpcId: !Ref NatVpc
      AvailabilityZone: !Select [2, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet3
        - Key: "aws-cdk:subnet-name"
          Value: nat-public
        - Key: "aws-cdk:subnet-type"
          Value: Public

  NatVpcPublicSubnet3RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-publicSubnet3

  NatVpcPublicSubnet3RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet3RouteTable
      SubnetId: !Ref NatVpcPublicSubnet3Subnet

  NatVpcPublicSubnet3DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet3RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref NatVpcIGW
    DependsOn:
      - NatVpcVPCGW
```

### Private Subnet の作成 1

```yaml:nat-vpc.cf.yaml続き
  NatVpcPrivateSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.255.255.96/27
      VpcId: !Ref NatVpc
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: nat-vpc/nat-privateSubnet1
        - Key: "aws-cdk:subnet-name"
          Value: nat-private
        - Key: "aws-cdk:subnet-type"
          Value: Private

  NatVpcPrivateSubnet1RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-privateSubnet1

  NatVpcPrivateSubnet1RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref NatVpcPrivateSubnet1RouteTable
      SubnetId: !Ref NatVpcPrivateSubnet1

  NatVpcPrivateSubnet1DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPrivateSubnet1RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatVpcPublicSubnet1NATGateway
```

### Private Subnet の作成 2

```yaml:nat-vpc.cf.yaml続き
  NatVpcPrivateSubnet2:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.255.255.128/27
      VpcId: !Ref NatVpc
      AvailabilityZone: !Select [1, !GetAZs ""]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: nat-vpc/nat-privateSubnet2
        - Key: "aws-cdk:subnet-name"
          Value: nat-private
        - Key: "aws-cdk:subnet-type"
          Value: Private

  NatVpcPrivateSubnet2RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-privateSubnet2

  NatVpcPrivateSubnet2RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref NatVpcPrivateSubnet2RouteTable
      SubnetId: !Ref NatVpcPrivateSubnet2

  NatVpcPrivateSubnet2DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPrivateSubnet2RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatVpcPublicSubnet1NATGateway
```

### Private Subnet の作成 3

```yaml:nat-vpc.cf.yaml続き
  NatVpcPrivateSubnet3:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.255.255.160/27
      VpcId: !Ref NatVpc
      AvailabilityZone: !Select [2, !GetAZs ""]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: nat-vpc/nat-privateSubnet3
        - Key: "aws-cdk:subnet-name"
          Value: nat-private
        - Key: "aws-cdk:subnet-type"
          Value: Private

  NatVpcPrivateSubnet3RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc/nat-privateSubnet3

  NatVpcPrivateSubnet3RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref NatVpcPrivateSubnet3RouteTable
      SubnetId: !Ref NatVpcPrivateSubnet3

  NatVpcPrivateSubnet3DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPrivateSubnet3RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatVpcPublicSubnet1NATGateway
```

### Transit Gateway の作成

```yaml:nat-vpc.cf.yaml続き
  NatVpcTGW:
    Type: "AWS::EC2::TransitGateway"
    Properties:
      AutoAcceptSharedAttachments: enable
      DefaultRouteTableAssociation: enable
      DefaultRouteTablePropagation: enable
      VpnEcmpSupport: enable
      DnsSupport: enable
      Tags:
        - Key: Name
          Value: Devel TGW

  NatVpcTGWAtt:
    Type: "AWS::EC2::TransitGatewayAttachment"
    Properties:
      SubnetIds:
        - !Ref NatVpcPrivateSubnet1
        - !Ref NatVpcPrivateSubnet2
        - !Ref NatVpcPrivateSubnet3
      TransitGatewayId: !Ref NatVpcTGW
      VpcId: !Ref NatVpc
      Tags:
        - Key: Name
          Value: nat-vpc
```

### Default Gateway を NAT 専用 VPC に向ける

```yaml:nat-vpc.cf.yaml続き
#   NatVpcTGWRoute:
#     Type: "AWS::EC2::TransitGatewayRoute"
#     Properties:
#       TransitGatewayRouteTableId: !Ref NatVpcTGWRouteTable
#       DestinationCidrBlock: 0.0.0.0/0
#       TransitGatewayAttachmentId: !Ref NatVpcTGWAtt

#   NatVpcTGWRouteTable:
#     Type: "AWS::EC2::TransitGatewayRouteTable"
#     Properties:
#       TransitGatewayId: !Ref NatVpcTGW

#   NatVpcTGWRouteTableAssociation:
#     Type: AWS::EC2::TransitGatewayRouteTableAssociation
#     Properties:
#       TransitGatewayAttachmentId: !Ref NatVpcTGWAtt
#       TransitGatewayRouteTableId: !Ref NatVpcTGWRouteTable
```

しかし、これは、標準の物と別に Route Table がもう 1 つ作成されてしまいうまくいきません。
なのでコメントアウトしておきました。

AutoAcceptSharedAttachments, DefaultRouteTableAssociation, DefaultRouteTablePropagation の設定で、Transit Gateway に新しい VPC が接続されたときは自動でルーティングまで作成するようにしています。
このため、Transit Gateway を作成した段階で Route Table が 1 つ作成され、上記の YAML でもう 1 つ作成されてしまうようです。
先に作成した Transit Gateway の Route Table の ID を取得できればいいのですが、現在の CloudFormation ではその機能がなく、残念ながら現在は CloudFormation で記述することはできないようです。

ということで、コンソールから、手動で Transit Gateway の Route Table に 0.0.0.0/0 向け通信は NAT 専用 VPC に行くようなルーティングを追加してください。

### Transit Gateway を他のアカウントと共有

```yaml:nat-vpc.cf.yaml続き
  NatVpcTGWRAM:
    Type: "AWS::RAM::ResourceShare"
    Properties:
      Name: NAT_TGW
      AllowExternalPrincipals: false
      Principals:
        - Transit GateWayを公開するアカウントIDやOUまたは組織のARN
        # - arn:aws:organizations::123456789012:organization/o-1234abcdef
      ResourceArns:
        - "Fn::Join":
            - ""
            - - "arn:aws:ec2:"
              - !Ref AWS::Region
              - ":"
              - !Ref AWS::AccountId
              - ":transit-gateway/"
              - !Ref NatVpcTGW
```

### 必要な情報を Output

```yaml:nat-vpc.cf.yaml続き
Outputs:
  NatVpcTGW:
    Description: Transit Gateway ID
    Value: !Ref NatVpcTGW
    Export:
      Name: NatVpcTGW

  NatVpcPublicSubnet1RouteTable:
    Value: !Ref NatVpcPublicSubnet1RouteTable
    Export:
      Name: NatVpcPublicSubnet1RouteTable

  NatVpcPublicSubnet2RouteTable:
    Value: !Ref NatVpcPublicSubnet2RouteTable
    Export:
      Name: NatVpcPublicSubnet2RouteTable

  NatVpcPublicSubnet3RouteTable:
    Value: !Ref NatVpcPublicSubnet3RouteTable
    Export:
      Name: NatVpcPublicSubnet3RouteTable
```

### NAT Gateway から Transit Gateway への routing

NAT Gateway(のある Subnet)から 10.0.0.0/8 宛の通信はすべて TransitGateway を通過するような Routing を作成します。
Private Subnet は Transit Gateway が同じネットワークにいますが、Public はそうではなく、Default Gateway が Internet Gateway なので、Transit Gateway から来た戻りのルーティングを書いてあげる必要があります。

NAT は Public1 にしか作っていないので、NAT から戻れればいいだけであれば、Public1 の分だけあればいいのかもしれないですが。

```yaml:route.cf.yaml
Parameters:
  NatVpcPublicSubnet1RouteTable:
    Type: String

  NatVpcPublicSubnet2RouteTable:
    Type: String

  NatVpcPublicSubnet3RouteTable:
    Type: String

  NatVpcTGW:
    Type: String

Resources:
  NatVpcPublicSubnet1TGWRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet1RouteTable
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Ref NatVpcTGW

  NatVpcPublicSubnet2TGWRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet2RouteTable
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Ref NatVpcTGW

  NatVpcPublicSubnet3TGWRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref NatVpcPublicSubnet3RouteTable
      DestinationCidrBlock: 10.0.0.0/8
      TransitGatewayId: !Ref NatVpcTGW
```

## NAT に接続する VPC1 の作成

### Nested Stack 親

```yaml:root.cf.yaml
Parameters:
  NatVpcTGW:
    Type: String
    Default: Transit GatewayのID (tgw-1234567890)

Resources:
  VPC1:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./vpc1.cf.yaml
      Parameters:
        NatVpcTGW: !Ref NatVpcTGW

  VPC1Routing:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./route.cf.yaml
      Parameters:
        NatVpcTGW: !Ref NatVpcTGW
        Vpc1PrivateSubnet1RouteTable: !GetAtt VPC1.Outputs.Vpc1PrivateSubnet1RouteTable
        Vpc1PrivateSubnet2RouteTable: !GetAtt VPC1.Outputs.Vpc1PrivateSubnet2RouteTable
        Vpc1PrivateSubnet3RouteTable: !GetAtt VPC1.Outputs.Vpc1PrivateSubnet3RouteTable
```

### VPC と Private Subnet を作成

```yaml:vpc1.cf.yaml
Parameters:
  NatVpcTGW:
    Type: String

Resources:
  Vpc1:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: 10.0.8.0/24
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: vpc1-vpc

  Vpc1PublicSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.8.0/27
      VpcId: !Ref Vpc1
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet1
        - Key: "aws-cdk:subnet-name"
          Value: vpc1-public
        - Key: "aws-cdk:subnet-type"
          Value: Public

  Vpc1PublicSubnet1RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet1

  Vpc1PublicSubnet1RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref Vpc1PublicSubnet1RouteTable
      SubnetId: !Ref Vpc1PublicSubnet1

  Vpc1PublicSubnet1DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref Vpc1PublicSubnet1RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Vpc1
    DependsOn:
      - Vpc1VPCGW

  Vpc1PublicSubnet1EIP:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet1

  Vpc1PublicSubnet2:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.8.32/27
      VpcId: !Ref Vpc1
      AvailabilityZone: !Select [1, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet2
        - Key: "aws-cdk:subnet-name"
          Value: vpc1-public
        - Key: "aws-cdk:subnet-type"
          Value: Public

  Vpc1PublicSubnet2RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet2

  Vpc1PublicSubnet2RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref Vpc1PublicSubnet2RouteTable
      SubnetId: !Ref Vpc1PublicSubnet2

  Vpc1PublicSubnet2DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref Vpc1PublicSubnet2RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Vpc1IGW
    DependsOn:
      - Vpc1VPCGW

  Vpc1PublicSubnet3Subnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.8.64/27
      VpcId: !Ref Vpc1
      AvailabilityZone: !Select [2, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet3
        - Key: "aws-cdk:subnet-name"
          Value: vpc1-public
        - Key: "aws-cdk:subnet-type"
          Value: Public

  Vpc1PublicSubnet3RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-publicSubnet3

  Vpc1PublicSubnet3RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref Vpc1PublicSubnet3RouteTable
      SubnetId: !Ref Vpc1PublicSubnet3Subnet

  Vpc1PublicSubnet3DefaultRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref Vpc1PublicSubnet3RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Vpc1IGW
    DependsOn:
      - Vpc1VPCGW

  Vpc1PrivateSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.8.96/27
      VpcId: !Ref Vpc1
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-privateSubnet1
        - Key: "aws-cdk:subnet-name"
          Value: vpc1-private
        - Key: "aws-cdk:subnet-type"
          Value: Private

  Vpc1PrivateSubnet1RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-privateSubnet1

  Vpc1PrivateSubnet1RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref Vpc1PrivateSubnet1RouteTable
      SubnetId: !Ref Vpc1PrivateSubnet1

  Vpc1PrivateSubnet2:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.8.128/27
      VpcId: !Ref Vpc1
      AvailabilityZone: !Select [1, !GetAZs ""]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-privateSubnet2
        - Key: "aws-cdk:subnet-name"
          Value: vpc1-private
        - Key: "aws-cdk:subnet-type"
          Value: Private

  Vpc1PrivateSubnet2RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-privateSubnet2

  Vpc1PrivateSubnet2RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref Vpc1PrivateSubnet2RouteTable
      SubnetId: !Ref Vpc1PrivateSubnet2

  Vpc1PrivateSubnet3:
    Type: "AWS::EC2::Subnet"
    Properties:
      CidrBlock: 10.0.8.160/27
      VpcId: !Ref Vpc1
      AvailabilityZone: !Select [2, !GetAZs ""]
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-privateSubnet3
        - Key: "aws-cdk:subnet-name"
          Value: vpc1-private
        - Key: "aws-cdk:subnet-type"
          Value: Private

  Vpc1PrivateSubnet3RouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc/vpc1-privateSubnet3

  Vpc1PrivateSubnet3RouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref Vpc1PrivateSubnet3RouteTable
      SubnetId: !Ref Vpc1PrivateSubnet3

  Vpc1IGW:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: Name
          Value: vpc1-vpc-IGW

  Vpc1VPCGW:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      VpcId: !Ref Vpc1
      InternetGatewayId: !Ref Vpc1IGW

  Vpc1TGW:
    Type: "AWS::EC2::TransitGateway"
    Properties:
      AutoAcceptSharedAttachments: enable
      DefaultRouteTableAssociation: enable
      DefaultRouteTablePropagation: enable
      VpnEcmpSupport: enable
      DnsSupport: enable
      Tags:
        - Key: Name
          Value: Devel TGW

  Vpc1TGWAtt:
    Type: "AWS::EC2::TransitGatewayAttachment"
    Properties:
      SubnetIds:
        - !Ref Vpc1PrivateSubnet1
        - !Ref Vpc1PrivateSubnet2
        - !Ref Vpc1PrivateSubnet3
      TransitGatewayId: !Ref NatVpcTGW
      VpcId: !Ref Vpc1
      Tags:
        - Key: Name
          Value: vpc1-vpc

Outputs:
  Vpc1TGW:
    Description: Transit Gateway ID
    Value: !Ref Vpc1TGW
    Export:
      Name: Vpc1TGW

  Vpc1PrivateSubnet1RouteTable:
    Value: !Ref Vpc1PrivateSubnet1RouteTable
    Export:
      Name: Vpc1PrivateSubnet1RouteTable

  Vpc1PrivateSubnet2RouteTable:
    Value: !Ref Vpc1PrivateSubnet2RouteTable
    Export:
      Name: Vpc1PrivateSubnet2RouteTable

  Vpc1PrivateSubnet3RouteTable:
    Value: !Ref Vpc1PrivateSubnet3RouteTable
    Export:
      Name: Vpc1PrivateSubnet3RouteTable
```

### Routing の設定

```yaml:route.cf.yaml
Parameters:
  Vpc1PrivateSubnet1RouteTable:
    Type: String

  Vpc1PrivateSubnet2RouteTable:
    Type: String

  Vpc1PrivateSubnet3RouteTable:
    Type: String

  NatVpcTGW:
    Type: String

Resources:
  Vpc1PrivateSubnet1TGWRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref Vpc1PrivateSubnet1RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayId: !Ref NatVpcTGW

  Vpc1PrivateSubnet2TGWRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref Vpc1PrivateSubnet2RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayId: !Ref NatVpcTGW

  Vpc1PrivateSubnet3TGWRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref Vpc1PrivateSubnet3RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayId: !Ref NatVpcTGW
```


*本記事は @qualitia_cdevの中の一人、@hirachanさんに作成していただきました。