1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Transit Gatewayのピアリング接続を試してみる

1
Last updated at Posted at 2026-03-31

Transit Gatewayピアリング

Transit Gateway(TGW)ピアリングは、同一リージョン内(Intra-Region)および異なるリージョン間(Inter-Region)のTransit Gatewayを相互接続し、IPv4/IPv6トラフィックをルーティングする機能です。ピアリングは自アカウント内のTGW同士だけでなく、別のAWSアカウントが所有するTGWとの間でも確立可能です。 ピアリング設定は以下の流れで行います。

  1. 一方のTGWでピアリングアタッチメントを作成し相手先のTGWを指定します
  2. 相手先のTGWでピアリングリクエストを承認します
  3. 通信に必要なルートテーブルの修正を行います

ピアリングの制約

TGWピアリングには、以下の制約があります。

  • 動的ルーティングに非対応:ピアリングアタッチメント間ではルート伝播はサポートされていないため、静的ルートをTGWのルートテーブルに手動で追加する必要があります(ピアリング越しのBGPによるルート伝播もサポートされません)
  • ECMPに非対応:TGWピアリングでは動的ルーティングをサポートしていない、かつ、同じ静的ルートを2つの異なるターゲットに対して設定できないためECMPを利用することができません
  • マルチキャストに非対応:ピアリング越しのマルチキャストには対応していません

現状はBGPピアリングがサポートされないため、ピアリングを行うTransit Gateway同士のASNは重複しても問題ありません。ただし、ルート伝播がサポートされた場合を考慮してASNは重複しないように設計することが推奨されています。

試してみる

それでは実際にTGWピアリングを試してみます。今回は、同様のアカウントで別々のリージョンのTGW環境を用意します。

検証環境構成

検証のため以下の環境を用意します。

image.png

本構成では既に以下の通信が可能な状態です。

  • us-east-1(左下)のEC2からNetwork Firewallでパケット検査し、VPC-BoundaryのNat Gateway経由でインターネット接続
  • us-east-2(右側)のVPC間の通信

※構成図には書いていませんが、us-east-2(右側)のEC2は動作確認用にVPCエンドポイントでSSMアクセス可能な状態にしています

検証環境は、サクッとCloudFormationで用意しました。(ほとんどAIに書いてもらいました。本当に優秀ですね。。。)

us-east-1リソース
us-east-1.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: |
  Centralized Network Firewall architecture (Transit Gateway attachment / 2-VPC design)
  Network Firewall is attached directly to Transit Gateway,
  eliminating Firewall subnets and VPC endpoints from the VPCs.

  Traffic flow:
    [Egress]  EC2 -> TGW(Private RT) -> NF(inspect) -> TGW(NF RT) -> VPC-Boundary TGW Subnet
                  -> NAT GW -> IGW -> Internet
    [Ingress] Internet -> IGW -> NAT GW -> TGW(Boundary RT) -> NF(inspect) -> TGW(NF RT) -> EC2

Parameters:
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
    Description: Newest Amazon Linux 2023 AMI (Systems Manager Parameter Store)

Mappings:
  # AZ ID mapping required for Network Firewall TGW attachment.
  # AZ IDs are globally unique, but the mapping from AZ name (e.g. us-east-1a)
  # to AZ ID varies per AWS account.
  RegionFirstAZId:
    us-east-1:      { ZoneId: use1-az1 }
    us-east-2:      { ZoneId: use2-az1 }
    us-west-1:      { ZoneId: usw1-az1 }
    us-west-2:      { ZoneId: usw2-az1 }
    ap-northeast-1: { ZoneId: apne1-az1 }
    ap-northeast-2: { ZoneId: apne2-az1 }
    ap-northeast-3: { ZoneId: apne3-az1 }
    ap-southeast-1: { ZoneId: apse1-az1 }
    ap-southeast-2: { ZoneId: apse2-az1 }
    ap-south-1:     { ZoneId: aps1-az1 }
    eu-west-1:      { ZoneId: euw1-az1 }
    eu-west-2:      { ZoneId: euw2-az1 }
    eu-west-3:      { ZoneId: euw3-az1 }
    eu-central-1:   { ZoneId: euc1-az1 }
    eu-north-1:     { ZoneId: eun1-az1 }
    sa-east-1:      { ZoneId: sae1-az1 }
    ca-central-1:   { ZoneId: cac1-az1 }

Resources:

  # ================================================================
  # VPC-Boundary (10.1.0.0/16)
  #   - TGW subnet    (10.1.1.0/24) : Transit Gateway dedicated
  #   - Public subnet (10.1.2.0/24) : NAT Gateway
  # ================================================================
  VPCBoundary:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.1.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: VPC-Boundary

  IGWBoundary:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: IGW-Boundary

  IGWBoundaryAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPCBoundary
      InternetGatewayId: !Ref IGWBoundary

  SubnetBoundaryTGW:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCBoundary
      CidrBlock: 10.1.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Boundary-TGW

  SubnetBoundaryPublic:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCBoundary
      CidrBlock: 10.1.2.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Boundary-Public

  EIPNatGW:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  NatGateway:
    Type: AWS::EC2::NatGateway
    DependsOn: IGWBoundaryAttachment
    Properties:
      AllocationId: !GetAtt EIPNatGW.AllocationId
      SubnetId: !Ref SubnetBoundaryPublic
      Tags:
        - Key: Name
          Value: NatGW-Boundary

  # ------------------------------------------------------------------
  # Route table: Public subnet
  #   0.0.0.0/0   -> IGW  (internet egress)
  #   10.2.0.0/16 -> TGW  (return packets from NAT GW forwarded to TGW)
  #                        TGW(Boundary RT) -> NF(inspect) -> TGW(NF RT) -> Private
  # ------------------------------------------------------------------
  RTBoundaryPublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCBoundary
      Tags:
        - Key: Name
          Value: RT-Boundary-Public

  RouteBoundaryPublicDefault:
    Type: AWS::EC2::Route
    DependsOn: IGWBoundaryAttachment
    Properties:
      RouteTableId: !Ref RTBoundaryPublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGWBoundary

  RouteBoundaryPublicToPrivate:
    Type: AWS::EC2::Route
    DependsOn: TGWAttachmentBoundary
    Properties:
      RouteTableId: !Ref RTBoundaryPublic
      DestinationCidrBlock: 10.2.0.0/16
      TransitGatewayId: !Ref TransitGateway

  # ------------------------------------------------------------------
  # Route table: TGW subnet
  #   0.0.0.0/0 -> NAT GW  (NF-inspected egress packets forwarded to NAT GW)
  # ------------------------------------------------------------------
  RTBoundaryTGW:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCBoundary
      Tags:
        - Key: Name
          Value: RT-Boundary-TGW

  RouteBoundaryTGWDefault:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RTBoundaryTGW
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

  RTABoundaryPublic:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetBoundaryPublic
      RouteTableId: !Ref RTBoundaryPublic

  RTABoundaryTGW:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetBoundaryTGW
      RouteTableId: !Ref RTBoundaryTGW

  # ================================================================
  # Network Firewall (attached directly to Transit Gateway)
  # ================================================================
  FirewallStatefulRuleGroup:
    Type: AWS::NetworkFirewall::RuleGroup
    Properties:
      RuleGroupName: !Sub "${AWS::StackName}-StatefulAllowAll"
      Type: STATEFUL
      Capacity: 100
      RuleGroup:
        StatefulRuleOptions:
          RuleOrder: STRICT_ORDER
        RulesSource:
          RulesString: |
            pass ip any any -> any any (msg:"Allow all traffic"; sid:1; rev:1;)
      Tags:
        - Key: Name
          Value: StatefulAllowAll

  FirewallPolicy:
    Type: AWS::NetworkFirewall::FirewallPolicy
    Properties:
      FirewallPolicyName: !Sub "${AWS::StackName}-CentralizedInspectionPolicy"
      FirewallPolicy:
        StatelessDefaultActions:
          - aws:forward_to_sfe
        StatelessFragmentDefaultActions:
          - aws:forward_to_sfe
        StatefulEngineOptions:
          RuleOrder: STRICT_ORDER
        StatefulDefaultActions:
          - aws:drop_strict
        StatefulRuleGroupReferences:
          - ResourceArn: !GetAtt FirewallStatefulRuleGroup.RuleGroupArn
            Priority: 1
      Tags:
        - Key: Name
          Value: CentralizedInspectionPolicy

  # Specifying TransitGatewayId attaches the firewall directly to TGW.
  # VpcId and SubnetMappings are not required (no Firewall subnet or VPC endpoint).
  NetworkFirewall:
    Type: AWS::NetworkFirewall::Firewall
    Properties:
      FirewallName: !Sub "${AWS::StackName}-CentralizedFirewall"
      FirewallPolicyArn: !GetAtt FirewallPolicy.FirewallPolicyArn
      TransitGatewayId: !Ref TransitGateway
      AvailabilityZoneMappings:
        - AvailabilityZone: !FindInMap [RegionFirstAZId, !Ref AWS::Region, ZoneId]
      DeleteProtection: false
      FirewallPolicyChangeProtection: false
      Tags:
        - Key: Name
          Value: CentralizedFirewall

  # ================================================================
  # VPC-Private (10.2.0.0/16)
  #   - TGW subnet (10.2.1.0/24) : Transit Gateway dedicated
  #   - EC2 subnet (10.2.2.0/24) : EC2 instance
  # ================================================================
  VPCPrivate:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.2.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: VPC-Private

  SubnetPrivateTGW:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCPrivate
      CidrBlock: 10.2.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Private-TGW

  SubnetPrivateEC2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCPrivate
      CidrBlock: 10.2.2.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Private-EC2

  # ------------------------------------------------------------------
  # Route table: EC2 subnet
  #   0.0.0.0/0 -> TGW  (all egress traffic forwarded to TGW)
  #                       TGW(Private RT) -> NF(inspect) -> TGW(NF RT) -> Boundary
  # ------------------------------------------------------------------
  RTPrivateEC2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCPrivate
      Tags:
        - Key: Name
          Value: RT-Private-EC2

  RoutePrivateEC2Default:
    Type: AWS::EC2::Route
    DependsOn: TGWAttachmentPrivate
    Properties:
      RouteTableId: !Ref RTPrivateEC2
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayId: !Ref TransitGateway

  # Route table: TGW subnet (local routes only)
  RTPrivateTGW:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCPrivate
      Tags:
        - Key: Name
          Value: RT-Private-TGW

  RTAPrivateEC2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivateEC2
      RouteTableId: !Ref RTPrivateEC2

  RTAPrivateTGW:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivateTGW
      RouteTableId: !Ref RTPrivateTGW

  # ================================================================
  # EC2 instance (VPC-Private)
  # ================================================================
  SGPrivateEC2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Private EC2 instance
      VpcId: !Ref VPCPrivate
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic
      Tags:
        - Key: Name
          Value: SG-Private-EC2

  EC2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${AWS::StackName}-EC2InstanceRole"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2InstanceRole

  PrivateEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: t3.micro
      SubnetId: !Ref SubnetPrivateEC2
      SecurityGroupIds:
        - !Ref SGPrivateEC2
      IamInstanceProfile: !Ref EC2InstanceProfile
      Tags:
        - Key: Name
          Value: EC2-Private

  # ================================================================
  # Transit Gateway
  # ================================================================
  TransitGateway:
    Type: AWS::EC2::TransitGateway
    Properties:
      Description: Centralized inspection Transit Gateway
      # ASN must differ from the peer TGW (tgw-peering-remote uses 64513)
      AmazonSideAsn: 64512
      DefaultRouteTableAssociation: disable
      DefaultRouteTablePropagation: disable
      AutoAcceptSharedAttachments: disable
      Tags:
        - Key: Name
          Value: TGW-Central

  # TGW attachments for VPCs only; NF attachment is created automatically by NetworkFirewall resource
  TGWAttachmentBoundary:
    Type: AWS::EC2::TransitGatewayAttachment
    Properties:
      TransitGatewayId: !Ref TransitGateway
      VpcId: !Ref VPCBoundary
      SubnetIds:
        - !Ref SubnetBoundaryTGW
      Tags:
        - Key: Name
          Value: TGWAttachment-Boundary

  TGWAttachmentPrivate:
    Type: AWS::EC2::TransitGatewayAttachment
    Properties:
      TransitGatewayId: !Ref TransitGateway
      VpcId: !Ref VPCPrivate
      SubnetIds:
        - !Ref SubnetPrivateTGW
      Tags:
        - Key: Name
          Value: TGWAttachment-Private

  # ----------------------------------------------------------------
  # TGW route table 1: Private (for VPC-Private attachment)
  #   All traffic -> Network Firewall attachment (for inspection)
  # ----------------------------------------------------------------
  TGWRouteTablePrivate:
    Type: AWS::EC2::TransitGatewayRouteTable
    Properties:
      TransitGatewayId: !Ref TransitGateway
      Tags:
        - Key: Name
          Value: TGW-RT-Private

  TGWAssociationPrivate:
    Type: AWS::EC2::TransitGatewayRouteTableAssociation
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTablePrivate
      TransitGatewayAttachmentId: !Ref TGWAttachmentPrivate

  TGWRoutePrivateDefault:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTablePrivate
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayAttachmentId: !GetAtt NetworkFirewall.TransitGatewayAttachmentId

  # ----------------------------------------------------------------
  # TGW route table 2: NF (for Network Firewall attachment)
  #   Routes post-inspection packets based on destination
  #   0.0.0.0/0   -> Boundary (internet-bound egress)
  #   10.2.0.0/16 -> Private  (return traffic to VPC-Private)
  # ----------------------------------------------------------------
  TGWRouteTableNF:
    Type: AWS::EC2::TransitGatewayRouteTable
    Properties:
      TransitGatewayId: !Ref TransitGateway
      Tags:
        - Key: Name
          Value: TGW-RT-NF

  TGWAssociationNF:
    Type: AWS::EC2::TransitGatewayRouteTableAssociation
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTableNF
      TransitGatewayAttachmentId: !GetAtt NetworkFirewall.TransitGatewayAttachmentId

  TGWRouteNFDefault:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTableNF
      DestinationCidrBlock: 0.0.0.0/0
      TransitGatewayAttachmentId: !Ref TGWAttachmentBoundary

  TGWRouteNFToPrivate:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTableNF
      DestinationCidrBlock: 10.2.0.0/16
      TransitGatewayAttachmentId: !Ref TGWAttachmentPrivate

  # ----------------------------------------------------------------
  # TGW route table 3: Boundary (for VPC-Boundary attachment)
  #   Return traffic -> Network Firewall attachment (bidirectional inspection)
  # ----------------------------------------------------------------
  TGWRouteTableBoundary:
    Type: AWS::EC2::TransitGatewayRouteTable
    Properties:
      TransitGatewayId: !Ref TransitGateway
      Tags:
        - Key: Name
          Value: TGW-RT-Boundary

  TGWAssociationBoundary:
    Type: AWS::EC2::TransitGatewayRouteTableAssociation
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTableBoundary
      TransitGatewayAttachmentId: !Ref TGWAttachmentBoundary

  TGWRouteBoundaryToPrivate:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTableBoundary
      DestinationCidrBlock: 10.2.0.0/16
      TransitGatewayAttachmentId: !GetAtt NetworkFirewall.TransitGatewayAttachmentId

# ================================================================
# Outputs
# ================================================================
Outputs:
  VPCBoundaryId:
    Description: VPC-Boundary ID
    Value: !Ref VPCBoundary

  VPCPrivateId:
    Description: VPC-Private ID
    Value: !Ref VPCPrivate

  TransitGatewayId:
    Description: Transit Gateway ID
    Value: !Ref TransitGateway

  NetworkFirewallArn:
    Description: Network Firewall ARN
    Value: !Ref NetworkFirewall

  NetworkFirewallTGWAttachmentId:
    Description: Network Firewall Transit Gateway Attachment ID
    Value: !GetAtt NetworkFirewall.TransitGatewayAttachmentId

  NatGatewayPublicIP:
    Description: NAT Gateway Elastic IP
    Value: !Ref EIPNatGW

  PrivateEC2InstanceId:
    Description: Private EC2 Instance ID (accessible via SSM Session Manager)
    Value: !Ref PrivateEC2Instance

us-east-2リソース
us-east-2.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: |
  TGW peering verification template (deploy to remote region)
  VPC-Private2 and VPC-Private3 communicate with each other via TGW.
  TGW peering itself must be configured manually from the console.

  VPC-Private2: 172.16.0.0/16
  VPC-Private3: 172.17.0.0/16

Parameters:
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
    Description: Newest Amazon Linux 2023 AMI (Systems Manager Parameter Store)

Resources:

  # ================================================================
  # VPC-Private2 (172.16.0.0/16)
  #   - TGW subnet (172.16.1.0/24) : Transit Gateway dedicated
  #   - EC2 subnet (172.16.2.0/24) : EC2 instance
  # ================================================================
  VPCPrivate2:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 172.16.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: VPC-Private2

  SubnetPrivate2EC2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCPrivate2
      CidrBlock: 172.16.2.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Private2-EC2

  SubnetPrivate2TGW:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCPrivate2
      CidrBlock: 172.16.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Private2-TGW

  # ------------------------------------------------------------------
  # Route table: EC2 subnet (VPC-Private2)
  #   172.17.0.0/16 -> TGW  (traffic to VPC-Private3)
  # ------------------------------------------------------------------
  RTPrivate2EC2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCPrivate2
      Tags:
        - Key: Name
          Value: RT-Private2-EC2

  RoutePrivate2ToPrivate3:
    Type: AWS::EC2::Route
    DependsOn: TGWAttachmentPrivate2
    Properties:
      RouteTableId: !Ref RTPrivate2EC2
      DestinationCidrBlock: 172.17.0.0/16
      TransitGatewayId: !Ref TransitGateway

  RTAPrivate2EC2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivate2EC2
      RouteTableId: !Ref RTPrivate2EC2

  # Route table: TGW subnet (local routes only)
  RTPrivate2TGW:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCPrivate2
      Tags:
        - Key: Name
          Value: RT-Private2-TGW

  RTAPrivate2TGW:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivate2TGW
      RouteTableId: !Ref RTPrivate2TGW

  # ================================================================
  # VPC-Private3 (172.17.0.0/16)
  #   - TGW subnet (172.17.1.0/24) : Transit Gateway dedicated
  #   - EC2 subnet (172.17.2.0/24) : EC2 instance
  # ================================================================
  VPCPrivate3:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 172.17.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: VPC-Private3

  SubnetPrivate3EC2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCPrivate3
      CidrBlock: 172.17.2.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Private3-EC2

  SubnetPrivate3TGW:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPCPrivate3
      CidrBlock: 172.17.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: Subnet-Private3-TGW

  # ------------------------------------------------------------------
  # Route table: EC2 subnet (VPC-Private3)
  #   172.16.0.0/16 -> TGW  (traffic to VPC-Private2)
  # ------------------------------------------------------------------
  RTPrivate3EC2:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCPrivate3
      Tags:
        - Key: Name
          Value: RT-Private3-EC2

  RoutePrivate3ToPrivate2:
    Type: AWS::EC2::Route
    DependsOn: TGWAttachmentPrivate3
    Properties:
      RouteTableId: !Ref RTPrivate3EC2
      DestinationCidrBlock: 172.16.0.0/16
      TransitGatewayId: !Ref TransitGateway

  RTAPrivate3EC2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivate3EC2
      RouteTableId: !Ref RTPrivate3EC2

  # Route table: TGW subnet (local routes only)
  RTPrivate3TGW:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCPrivate3
      Tags:
        - Key: Name
          Value: RT-Private3-TGW

  RTAPrivate3TGW:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivate3TGW
      RouteTableId: !Ref RTPrivate3TGW

  # ================================================================
  # SSM VPC endpoints (enables Session Manager without internet access)
  # ================================================================
  SGEndpointPrivate2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for SSM VPC endpoints in VPC-Private2
      VpcId: !Ref VPCPrivate2
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 172.16.0.0/16
          Description: Allow HTTPS from VPC-Private2
      Tags:
        - Key: Name
          Value: SG-Endpoint-Private2

  VPCESSMPrivate2:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPCPrivate2
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
      VpcEndpointType: Interface
      SubnetIds:
        - !Ref SubnetPrivate2EC2
      SecurityGroupIds:
        - !Ref SGEndpointPrivate2
      PrivateDnsEnabled: true

  VPCESSMMessagesPrivate2:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPCPrivate2
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages"
      VpcEndpointType: Interface
      SubnetIds:
        - !Ref SubnetPrivate2EC2
      SecurityGroupIds:
        - !Ref SGEndpointPrivate2
      PrivateDnsEnabled: true

  VPCEEC2MessagesPrivate2:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPCPrivate2
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages"
      VpcEndpointType: Interface
      SubnetIds:
        - !Ref SubnetPrivate2EC2
      SecurityGroupIds:
        - !Ref SGEndpointPrivate2
      PrivateDnsEnabled: true

  SGEndpointPrivate3:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for SSM VPC endpoints in VPC-Private3
      VpcId: !Ref VPCPrivate3
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 172.17.0.0/16
          Description: Allow HTTPS from VPC-Private3
      Tags:
        - Key: Name
          Value: SG-Endpoint-Private3

  VPCESSMPrivate3:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPCPrivate3
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
      VpcEndpointType: Interface
      SubnetIds:
        - !Ref SubnetPrivate3EC2
      SecurityGroupIds:
        - !Ref SGEndpointPrivate3
      PrivateDnsEnabled: true

  VPCESSMMessagesPrivate3:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPCPrivate3
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages"
      VpcEndpointType: Interface
      SubnetIds:
        - !Ref SubnetPrivate3EC2
      SecurityGroupIds:
        - !Ref SGEndpointPrivate3
      PrivateDnsEnabled: true

  VPCEEC2MessagesPrivate3:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPCPrivate3
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages"
      VpcEndpointType: Interface
      SubnetIds:
        - !Ref SubnetPrivate3EC2
      SecurityGroupIds:
        - !Ref SGEndpointPrivate3
      PrivateDnsEnabled: true

  # ================================================================
  # EC2 instances
  # ================================================================
  EC2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${AWS::StackName}-EC2InstanceRole"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2InstanceRole

  SGPrivate2EC2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EC2 in VPC-Private2
      VpcId: !Ref VPCPrivate2
      SecurityGroupIngress:
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 172.17.0.0/16
          Description: Allow ICMP from VPC-Private3
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: Allow all outbound
      Tags:
        - Key: Name
          Value: SG-Private2-EC2

  EC2Private2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: t3.micro
      SubnetId: !Ref SubnetPrivate2EC2
      SecurityGroupIds:
        - !Ref SGPrivate2EC2
      IamInstanceProfile: !Ref EC2InstanceProfile
      Tags:
        - Key: Name
          Value: EC2-Private2

  SGPrivate3EC2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EC2 in VPC-Private3
      VpcId: !Ref VPCPrivate3
      SecurityGroupIngress:
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 172.16.0.0/16
          Description: Allow ICMP from VPC-Private2
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: Allow all outbound
      Tags:
        - Key: Name
          Value: SG-Private3-EC2

  EC2Private3:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: t3.micro
      SubnetId: !Ref SubnetPrivate3EC2
      SecurityGroupIds:
        - !Ref SGPrivate3EC2
      IamInstanceProfile: !Ref EC2InstanceProfile
      Tags:
        - Key: Name
          Value: EC2-Private3

  # ================================================================
  # Transit Gateway
  # ================================================================
  TransitGateway:
    Type: AWS::EC2::TransitGateway
    Properties:
      Description: Transit Gateway for peering verification
      # ASN must differ from the peer TGW (network-firewall-centralized uses default 64512)
      AmazonSideAsn: 64513
      DefaultRouteTableAssociation: disable
      DefaultRouteTablePropagation: disable
      AutoAcceptSharedAttachments: disable
      Tags:
        - Key: Name
          Value: TGW-Peering

  TGWAttachmentPrivate2:
    Type: AWS::EC2::TransitGatewayAttachment
    Properties:
      TransitGatewayId: !Ref TransitGateway
      VpcId: !Ref VPCPrivate2
      SubnetIds:
        - !Ref SubnetPrivate2TGW
      Tags:
        - Key: Name
          Value: TGWAttachment-Private2

  TGWAttachmentPrivate3:
    Type: AWS::EC2::TransitGatewayAttachment
    Properties:
      TransitGatewayId: !Ref TransitGateway
      VpcId: !Ref VPCPrivate3
      SubnetIds:
        - !Ref SubnetPrivate3TGW
      Tags:
        - Key: Name
          Value: TGWAttachment-Private3

  # ----------------------------------------------------------------
  # TGW route table: for Private2 attachment
  #   172.17.0.0/16 -> TGWAttachment-Private3
  # ----------------------------------------------------------------
  TGWRouteTablePrivate2:
    Type: AWS::EC2::TransitGatewayRouteTable
    Properties:
      TransitGatewayId: !Ref TransitGateway
      Tags:
        - Key: Name
          Value: TGW-RT-Private2

  TGWAssociationPrivate2:
    Type: AWS::EC2::TransitGatewayRouteTableAssociation
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTablePrivate2
      TransitGatewayAttachmentId: !Ref TGWAttachmentPrivate2

  TGWRoutePrivate2ToPrivate3:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTablePrivate2
      DestinationCidrBlock: 172.17.0.0/16
      TransitGatewayAttachmentId: !Ref TGWAttachmentPrivate3

  # ----------------------------------------------------------------
  # TGW route table: for Private3 attachment
  #   172.16.0.0/16 -> TGWAttachment-Private2
  # ----------------------------------------------------------------
  TGWRouteTablePrivate3:
    Type: AWS::EC2::TransitGatewayRouteTable
    Properties:
      TransitGatewayId: !Ref TransitGateway
      Tags:
        - Key: Name
          Value: TGW-RT-Private3

  TGWAssociationPrivate3:
    Type: AWS::EC2::TransitGatewayRouteTableAssociation
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTablePrivate3
      TransitGatewayAttachmentId: !Ref TGWAttachmentPrivate3

  TGWRoutePrivate3ToPrivate2:
    Type: AWS::EC2::TransitGatewayRoute
    Properties:
      TransitGatewayRouteTableId: !Ref TGWRouteTablePrivate3
      DestinationCidrBlock: 172.16.0.0/16
      TransitGatewayAttachmentId: !Ref TGWAttachmentPrivate2

# ================================================================
# Outputs
# ================================================================
Outputs:
  VPCPrivate2Id:
    Description: VPC-Private2 ID
    Value: !Ref VPCPrivate2

  VPCPrivate3Id:
    Description: VPC-Private3 ID
    Value: !Ref VPCPrivate3

  TransitGatewayId:
    Description: Transit Gateway ID (used when configuring TGW peering)
    Value: !Ref TransitGateway

  EC2Private2InstanceId:
    Description: EC2-Private2 Instance ID (accessible via SSM Session Manager)
    Value: !Ref EC2Private2

  EC2Private3InstanceId:
    Description: EC2-Private3 Instance ID (accessible via SSM Session Manager)
    Value: !Ref EC2Private3

今回の検証にあたり調べていたところ、Network FirewallのTransit Gateway統合機能がサポートされていました。Firewall用のVPC作成が不要ということで、便利そうだったので本機能を採用しています。

設定手順

今回は、ピアリングを作成した後、VPC-Private2のEC2をFWで検査しNat Gateway経由でインターネット接続ができる状態を目指します。

image.png

①ピアリングアタッチメントの作成

まずは、TGW同士を繋ぐためピアリングを構成します。ピアリングはのアタッチメントを作成することで実現します。今回は、us-east-1から作業します。
※本記事の画像に含まれるリソースはすべて削除済みのため、リソースID等はマスクしておりません

image.png

アタッチメントタイプは「peering connection」を選びます。ピア接続アタッチメントに対向のTGWを指定します。

image.png

アタッチメントを作成すると、Initialing request状態となります。

image.png

②ピアリングリクエストの承認

ピアリングアタッチメントを作成すると、対向のTGWで承諾が必要です。us-east-2のアタッチメントの画面に新規のアタッチメントが作成されているため、アタッチメントを承諾します。(デフォルトでは名前がついてないようです)

image.png

③ルートテーブルの修正

ピアリングが構成できたため、実現したい通信内容に応じてルートテーブル(TGW、サブネット)を修正します。今回は、以下のルートテーブルを編集します。
※本記事では、ルートテーブルの設定に関する記載は割愛します

image.png

ポイントとしては、以下の2点です。

  1. ピアリング用に作成したアタッチメントにルートテーブルを関連付けます。新規ルートテーブルを作成した後、関連付けまで忘れずに行います。ピアリングアタッチメントは、双方のリージョンで作成されているため、それぞれのアタッチメントで関連付けが必要です。
  2. 特定のアタッチメントからのルーティング先(次のターゲット)がピアリングアタッチメントであった場合、参照されるのは対向のTGW側のピアリングアタッチメントのルートテーブルです。そのため、ピアリングアタッチメントのルートテーブルには自TGW側のルートを設定します。イメージについては、以下を参照してください。
  • インターネット向けの通信
    image.png

  • 帰りの通信
    image.png

動作確認

それでは動作確認してみます。右上のEC2インスタンスから8.8.8.8にpingをうちます。問題なく繋がっています。

image.png

ちょっと分かりづらいですが、送信元のIPアドレスも確認してみるとNat GatewayのEIPであることが確認できます。

image.png

Network Firewallがきいているかも確認したいので、ICMPをdropするルールを追加します。

image.png

すると、pingが届かなくなることを確認できます。ファイアウォールが正しく機能していそうです。

image.png

さいごに

AWS-ANSの更新があったので、復習もかねて久々にTGWを検証してみました。やっぱり、ネットワークはおもしろいですね。また気が向いたらTGWの記事を書きたいと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?