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

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

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

------------

## 構成図
![](https://cloud5.jp/wp-content/uploads/2022/12/スクリーンショット-2022-12-24-21.07.40-640x285.png)

------------

## ハンズオン
### 構築の流れ
#### 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 Peering`は`1.VPC-A`を参照することで`VPC Peering`の構築を行うため、削除の順番は`3.VPC-A(ルートテーブル修正ver)`で修正した部分を削除しないと、下記画像のような表示が出て、スタックを削除することが出来ません。
![](https://cloud5.jp/wp-content/uploads/2022/12/スクリーンショット-2022-12-24-18.36.05-1-640x49.png)
#### 上記の順番で構築を行なっていきます。
最終的にピアリング接続のルートテーブルに、各VPCのルートテーブル同士が下記画像のように2つのルートテーブルが表示されれば、それぞれが接続されていることになります。
![](https://cloud5.jp/wp-content/uploads/2022/12/スクリーンショット-2022-12-24-18.39.28-640x117.png)

-----------

### 1.VPC-A作成
まずは`VPC-A`を構築します。
後続の手順`VPC-B + VPC Peering`を構築した後に、更新する部分はコメントアウトしている（`PrivateRouteA Create`）部分です。

```yaml
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番目となっています。

```yaml
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`を参照して、ルートテーブルの更新を行います。

```yaml
##### 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ピアリング接続を確認して、各ルートテーブルに接続されていることが確認できる
![](https://cloud5.jp/wp-content/uploads/2022/12/スクリーンショット-2022-12-24-18.39.28-1-640x117.png)


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

```yaml
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 内のメインルートテーブルにルートを追加するにはどうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudformation-route-table-vpc/)などを参考にして、ルートテーブルをLambdaで追加させる構築するのが根本的な解決になるかもしれないですが、さっくりやる分には自分はこれくらいで良さそうです。