パブリックサブネットとプライベートサブネットを一つずつもつ VPC を CloudFormation を使って構築する。
プライベートサブネットからインターネットにアクセスできるように NAT ゲートウェイを利用する。
AWS CloudFormation は AWS CLI で操作する。
以降では、段階的に構築していく手順を示す。
途中の手順を省いて完成したテンプレートをみたい方はこちら。
- 構成図
- VPC を作成
- パブリックサブネットを作成
- スタックに変更を適用してサブネットを作成
- ルートテーブルを作成してインターネットへのルートを登録
- プライベートサブネットを作成
- ルートテーブルを作成して NAT ゲートウェイ を登録
- 最終的なテンプレート
- スタックを削除
- 参考
まずはシンプルに VPC だけを作成するテンプレート。
1
2
3
4
5
6
7
8
|
AWSTemplateFormatVersion: "2010-09-09"
Resources:
VPC: # Logical ID. Unique in template
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true # Default true
EnableDnsHostnames: true # EC2 インスタンスが DNS ホスト名を取得するかどうか。 Default false
|
Resources
以下に作成するリソースを定義していく。
これを template.yaml に保存する。
まずは次のコマンドを実行して CloudFormation のスタックを作成する。
1
|
$ aws cloudformation create-stack --stack-name sample-vpc --template-body file://template.yaml
|
次のコマンドでスタックの状態を確認する。
1
|
$ aws cloudformation describe-stacks --stack-name sample-vpc
|
次のような出力が得られるはず。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/sample-vpc/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"StackName": "sample-vpc",
"CreationTime": "2020-02-23T07:10:51.420000+00:00",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
|
StackStatus
が CREATE_COMPLETE
になっていれば作成完了 。
CREATE_IN_PROGRESS
あればまだ作成中。
パブリックサブネットを追加するには先ほど作成した template.yaml の Resources
以下に次のパブリックサブネットの定義を追加する。
1
2
3
4
5
6
7
8
|
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: true # このサブネットで起動されたインスタンスがパブリック IPv4 アドレスを受け取るかどうか
|
VPC 定義と合わせたテンプレート template.yaml は次のようになる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
AWSTemplateFormatVersion: "2010-09-09"
Resources:
VPC: # Logical ID. Unique in template
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true # Default true
EnableDnsHostnames: true # EC2 インスタンスが DNS ホスト名を取得するかどうか。 Default false
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: true # このサブネットで起動されたインスタンスがパブリック IPv4 アドレスを受け取るかどうか
|
この変更を適用するために、まず変更セットを作成する。
1
|
$ changeset_id=$(aws cloudformation create-change-set --stack-name sample-vpc --template-body file://template.yaml --change-set-name change-$(date +%Y%m%d-%H%M%S) --query 'Id')
|
変更セットを作成しても、変更はまだ適用されない。
次のコマンドで実際に変更が適用されてパブリックサブネットが作成される。
1
|
$ aws cloudformation execute-change-set --change-set-name ${changeset_id//\"}
|
スタックを作成したときと同様にスタックの状態を確認する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
$ aws cloudformation describe-stacks --stack-name sample-vpc
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/sample-vpc/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"StackName": "sample-vpc",
"ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:changeSet/change-20200223-162707/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"CreationTime": "2020-02-23T07:10:51.420000+00:00",
"LastUpdatedTime": "2020-02-23T07:35:10.450000+00:00",
"RollbackConfiguration": {},
"StackStatus": "UPDATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
|
StackStatus
が UPDATE_COMPLETE
になっていれば変更完了。
まだ変更適用中であれば UPDATE_IN_PROGRESS
になる。
ルートテーブルを作成してインターネットへのルートを登録
パブリックサブネットからインターネットにアクセスできるようにするには、
パブリックサブネットに関連付けられたルートテーブルにインターネットゲートウェイを経由してインターネットにアクセスするルートを設定する必要がある。
VPC には、作成時に自動的に作成されるメインルートテーブルが存在する。
CloudFormation でメインルートテーブルにルートを追加するには Lambda-backed カスタムリソースを利用する必要がある。
参考
今回はルートテーブルを別途作成してそちらを利用する。
すなわち CloudFormation で以下のことを行う。
- ルートテーブルを作成
- インターネットゲートウェイを作成
- インターネットゲートウェイを経由したルートをルートテーブルに登録
- 作成したルートテーブルをパブリックサブネットに関連付け
これを行うには template.yaml の Resources
に次の定義を追加する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
|
template.yaml に変更を加えたら、変更セットを作成し、適用する。
1
2
|
$ changeset_id=$(aws cloudformation create-change-set --stack-name sample-vpc --template-body file://template.yaml --change-set-name change-$(date +%Y%m%d-%H%M%S) --query 'Id')
$ aws cloudformation execute-change-set --change-set-name ${changeset_id//\"}
|
aws cloudformation describe-stacks --stack-name sample-vpc
の結果で StackStatus
が UPDATE_COMPLETE
になれば完了。
パブリックサブネットを作成したときと同様にしてプライベートサブネットを作成する。
CidrBlock
と MapPublicIpOnLaunch
だけ変更する。
1
2
3
4
5
6
7
8
|
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: false # このサブネットで起動されたインスタンスがパブリック IPv4 アドレスを受け取るかどうか
|
変更セットを作成して適用する。
1
2
|
$ changeset_id=$(aws cloudformation create-change-set --stack-name sample-vpc --template-body file://template.yaml --change-set-name change-$(date +%Y%m%d-%H%M%S) --query 'Id')
$ aws cloudformation execute-change-set --change-set-name ${changeset_id//\"}
|
aws cloudformation describe-stacks --stack-name sample-vpc
の結果で StackStatus
が UPDATE_COMPLETE
になれば完了。
ルートテーブルを作成して NAT ゲートウェイを登録
プライベートサブネットからインターネットにアクセスするには、
パブリックサブネットに NAT ゲートウェイを作成し、そこを経由してアクセスするようにする。
パブリックサブネットのルートテーブルでインターネットゲートウェイを経由するルートを登録したように、
プライベートサブネットでは NAT ゲートウェイを経由するルートを登録する。
また NAT ゲートウェイには Elastic IP が必要になる。
これを実現するには template.yaml の Resources
に次の定義を追加する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
ElasticIPForNAT:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NATGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId:
Fn::GetAtt:
- ElasticIPForNAT
- AllocationId
SubnetId: !Ref PublicSubnet
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRouteToInternet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateRouteTable
|
これまでと同様に変更セットを作成して適用する。
1
2
|
$ changeset_id=$(aws cloudformation create-change-set --stack-name sample-vpc --template-body file://template.yaml --change-set-name change-$(date +%Y%m%d-%H%M%S) --query 'Id')
$ aws cloudformation execute-change-set --change-set-name ${changeset_id//\"}
|
aws cloudformation describe-stacks --stack-name sample-vpc
の結果で StackStatus
が UPDATE_COMPLETE
になれば完了。
これでパブリックサブネットとプライベートサブネットを持つ VPC が完成した。
今回作成した VPC の最終的なテンプレートは次のようになる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
AWSTemplateFormatVersion: "2010-09-09"
Resources:
VPC: # Logical ID. Unique in template
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true # Default true
EnableDnsHostnames: true # EC2 インスタンスが DNS ホスト名を取得するかどうか。 Default false
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: true # このサブネットで起動されたインスタンスがパブリック IPv4 アドレスを受け取るかどうか
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: false # このサブネットで起動されたインスタンスがパブリック IPv4 アドレスを受け取るかどうか
ElasticIPForNAT:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NATGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId:
Fn::GetAtt:
- ElasticIPForNAT
- AllocationId
SubnetId: !Ref PublicSubnet
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PrivateRouteToInternet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateRouteTable
|
作成した VPC が不要になれば、次のコマンドで削除できる。
1
|
$ aws cloudformation delete-stack --stack-name sample-vpc
|