最近ガバメントクラウド関係の業務で CloudFormation のテンプレートを読む機会がありました。
そこで、勉強がてら、これまでガバメントクラウド環境の勉強としてよく作ってきた閉域 VPC や Transit Gateway (TGW)、Route 53 インバウンドエンドポイント・アウトバウンドエンドポイント辺りの検証環境を CloudFormation で一気にデプロイできないかと思い立ったので、早速やってみました。
CloudFormation で構築する閉域 VPC 環境の概要
今回 CloudFormation で作成するリソースの構成図は以下のとおりです。赤点線の枠で囲った範囲を今回 CloudFormation で作成してみます。
その他今回作成する環境の概要は次のとおりです。
- ガバメントクラウドを想定した検証環境のため、マルチアカウント(アカウント A、B とします)間で VPC を接続します。
- アカウント A 側には既に VPC があり、パブリックサブネットにある VPN インスタンス経由でオンプレミス(自宅)の構築済み DNS サーバーと接続されています。
- アカウント A 側に CloudFormation で TGW、プライベートサブネット、Route 53 インバウンドエンドポイント、アウトバウンドエンドポイントを作成します。
- アカウント B には一から VPC を作成し、TGW アタッチメントでアカウント A の VPC やオンプレミスに接続します。
- マルチアカウント構成のため、途中 TGW や Route 53 リゾルバールールを CloudFormation ではなく RAM で直接共有をかけているので、これを想定して CloudFormation スタックは一発でリソースを作るのではなく、段階ごとに分けてリソースを作成するようにしています。
構築作業の流れ
- CloudFormation でアカウント A 側に TGW を作成し、既存 VPC と接続
- アカウント A の TGW を Resource Access Manager (RAM) でアカウント B へ共有
- アカウント B の RAM で TGW の共有を承認
- アカウント B の CloudFormation で VPC と DHCP オプションセットを作成し、共有された TGW にアタッチメントを作成
- アカウント A の CloudFormation でアカウント B のサブネットへ向かう VPC ルートテーブル、TGW ルートテーブルを作成(アカウント B の TGW アタッチメントが作成された後)
- アカウント A の CloudFormation で Route 53 インバウンドエンドポイント、アウトバウンドエンドポイント、作成したアウトバウンドエンドポイント経由でオンプレミス側のドメインへの名前解決をオンプレミス DNS サーバーへ向けるリゾルバールールを作成
- アカウント A のリゾルバールールを RAM でアカウント B へ共有
- アカウント B の RAM で共有されたリゾルバールールを承認
- アカウント A のCloudFormation で既存 VPC に関連付ける Route 53 プライベートホストゾーンを作成
- アカウント B の VPC に任意のインスタンスを作成し、オンプレミスのドメイン、アカウント A の Route 53 プライベートホストゾーンのドメインを名前解決できればゴール
それでは作成した CloudFormation テンプレートを見ていきます。
アカウント A の VPC に TGW などを作成
CloudFormation テンプレート
まず最初に流す CloudFormation テンプレートは以下のとおりです。アカウント A の既存 VPC に TGW などを作成していきます。
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a Subnet, with a pair of private subnets spread across two Availability Zones.
Parameters:
# TGW アタッチメントを接続するサブネット
PrivateTGWCidr:
Description: Please enter the IP range (CIDR notation) for the TGW subnet in the first Availability Zone
Type: String
Default: 10.128.16.0/20
PrivateSubnet1Cidr:
Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
Type: String
Default: 10.128.128.0/20
PrivateSubnet2Cidr:
Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
Type: String
Default: 10.128.144.0/20
# 既存の VPC にリソースを作成するため、あらかじめ VPC ID をパラメーターへセット
VPCId:
Description: Please enter the VPC ID
Type: AWS::EC2::VPC::Id
Default: vpc-hogehoge123
TGWAsn:
Description: Please enter the ASN for TGW
Type: Number
Default: 65531
OnpremissCidr:
Description: Please enter the Onpremiss CIDR
Type: String
Default: 10.0.0.0/8
# オンプレミスと VPN 接続している EC2 インスタンスのネットワークインターフェース ID を
# オンプレミスへのルートテーブル用にパラメーターへセット
VPNApplanceENIId:
Description: Please enter the ENI ID that connecting to onpremiss by VPN
Type: String
Default: eni-fugafuga456
CostTagValue:
Type: String
Description: Set CostTag Value
Default: testtag01
Resources:
#
# VPC サブネットの作成
#
PrivateTGWSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPCId
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Ref PrivateTGWCidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test01-dev-subnet-private-ns-az1
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPCId
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Ref PrivateSubnet1Cidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test01-dev-subnet-private-endpoint-az1
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPCId
AvailabilityZone: !Select [1, !GetAZs ""]
CidrBlock: !Ref PrivateSubnet2Cidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test01-dev-subnet-private-endpoint-az2
#
# VPC ルートテーブルの作成
#
PrivateTGWSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPCId
Tags:
- Key: Name
Value: test01-dev-rtb-private-ns-az1
PrivateTGWSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateTGWSubnetRouteTable
SubnetId: !Ref PrivateTGWSubnet
PrivateTGWSubnetRouteTableRoute1:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
RouteTableId: !Ref PrivateTGWSubnetRouteTable
NetworkInterfaceId: !Ref VPNApplanceENIId
PrivateSubnet1RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPCId
Tags:
- Key: Name
Value: test01-dev-rtb-private-endpoint-az1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateSubnet1RouteTable
SubnetId: !Ref PrivateSubnet1
PrivateSubnet1RouteTableRoute1:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
RouteTableId: !Ref PrivateSubnet1RouteTable
NetworkInterfaceId: !Ref VPNApplanceENIId
PrivateSubnet2RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPCId
Tags:
- Key: Name
Value: test01-dev-rtb-private-endpoint-az2
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateSubnet2RouteTable
SubnetId: !Ref PrivateSubnet2
PrivateSubnet2RouteTableRoute1:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
RouteTableId: !Ref PrivateSubnet2RouteTable
NetworkInterfaceId: !Ref VPNApplanceENIId
#
# Transit Gateway の作成
#
# 別アカウントからの TGW アタッチメントの自動承認は
# 本当は無効にしたいが、有効にしないと、
# 手動で承認しないと別アカウントで TGW アタッチメントを作成する
# CloudFormation スタックが失敗してしまうため、
# 敢えて自動承認を有効とした。
#
TGW:
Type: AWS::EC2::TransitGateway
Properties:
AmazonSideAsn: !Ref TGWAsn
AutoAcceptSharedAttachments: enable
DefaultRouteTableAssociation: disable
DefaultRouteTablePropagation: disable
Tags:
- Key: Name
Value: test01-dev-tgw
TGWAttachment1:
Type: AWS::EC2::TransitGatewayAttachment
Properties:
Options:
DnsSupport: enable
SecurityGroupReferencingSupport: enable
SubnetIds:
- !Ref PrivateTGWSubnet
Tags:
- Key: Name
Value: test01-dev-tgw-attach-vpc-tokyo
- Key: CostTag
Value:
Ref: CostTagValue
TransitGatewayId: !Ref TGW
VpcId: !Ref VPCId
# アカウント A VPC 用の TGW ルートテーブル
TGWRouteTable1:
Type: AWS::EC2::TransitGatewayRouteTable
DependsOn: TGWAttachment1
Properties:
Tags:
- Key: Name
Value: test01-dev-tgw-rtb-ns-ew
TransitGatewayId: !Ref TGW
TGWRouteTable1Route1:
Type: AWS::EC2::TransitGatewayRoute
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
TransitGatewayAttachmentId: !Ref TGWAttachment1
TransitGatewayRouteTableId: !Ref TGWRouteTable1
TGWRouteTable1Association1:
Type: AWS::EC2::TransitGatewayRouteTableAssociation
Properties:
TransitGatewayAttachmentId: !Ref TGWAttachment1
TransitGatewayRouteTableId: !Ref TGWRouteTable1
TGWRouteTable1Propagation1:
Type: AWS::EC2::TransitGatewayRouteTablePropagation
Properties:
TransitGatewayAttachmentId: !Ref TGWAttachment1
TransitGatewayRouteTableId: !Ref TGWRouteTable1
#
# S3 ゲートウェイエンドポイントの作成
#
# S3 ゲートウェイエンドポイントは作成しておいた方が何かと便利
#
S3GatewayEndpoint:
Type: 'AWS::EC2::VPCEndpoint'
Properties:
VpcEndpointType: 'Gateway'
VpcId: !Ref VPCId
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
RouteTableIds:
- !Ref PrivateSubnet1RouteTable
- !Ref PrivateSubnet2RouteTable
#
# 別アカウントの VPC へのルートテーブルを作成する後続のスタックに渡したい値を出力
#
Outputs:
TGW:
Value: !Ref TGW
Export:
Name: TGW
TGWAttachment1:
Value: !Ref TGWAttachment1
Export:
Name: TGWAttachment1
PrivateTGWSubnetRouteTable:
Value: !Ref PrivateTGWSubnetRouteTable
Export:
Name: PrivateTGWSubnetRouteTable
PrivateSubnet1RouteTable:
Value: !Ref PrivateSubnet1RouteTable
Export:
Name: PrivateSubnet1RouteTable
PrivateSubnet2RouteTable:
Value: !Ref PrivateSubnet2RouteTable
Export:
Name: PrivateSubnet2RouteTable
TGWRouteTable1:
Value: !Ref TGWRouteTable1
Export:
Name: TGWRouteTable1
テンプレートから CloudFormation スタックを作成
作成したテンプレートをアップロードして CloudFormation スタックを作成します。
パラメーターで設定値を変えられるようにしています。
スタックの作成に成功するとテンプレートで指定したリソースが作成されます。TGW の ID を確認して RAM へ移ります。
RAM でアカウント B へ TGW を共有
RAM のリソース共有へ進み、作成された TGW を選択します。
アカウント B へ TGW を共有します。
アカウント B の RAM で TGW の共有を承認
アカウント B 側の RAM で共有された TGW を承認します。これでアカウント A の TGW にアタッチメントを作成できます。
アカウント B に VPC と TGW アタッチメントを作成
CloundFormation テンプレート
次にアカウント B の VPC と、TGW アタッチメントを作成します。
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a VPC, with a pair of private subnets spread across two Availability Zones.
Parameters:
EnvironmentName:
Description: An environment name that is prefixed to resource names
Type: String
Default: test02-dev-vpc
VPCCidr:
Description: Please enter the IP range (Cidr notation) for this VPC
Type: String
Default: 10.129.0.0/16
# TGW 用のサブネット
PrivateTGWCidr:
Description: Please enter the IP range (Cidr notation) for the TGW subnet in the first Availability Zone
Type: String
Default: 10.129.16.0/20
PrivateSubnet1Cidr:
Description: Please enter the IP range (Cidr notation) for the private subnet in the first Availability Zone
Type: String
Default: 10.129.128.0/20
PrivateSubnet2Cidr:
Description: Please enter the IP range (Cidr notation) for the private subnet in the second Availability Zone
Type: String
Default: 10.129.144.0/20
OnpremissCidr:
Description: Please enter the Onpremiss CIDR
Type: String
Default: 10.0.0.0/8
# Transit Gateway はアカウント A のスタックで作成しているため手作業で値をパラメーターへセット
TGWId:
Description: Please enter the TGW ID
Type: String
VPC2Cidr:
Description: Please enter the other VPC CIDR
Type: String
Default: 10.128.0.0/16
LocalDomainName:
Description: Please enter the local domain name in this VPC
Type: String
Default: test02.intra.morori.jp
# Route 53 インバウンドエンドポイントで使う IP アドレスは予め決めておきパラメーターへセット
# DHCP オプションセットに使うため
Route53InboundEndpointIpAddress1:
Description: Please enter the IP Address for the first Route 53 Inbound Endpoint Address
Type: String
Default: 10.128.128.50
Route53InboundEndpointIpAddress2:
Description: Please enter the IP Address for the second Route 53 Inbound Endpoint Address
Type: String
Default: 10.128.144.50
CostTagValue:
Type: String
Description: Set CostTag Value
Default: testtag02
Resources:
#
# VPC の作成
#
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidr
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref EnvironmentName
#
# サブネットの作成
#
PrivateTGWSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Ref PrivateTGWCidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test02-dev-subnet-private-ns-az1
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Ref PrivateSubnet1Cidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test02-dev-subnet-private-server-az1
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs ""]
CidrBlock: !Ref PrivateSubnet2Cidr
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: test02-dev-subnet-private-server-az2
#
# VPC ルートテーブルの作成
#
PrivateTGWSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: test02-dev-rtb-private-ns-az1
PrivateTGWSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateTGWSubnetRouteTable
SubnetId: !Ref PrivateTGWSubnet
PrivateTGWSubnetRouteTableRoute1:
Type: AWS::EC2::Route
DependsOn: TGWAttachment1
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
RouteTableId: !Ref PrivateTGWSubnetRouteTable
TransitGatewayId: !Ref TGWId
PrivateTGWSubnetRouteTableRoute2:
Type: AWS::EC2::Route
DependsOn: TGWAttachment1
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !Ref PrivateTGWSubnetRouteTable
TransitGatewayId: !Ref TGWId
PrivateSubnet1RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: test02-dev-rtb-private-server-az1
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateSubnet1RouteTable
SubnetId: !Ref PrivateSubnet1
# オンプレミス宛てのルートテーブルはネクストホップを TGW とする
PrivateSubnet1RouteTableRoute1:
Type: AWS::EC2::Route
DependsOn: TGWAttachment1
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
RouteTableId: !Ref PrivateSubnet1RouteTable
TransitGatewayId: !Ref TGWId
# アカウント A の VPC 宛てのルートテーブルはネクストホップを TGW とする
PrivateSubnet1RouteTableRoute2:
Type: AWS::EC2::Route
DependsOn: TGWAttachment1
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !Ref PrivateSubnet1RouteTable
TransitGatewayId: !Ref TGWId
PrivateSubnet2RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: test02-dev-rtb-private-server-az2
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateSubnet2RouteTable
SubnetId: !Ref PrivateSubnet2
PrivateSubnet2RouteTableRoute1:
Type: AWS::EC2::Route
DependsOn: TGWAttachment1
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
RouteTableId: !Ref PrivateSubnet2RouteTable
TransitGatewayId: !Ref TGWId
PrivateSubnet2RouteTableRoute2:
Type: AWS::EC2::Route
DependsOn: TGWAttachment1
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !Ref PrivateSubnet2RouteTable
TransitGatewayId: !Ref TGWId
#
# TGW アタッチメントを作成
#
TGWAttachment1:
Type: AWS::EC2::TransitGatewayAttachment
Properties:
Options:
DnsSupport: enable
SecurityGroupReferencingSupport: enable
SubnetIds:
- !Ref PrivateTGWSubnet
Tags:
- Key: Name
Value: test02-dev-tgw-attach-vpc-tokyo
- Key: CostTag
Value:
Ref: CostTagValue
TransitGatewayId: !Ref TGWId
VpcId: !Ref VPC
#
# DHCP オプションセットを作成
#
# DNS 参照先をアカウント A VPC の Route 53 インバウンドエンドポイントに設定する
DHCPOptionSet:
Type: AWS::EC2::DHCPOptions
Properties:
DomainName: !Ref LocalDomainName
DomainNameServers:
- !Ref Route53InboundEndpointIpAddress1
- !Ref Route53InboundEndpointIpAddress2
Tags:
- Key: Name
Value: test02-dev-dhcpoptionset
DHCPOptionSetAssociation:
Type: AWS::EC2::VPCDHCPOptionsAssociation
Properties:
DhcpOptionsId: !Ref DHCPOptionSet
VpcId: !Ref VPC
#
# S3 ゲートウェイエンドポイントを作成
#
S3GatewayEndpoint:
Type: 'AWS::EC2::VPCEndpoint'
Properties:
VpcEndpointType: 'Gateway'
VpcId: !Ref VPC
ServiceName: !Sub 'com.amazonaws.${AWS::Region}.s3'
RouteTableIds:
- !Ref PrivateSubnet1RouteTable
- !Ref PrivateSubnet2RouteTable
アカウント B でテンプレートから CloudFormation スタックを作成
テンプレートのパラメーターのうち TGW ID については、アカウント A で作成した TGW の ID を手作業でセットします。
アカウント B の VPC と TGW アタッチメントが作成できたら、アカウント A の VPC にこれらへ向けたルートテーブルを作成していきます。
アカウント A の VPC ルートテーブル、TGW ルートテーブルを作成
CloudFormation テンプレートは以下のとおりです。
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a route table for other vpc is connected by TGW
Parameters:
VPC1Cidr:
Description: Please enter the VPC CIDR
Type: String
Default: 10.128.0.0/16
VPC2Cidr:
Description: Please enter the other VPC CIDR
Type: String
Default: 10.129.0.0/16
OnpremissCidr:
Description: Please enter the Onpremiss CIDR
Type: String
Default: 10.0.0.0/8
# オンプレミスに VPN 接続している EC2 インスタンスがあるパブリックサブネットに
# アカウント B VPC へのルートテーブルを追加するため値をセット
VPNSubnetRouteTable:
Description: Please enter vpn subnet which is conneceted to the onpremiss route table ID
Type: String
Default: rtb-hogefuga789
# 別アカウントの TGW アタッチメント ID は手動でセット
TGWAttachment2:
Description: Please enter the other TGW Attachment ID
Type: String
Resources:
#
#アカウント B VPC へのルートを各 VPC ルートテーブルへ追加
#
VPNSubnetRouteTableRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !Ref VPNSubnetRouteTable
TransitGatewayId: !ImportValue TGW
PrivateTGWSubnetRouteTableRoute2:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !ImportValue PrivateTGWSubnetRouteTable
TransitGatewayId: !ImportValue TGW
PrivateSubnet1RouteTableRoute2:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !ImportValue PrivateSubnet1RouteTable
TransitGatewayId: !ImportValue TGW
PrivateSubnet2RouteTableRoute2:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
RouteTableId: !ImportValue PrivateSubnet2RouteTable
TransitGatewayId: !ImportValue TGW
#
# アカウント B VPC へのルートを TGW ルートテーブルへ追加
#
TGWRouteTable1Route2:
Type: AWS::EC2::TransitGatewayRoute
Properties:
DestinationCidrBlock: !Ref VPC2Cidr
TransitGatewayAttachmentId: !Ref TGWAttachment2
TransitGatewayRouteTableId: !ImportValue TGWRouteTable1
#
# アカウント B VPC 用の TGW ルートテーブルを作成
#
TGWRouteTable2:
Type: AWS::EC2::TransitGatewayRouteTable
Properties:
Tags:
- Key: Name
Value: test02-dev-tgw-rtb-ns-ew
TransitGatewayId: !ImportValue TGW
TGWRouteTable2Route1:
Type: AWS::EC2::TransitGatewayRoute
Properties:
DestinationCidrBlock: !Ref OnpremissCidr
TransitGatewayAttachmentId: !ImportValue TGWAttachment1
TransitGatewayRouteTableId: !Ref TGWRouteTable2
TGWRouteTable2Route2:
Type: AWS::EC2::TransitGatewayRoute
Properties:
DestinationCidrBlock: !Ref VPC1Cidr
TransitGatewayAttachmentId: !ImportValue TGWAttachment1
TransitGatewayRouteTableId: !Ref TGWRouteTable2
TGWRouteTable2Association1:
Type: AWS::EC2::TransitGatewayRouteTableAssociation
Properties:
TransitGatewayAttachmentId: !Ref TGWAttachment2
TransitGatewayRouteTableId: !Ref TGWRouteTable2
TGWRouteTable2Propagation1:
Type: AWS::EC2::TransitGatewayRouteTablePropagation
Properties:
TransitGatewayAttachmentId: !Ref TGWAttachment2
TransitGatewayRouteTableId: !Ref TGWRouteTable2
先に TGW などを作成したスタックから必要な情報を出力して取り込んでいるので、パラメーターは先ほどより少なくなっています。
ただし、アカウント B の TGW アタッチメントの ID は手動でセットするようになっています。
アカウント A VPC に Route 53 Resolver などを作成
CloudFormation テンプレートは以下のとおりです。
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a Route 53 Resolvers.
Parameters:
VPCId:
Description: Please enter the VPC ID
Type: AWS::EC2::VPC::Id
Default: vpc-hogehoge123
# Route 53 Resolver を作成するサブネットを指定
PrivateSubnet1:
Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
Type: AWS::EC2::Subnet::Id
PrivateSubnet2:
Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
Type: AWS::EC2::Subnet::Id
Route53InboundEndpointIpAddress1:
Type: String
Description: Please enter the IP Address for the first Route 53 Inbound Endpoint Address
Default: 10.128.128.50
Route53InboundEndpointIpAddress2:
Type: String
Description: Please enter the IP Address for the second Route 53 Inbound Endpoint Address
Default: 10.128.144.50
Route53OutboundEndpointIpAddress1:
Type: String
Description: Please enter the IP Address for the first Route 53 Inbound Endpoint Address
Default: 10.128.128.51
Route53OutboundEndpointIpAddress2:
Type: String
Description: Please enter the IP Address for the second Route 53 Inbound Endpoint Address
Default: 10.128.144.51
OnpremissCidr:
Description: Please enter the Onpremiss CIDR
Type: String
Default: 10.0.0.0/8
# オンプレミス側のリゾルバー DNS サーバーの IP アドレスを指定
OnpremissResolverIpAddress:
Type: String
Description: Please enter the IP Address for the onpremiss resolover Address
Default: 10.1.1.3
CostTagValue:
Type: String
Description: Set CostTag Value
Default: testtag01
Resources:
#
# Route 53 インバウンドエンドポイントの作成
#
Route53InboundEndpoint:
Type: AWS::Route53Resolver::ResolverEndpoint
Properties:
Direction: INBOUND
IpAddresses:
- Ip: !Ref Route53InboundEndpointIpAddress1
SubnetId: !Ref PrivateSubnet1
- Ip: !Ref Route53InboundEndpointIpAddress2
SubnetId: !Ref PrivateSubnet2
Name: test01-dev-vpce-route53inbound-interface
Protocols:
- Do53
ResolverEndpointType: IPV4
SecurityGroupIds:
- !Ref AllowDNSSecurityGroup
Tags:
- Key: CostTag
Value:
Ref: CostTagValue
#
# Route 53 アウトバウンドエンドポイントの作成
#
Route53OutboundEndpoint:
Type: AWS::Route53Resolver::ResolverEndpoint
Properties:
Direction: OUTBOUND
IpAddresses:
- Ip: !Ref Route53OutboundEndpointIpAddress1
SubnetId: !Ref PrivateSubnet1
- Ip: !Ref Route53OutboundEndpointIpAddress2
SubnetId: !Ref PrivateSubnet2
Name: test01-dev-vpce-route53outbound-interface
Protocols:
- Do53
ResolverEndpointType: IPV4
SecurityGroupIds:
- !Ref AllowDNSSecurityGroup
Tags:
- Key: CostTag
Value:
Ref: CostTagValue
#
# UDP 53, TCP 53 を許可するエンドポイント用のセキュリティグループを作成
#
AllowDNSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: test01-dev-sg-route53resolver-dns-vpce
GroupDescription: Security group with allowing DNS query rule
SecurityGroupIngress:
- CidrIp: !Ref OnpremissCidr
FromPort: 53
IpProtocol: udp
ToPort: 53
- CidrIp: !Ref OnpremissCidr
FromPort: 53
IpProtocol: tcp
ToPort: 53
VpcId: !Ref VPCId
#
# リゾルバールールの作成
#
# オンプレミスのドメインの名前解決要求をアウトバウンドエンドポイント経由で
# オンプレミスのリゾルバー DNS サーバーへ転送する
Route53ResolverRule:
Type: AWS::Route53Resolver::ResolverRule
Properties:
DomainName: intra.morori.jp
Name: test01-dev-resolverrule-onpremiss
ResolverEndpointId: !Ref Route53OutboundEndpoint
RuleType: FORWARD
TargetIps:
-
Ip: !Ref OnpremissResolverIpAddress
Port: 53
Route53ResolverRuleAssociation:
Type: AWS::Route53Resolver::ResolverRuleAssociation
Properties:
Name: test01-dev-route53resolverruleassociation
ResolverRuleId: !Ref Route53ResolverRule
VPCId: !Ref VPCId
リゾルバールールをアカウント B へ RAM で共有する
作成したリゾルバールールは RAM でアカウント B へ共有します。手順は割愛します。
Route 53 プライベートホストゾーンを作成しアカウント A VPC に関連付ける
CloudFormation テンプレートは以下のとおりです。
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a Route 53 Private Hosted Zone.
Parameters:
VPCId:
Description: Please enter the VPC ID you will associate with
Type: AWS::EC2::VPC::Id
PrivateDomainName:
Description: Please enter the private domain name
Type: String
Default: test01.intra.morori.jp
Record1FQDN:
Description: Please enter the first record's FQDN
Type: String
Default: dummy.test01.intra.morori.jp.
Record1IpAddress:
Description: Please enter the first record's IP Address
Type: String
Default: 10.128.128.52
Resources:
PrivateHostedZone:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref PrivateDomainName
VPCs:
- VPCId: !Ref VPCId
VPCRegion: !Ref AWS::Region
RecordSet1:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref PrivateHostedZone
Name: !Ref Record1FQDN
ResourceRecords:
- !Ref Record1IpAddress
TTL: 900
Type: A
アカウント B の VPC から TGW と Route 53 Resolver への疎通を確認
期待どおりに環境ができていれば、アカウント B の VPC から DHCP オプションセットで DNS は Route 53 インバウンドエンドポイントを参照し、TGW 経由でルーティングしてアクセスできるはずで、また、オンプレミスのドメインはリゾルバールールでアウトバウンドエンドポイントを経由してオンプレミスの DNS サーバーへ名前解決要求がルーティングして到達するはずです。
確認のため、アカウント B の VPC に EC2 インスタンスを立ててみます。これも CloudFormation
でやってみます。
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a EC2 instance.
Parameters:
# Amazon Linx 2023 の最新の AMI ID を自動で取得する
AmiId:
Description: Please enter the AMI ID for this intance
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64
InstanceType:
Description: Please enter the instance type for this intance
Type: String
Default: t4g.nano
InstanceName:
Description: Please enter the instance name
Type: String
Default: test02-dev-ec2-test-ap
KeyName:
Description: Please enter the key-pair name
Type: String
Default: test02
NetworkInterfaceSubnetId:
Description: Please enter the subnet id for this instance's network interface
Type: AWS::EC2::Subnet::Id
InstanceIpAddress:
Description: Please enter the IP Address for this instance
Type: String
Default: 10.129.128.101
VPCId:
Description: Please enter the VPC ID
Type: AWS::EC2::VPC::Id
OnpremissCidr:
Description: Please enter the Onpremiss CIDR
Type: String
Default: 10.0.0.0/8
CostTagValue:
Type: String
Description: Set CostTag Value
Default: testtag02
Resources:
#
# EC2 インスタンスの作成
#
TestInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref AmiId
InstanceType: !Ref InstanceType
KeyName: !Ref KeyName
NetworkInterfaces:
- AssociatePublicIpAddress: false
DeleteOnTermination: yes
DeviceIndex: 0
GroupSet:
- !Ref EC2SecurityGroup
SubnetId: !Ref NetworkInterfaceSubnetId
PrivateIpAddress: !Ref InstanceIpAddress
Tags:
- Key: Name
Value: !Ref InstanceName
- Key: CostTag
Value: !Ref CostTagValue
#
# EC2 インスタンス用のセキュリティグループを作成
#
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: test01-dev-sg-test-ap-ec2
GroupDescription: Security group with allowing all ICMP and SSH rule
SecurityGroupIngress:
- CidrIp: !Ref OnpremissCidr
FromPort: -1
IpProtocol: icmp
ToPort: -1
- CidrIp: !Ref OnpremissCidr
FromPort: 22
IpProtocol: tcp
ToPort: 22
VpcId: !Ref VPCId
インスタンスが作成できたら、早速オンプレミスのドメイン (intra.morori.jp) と、アカウント A の VPC に関連付けられている Route 53 プライベートホストゾーンのドメイン (test01.intra.morori.jp) の名前解決を試してみます。
これで TGW も Route 53 Resolver も疎通できていることが確認できました。
スタックを削除してリソースを全削除する
今回作成した CloudFormation テンプレートでは、スタック削除時にリソースを残さない設定にしているため、スタックを削除することでテンプレートから作成したリソースは全て削除できます。
TGW アタッチメントや Route 53 Reslover は料金が高いため、検証が終わったらすぐ削除したいですし、また、もし削除を忘れてしまうと高額な課金に繋がるため、作成したリソースを一気に削除できるのはありがたいです。
まとめ
このように検証環境を CloudFormation で作ると、短時間で環境を再現したり、使い終わった環境を簡単に削除できるようになります。
また、CloudFormation のユーザーズガイドを読むと、各リソースの詳細なオプションが確認できるため、とても勉強になりますのでおすすめです。
最後に、今回の環境で使っているオンプレミス環境からの VPN 接続や DNS サーバーの構築方法を、過去の記事にまとめていますので、良かったら参考にしてください。