VPC同士を VPC Peering する構築ハンズオン


この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。

はじめに

検証のためにVPC間を毎回ピアリングして、ルートテーブルで設定をして、検証後に削除して、再度検証する際にルートテーブルの設定を見落として、1人で「ほげっ?」とかなっている稲村です。

今回は構築というより手順的な要素が強いですが、VPC Peeringの構築で月曜日の寝ぼけた状態でも構築ができるように手順を書いていきたいと思います。


構成図


ハンズオン

構築の流れ

1.VPC-A作成

2.VPC-B + VPC Peering 作成

3.VPC-A(ルートテーブル更新ver)作成

4.リソースの削除順番

3.VPC-A(ルートテーブル修正ver)2.VPC-B + VPC Peeringを参照し、2.VPC-B + VPC Peering1.VPC-Aを参照することでVPC Peeringの構築を行うため、削除の順番は3.VPC-A(ルートテーブル修正ver)で修正した部分を削除しないと、下記画像のような表示が出て、スタックを削除することが出来ません。

上記の順番で構築を行なっていきます。

最終的にピアリング接続のルートテーブルに、各VPCのルートテーブル同士が下記画像のように2つのルートテーブルが表示されれば、それぞれが接続されていることになります。


1.VPC-A作成

まずはVPC-Aを構築します。
後続の手順VPC-B + VPC Peeringを構築した後に、更新する部分はコメントアウトしている(PrivateRouteA Create)部分です。

AWSTemplateFormatVersion: "2010-09-09"
Description: 
  VPC,Subnet,IGW,RouteTable, Create

Metadata: 
  "AWS::CloudFormation::Interface": 
    ParameterGroups: 
      - Label: 
          default: "Project Name"
        Parameters: 
          - PJName
      - Label: 
          default: "Network Configuration"
        Parameters: 
          - VPCCIDR
          - PublicSubnetACIDR
          - PublicSubnetCCIDR
          - PrivateSubnetACIDR
          - PrivateSubnetCCIDR
          - UserName

    ParameterLabels: 
      PJName:
        default: "PJName"
      VPCCIDR: 
        default: "VPC CIDR"
      PublicSubnetACIDR: 
        default: "PublicSubnetA CIDR"
      PublicSubnetCCIDR: 
        default: "PublicSubnetC CIDR"
      PrivateSubnetACIDR: 
        default: "PrivateSubnetA CIDR"
      PrivateSubnetCCIDR: 
        default: "PrivateSubnetC CIDR"
      UserName: 
        default: "TagsValue UserName"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJName:
    Type: String
    Default: "cfn-network-A"

  VPCCIDR:
    Type: String
    Default: "172.16.0.0/16"

  PublicSubnetACIDR:
    Type: String
    Default: "172.16.10.0/24"

  PublicSubnetCCIDR:
    Type: String
    Default: "172.16.20.0/24"

  PrivateSubnetACIDR:
    Type: String
    Default: "172.16.30.0/24"

  PrivateSubnetCCIDR:
    Type: String
    Default: "172.16.40.0/24"
  UserName:
    Type: String 
    Default: "inamura"

Resources: 
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
# VPC Create
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-vpc"
        - Key: User
          Value: !Ref UserName

# InternetGateway Create
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-igw"
        - Key: User
          Value: !Ref UserName

# IGW Attach
  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#          
# Public SubnetA Create
  PublicSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-public-subnet-a"
        - Key: User
          Value: !Ref UserName

# Public SubnetC Create
  PublicSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PublicSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-public-subnet-c"
        - Key: User
          Value: !Ref UserName

# Private SubnetA Create
  PrivateSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-private-subnet-a"
        - Key: User
          Value: !Ref UserName

# Private SubnetC Create
  PrivateSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-private-subnet-c"
        - Key: User
          Value: !Ref UserName

# ------------------------------------------------------------#
#  RouteTable
# ------------------------------------------------------------#          
# Public RouteTableA Create
  PublicRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-public-routetable"
        - Key: User
          Value: !Ref UserName

# Private RouteTableA Create
  PrivateRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-private-routetable"
        - Key: User
          Value: !Ref UserName

# ------------------------------------------------------------#
# Routing
# ------------------------------------------------------------# 
# PublicRouteA Create
  PublicRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

##### VPC Peering設定後にコメントアウトを削除、スタック削除の場合はこの部分をコメントアウトする#####
#   PrivateRoute:
#     Type: "AWS::EC2::Route"
#     Properties: 
#       RouteTableId: !Ref PrivateRouteTable
#       DestinationCidrBlock: !ImportValue cfn-network-B-vpc-cidr
#       VpcPeeringConnectionId: !ImportValue cfn-network-B-private-vpcpeering
#####ここまで###########################################################################

# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------# 
# PublicRouteTable Associate SubnetA
  PublicSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetA 
      RouteTableId: !Ref PublicRouteTable

# PublicRouteTable Associate SubnetC
  PublicSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetC 
      RouteTableId: !Ref PublicRouteTable

# PrivateRouteTable Associate SubnetA
  PrivateSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTable

# PrivateRouteTable Associate SubnetC
  PrivateSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetC
      RouteTableId: !Ref PrivateRouteTable

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#                
Outputs:
# VPC
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJName}-vpc"
  VPCCIDR:
    Value: !Ref VPCCIDR
    Export:
      Name: !Sub "${PJName}-vpc-cidr"

# Subnet
  PublicSubnetA:
    Value: !Ref PublicSubnetA
    Export:
      Name: !Sub "${PJName}-public-subneta"
  PublicSubnetC:
    Value: !Ref PublicSubnetC
    Export:
      Name: !Sub "${PJName}-public-subnetc"
  PrivateSubnetA:
    Value: !Ref PrivateSubnetA
    Export:
      Name: !Sub "${PJName}-private-subneta"
  PrivateSubnetC:
    Value: !Ref PrivateSubnetC
    Export:
      Name: !Sub "${PJName}-private-subnetc"

2.VPC-B + VPC Peering 作成

VPC-Bの構築と併せて、VPC Peeringを構築します。
VPC Peeringを構築するために、接続先(ここではVPC-A)が必要となるため、構築の順番が2番目となっています。

AWSTemplateFormatVersion: "2010-09-09"
Description: 
  VPC,Subnet,IGW,RouteTable, Create

Metadata: 
  "AWS::CloudFormation::Interface": 
    ParameterGroups: 
      - Label: 
          default: "Project Name"
        Parameters: 
          - PJName
      - Label: 
          default: "Network Configuration"
        Parameters: 
          - VPCCIDR
          - PublicSubnetACIDR
          - PublicSubnetCCIDR
          - PrivateSubnetACIDR
          - PrivateSubnetCCIDR
          - UserName

    ParameterLabels: 
      PJName:
        default: "PJName"
      VPCCIDR: 
        default: "VPC CIDR"
      PublicSubnetACIDR: 
        default: "PublicSubnetA CIDR"
      PublicSubnetCCIDR: 
        default: "PublicSubnetC CIDR"
      PrivateSubnetACIDR: 
        default: "PrivateSubnetA CIDR"
      PrivateSubnetCCIDR: 
        default: "PrivateSubnetC CIDR"
      UserName: 
        default: "TagsValue UserName"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJName:
    Type: String
    Default: "cfn-network-B"

  VPCCIDR:
    Type: String
    Default: "192.168.0.0/16"

  PublicSubnetACIDR:
    Type: String
    Default: "192.168.10.0/24"

  PublicSubnetCCIDR:
    Type: String
    Default: "192.168.20.0/24"

  PrivateSubnetACIDR:
    Type: String
    Default: "192.168.30.0/24"

  PrivateSubnetCCIDR:
    Type: String
    Default: "192.168.40.0/24"

  UserName:
    Type: String 
    Default: "inamura"

Resources: 
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
# VPC Create
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-vpc"
        - Key: User
          Value: !Ref UserName

# InternetGateway Create
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-igw"
        - Key: User
          Value: !Ref UserName

# IGW Attach
  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#          
# Public SubnetA Create
  PublicSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-public-subnet-a"
        - Key: User
          Value: !Ref UserName

# Public SubnetC Create
  PublicSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PublicSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-public-subnet-c"
        - Key: User
          Value: !Ref UserName

# Private SubnetA Create
  PrivateSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-private-subnet-a"
        - Key: User
          Value: !Ref UserName

# Private SubnetC Create
  PrivateSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-private-subnet-c"
        - Key: User
          Value: !Ref UserName

# ------------------------------------------------------------#
#  RouteTable
# ------------------------------------------------------------#          
# Public RouteTableA Create
  PublicRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-public-routetable"
        - Key: User
          Value: !Ref UserName

# Private RouteTableA Create
  PrivateRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${PJName}-private-routetable"
        - Key: User
          Value: !Ref UserName

# ------------------------------------------------------------#
#  VPCPeering
# ------------------------------------------------------------#
  VPCPeer: 
    Type: AWS::EC2::VPCPeeringConnection
    Properties: 
      VpcId: !GetAtt VPC.VpcId
      PeerVpcId: !ImportValue cfn-network-A-vpc
      Tags:
        - Key: "User"
          Value: !Ref UserName

# ------------------------------------------------------------#
# Routing 
# ------------------------------------------------------------# 
# PublicRouteA Create
  PublicRoute: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicRouteTable 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

# PrivateRouteA Create  attach VPCPeering
  PrivateRoute:
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateRouteTable 
      DestinationCidrBlock: !ImportValue cfn-network-A-vpc-cidr
      VpcPeeringConnectionId: !GetAtt VPCPeer.Id

# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------# 
# PublicRouteTable Associate SubnetA
  PublicSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetA 
      RouteTableId: !Ref PublicRouteTable

# PublicRouteTable Associate SubnetC
  PublicSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetC 
      RouteTableId: !Ref PublicRouteTable

# PrivateRouteTable Associate SubnetA
  PrivateSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTable

# PrivateRouteTable Associate SubnetC
  PrivateSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetC
      RouteTableId: !Ref PrivateRouteTable

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#                
Outputs:
# VPC
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJName}-vpc"
  VPCCIDR:
    Value: !Ref VPCCIDR
    Export:
      Name: !Sub "${PJName}-vpc-cidr"

# Subnet
  PublicSubnetA:
    Value: !Ref PublicSubnetA
    Export:
      Name: !Sub "${PJName}-public-subneta"
  PublicSubnetC:
    Value: !Ref PublicSubnetC
    Export:
      Name: !Sub "${PJName}-public-subnetc"
  PrivateSubnetA:
    Value: !Ref PrivateSubnetA
    Export:
      Name: !Sub "${PJName}-private-subneta"
  PrivateSubnetC:
    Value: !Ref PrivateSubnetC
    Export:
      Name: !Sub "${PJName}-private-subnetc"

# VPCPeering
  VPCPeeringId:
    Value: !GetAtt VPCPeer.Id
    Export:
      Name: !Sub "${PJName}-private-vpcpeering"

3.VPC-A(ルートテーブル更新ver)作成

変更部分のみ抜粋
自身のPrivateRouteTableに対して、VPC-BのCIDRと、VPCPeering IDを参照して、ルートテーブルの更新を行います。

##### VPC Peering設定後にコメントアウトを削除、スタック削除の場合はこの部分をコメントアウトする#####
   PrivateRoute:
     Type: "AWS::EC2::Route"
     Properties: 
       RouteTableId: !Ref PrivateRouteTable
       DestinationCidrBlock: !ImportValue cfn-network-B-vpc-cidr
       VpcPeeringConnectionId: !ImportValue cfn-network-B-private-vpcpeering
#####ここまで###########################################################################

4.リソースの削除順番

4.1. 手順3.VPC-A(ルートテーブル更新ver)で更新した#で囲われている部分をコメントアウト又は、削除してCFnのスタックを更新します。
4.2. VPC-BのCFnスタックを削除する
4.3. VPC-AのCFnスタックを削除する

挙動の確認

①VPCピアリング接続を確認して、各ルートテーブルに接続されていることが確認できる

単体でVPCPeeringを構築する際のCFn

上記VPCに含めずVPC Peeringのみを構築する際のCFn
ルートテーブルに含めるなどの手動での作業が必要

AWSTemplateFormatVersion: "2010-09-09"
Description: VPC Peering Inviter Create
# ------------------------------------------------------------#
#  Metadata
# ------------------------------------------------------------#
Metadata: 
  "AWS::CloudFormation::Interface": 
    ParameterGroups: 
      - Label: 
          default: "VPCPeering Configuration"
        Parameters: 
          - UserName

    ParameterLabels: 
      UserName: 
        default: "TagsValue UserName"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  UserName:
    Type: String
    Default: "inamura"

# ------------------------------------------------------------#
#  Resources
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
#  VPCPeering
# ------------------------------------------------------------#
  VPCPeer: 
    Type: AWS::EC2::VPCPeeringConnection
    Properties: 
      VpcId: !ImportValue cfn-network-A-vpc
      PeerVpcId: !ImportValue cfn-network-B-vpc
      Tags:
        - Key: "User"
          Value: !Ref UserName

さいごに

これで月曜の朝にギリギリに起きてもサックリ構築できますし、ルートテーブルの設定を見落として、1人で「ほげっ?」となる心配も少しは減るかと思います。
CloudFormation を使用して VPC 内のメインルートテーブルにルートを追加するにはどうすればよいですか?などを参考にして、ルートテーブルをLambdaで追加させる構築するのが根本的な解決になるかもしれないですが、さっくりやる分には自分はこれくらいで良さそうです。

Last modified: 2022-12-24

Author