この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
検証用のVPCを作成しておき、必要な時だけNAT Gatewayがあればいいのになぁ、と思っていたので簡単にNAT Gatewayを作成、削除できるCloudFormationテンプレートを作成しました。
NAT GatewayはVPCと違って利用可能となっている時間で料金が発生するので、できるだけ利用可能となっている時間を減らしたいわけです。
構成
今回作成するVPCは下記のようになっています。NAT Gatewayを必要に応じて作成、削除を行います。NAT GatewayはAZ障害を考慮して冗長化していますが、お好みでカスタマイズしてください。
- Frontend subnet
- パブリックサブネットになります。インターネットゲートウェイを通じて外部との通信が可能です。
- ELBや踏み台サーバを配置する想定です
- Application subnet
- NAT Gatewayが無い場合は外部アクセス不可のプライベートサブネットになります。ある場合はVPC内からの通信のみが可能になります。その状態を図ではProtectedとしています。
- APサーバなどを配置する想定です
- DataStore subnet
- 外部アクセス不可なプライベートサブネットになります
- RDSなどを配置する想定です
解説
NAT Gatewayの部分だけ抜粋して説明していきます。全体のCloudFormationテンプレートは記事の最後に記載しています。
Parameters
NAT Gatewayを作成するかどうかをパラメータとして受け取ります。デフォルトでは作成しないになっています。
Parameters:
EnableNatGateway:
Description: Enable NAT Gateway.
Type: String
Default: false
AllowedValues: [true, false]
Conditions
NAT Gatewayを作成するかどうかの条件を作成します。
Conditions:
EnableNatGateway:
!Equals [true, !Ref EnableNatGateway]
Resources
作成した条件が真であれば、NAT GatewayとEIPを作成します。
Resources:
・・・
NatGateway1:
Type: AWS::EC2::NatGateway
Condition: EnableNatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP1.AllocationId
SubnetId: !Ref FrontendSubnet1
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-ngw1
NatGatewayEIP1:
Type: AWS::EC2::EIP
Condition: EnableNatGateway
Properties:
Domain: vpc
NatGateway2:
Type: AWS::EC2::NatGateway
Condition: EnableNatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP2.AllocationId
SubnetId: !Ref FrontendSubnet2
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-ngw2
NatGatewayEIP2:
Type: AWS::EC2::EIP
Condition: EnableNatGateway
Properties:
Domain: vpc
・・・
NAT Gatewayが作成されていれば、各Application subnetのルートテーブルにNAT Gatewayへのルートを作成しています。
Resources:
・・・
ApplicationRoute1:
Type: AWS::EC2::Route
Condition: EnableNatGateway
Properties:
RouteTableId: !Ref ApplicationRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1
ApplicationRoute2:
Type: AWS::EC2::Route
Condition: EnableNatGateway
Properties:
RouteTableId: !Ref ApplicationRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway2
・・・
スタックを作成する
まずは記事の最後にあるテンプレートをデフォルト設定のままスタックを作成します。パラメータは下図のとおりです。作成が完了したらNAT Gateway、EIP、ルートテーブルを確認し、NAT Gatewayの設定が無いことを確認します。
NAT Gatewayを作成する
作成したVPCにNAT Gatewayを作成してみます。コンソールからのポチポチだけでできます。
先程作成したスタックを選択し、「更新する」をクリックします。
「現在のテンプレートを使用」を選択します
EnableNatGateway
をtrue
に変更し、NAT Gatewayが作成されるようにします。あとは「次へ」をクリックしていきます。
変更セットのプレビューでNAT Gateway、EIP、ルートが追加されることが分かります。「スタックの更新」をクリックします。
スタックの更新が完了したらNAT Gatewayが作成されているか確認して完了です。
NAT Gatewayを削除する
削除する場合はEnableNatGateway
をfalse
にして更新すればOKです。
変更セットを見れば下記のように、NAT Gateway関連のリソースが削除されることがわかると思います。
テンプレート全体
今回使用したテンプレートになります。
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Network Layer Template
#------------------------------------------------------------------------------
Parameters:
#------------------------------------------------------------------------------
SystemName:
Description: This value is used as the resource prefix.
Type: String
MinLength: 1
Default: example
Env:
Description: Environment Name
Type: String
Default: dev
AllowedValues:
- dev
- prd
VpcCidr:
Description: First and Second Octet of VPC, For example (10.0/172.16/192.168)
Type: String
Default: 10.0
AllowedPattern: "^(10\\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|172\\.(1[6-9]|2[0-9]|3[0-1])|192\\.168)$"
ConstraintDescription: xxx.xxx
EnableNatGateway:
Description: Enable NAT Gateway.
Type: String
Default: false
AllowedValues: [true, false]
#------------------------------------------------------------------------------
Conditions:
#------------------------------------------------------------------------------
EnableNatGateway:
!Equals [true, !Ref EnableNatGateway]
#------------------------------------------------------------------------------
Mappings:
#------------------------------------------------------------------------------
VpcConfig:
dev:
Vpc : .0.0/16
FrontendSubnet1 : .0.0/24
FrontendSubnet2 : .1.0/24
ApplicationSubnet1 : .10.0/24
ApplicationSubnet2 : .11.0/24
DatastoreSubnet1 : .20.0/24
DatastoreSubnet2 : .21.0/24
#------------------------------------------------------------------------------
Resources:
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
Vpc:
#------------------------------------------------------------------------------
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, Vpc ]}]
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-vpc
#------------------------------------------------------------------------------
InternetGateway:
#------------------------------------------------------------------------------
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-igw
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
#------------------------------------------------------------------------------
FrontendRouteTable:
#------------------------------------------------------------------------------
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-frontend-rtb
FrontendRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref FrontendRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
#------------------------------------------------------------------------------
ApplicationRouteTable1:
#------------------------------------------------------------------------------
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-application1-rtb
ApplicationRoute1:
Type: AWS::EC2::Route
Condition: EnableNatGateway
Properties:
RouteTableId: !Ref ApplicationRouteTable1
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway1
#------------------------------------------------------------------------------
ApplicationRouteTable2:
#------------------------------------------------------------------------------
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-application2-rtb
ApplicationRoute2:
Type: AWS::EC2::Route
Condition: EnableNatGateway
Properties:
RouteTableId: !Ref ApplicationRouteTable2
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway2
#------------------------------------------------------------------------------
DataStoreRouteTable:
#------------------------------------------------------------------------------
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-datastore-rtb
#------------------------------------------------------------------------------
NetworkACL:
#------------------------------------------------------------------------------
Type: AWS::EC2::NetworkAcl
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-nacl
VpcId: !Ref Vpc
NetworkACLEntryEgress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: 0.0.0.0/0
Egress: true
NetworkAclId: !Ref NetworkACL
Protocol: -1
RuleAction : allow
RuleNumber : 100
NetworkACLEntryIngress:
Type: AWS::EC2::NetworkAclEntry
Properties:
CidrBlock: 0.0.0.0/0
Egress: false
NetworkAclId: !Ref NetworkACL
Protocol: -1
RuleAction : allow
RuleNumber : 100
#------------------------------------------------------------------------------
FrontendSubnet1:
#------------------------------------------------------------------------------
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 0, "Fn::GetAZs": { Ref: "AWS::Region" } ]
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, FrontendSubnet1 ]}]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-frontend1-subnet
VpcId: !Ref Vpc
FrontendSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref FrontendSubnet1
RouteTableId: !Ref FrontendRouteTable
FrontendSubnet1NACLAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref FrontendSubnet1
NetworkAclId: !Ref NetworkACL
#------------------------------------------------------------------------------
FrontendSubnet2:
#------------------------------------------------------------------------------
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 1, "Fn::GetAZs": { Ref: "AWS::Region" } ]
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, FrontendSubnet2 ]}]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-frontend2-subnet
VpcId: !Ref Vpc
FrontendSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref FrontendSubnet2
RouteTableId: !Ref FrontendRouteTable
FrontendSubnet2NACLAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref FrontendSubnet2
NetworkAclId: !Ref NetworkACL
#------------------------------------------------------------------------------
ApplicationSubnet1:
#------------------------------------------------------------------------------
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 0, "Fn::GetAZs": { Ref: "AWS::Region" } ]
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, ApplicationSubnet1 ]}]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-application1-subnet
VpcId: !Ref Vpc
ApplicationSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref ApplicationSubnet1
RouteTableId: !Ref ApplicationRouteTable1
ApplicationSubnet1NACLAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref ApplicationSubnet1
NetworkAclId: !Ref NetworkACL
#------------------------------------------------------------------------------
ApplicationSubnet2:
#------------------------------------------------------------------------------
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 1, "Fn::GetAZs": { Ref: "AWS::Region" } ]
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, ApplicationSubnet2 ]}]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-application2-subnet
VpcId: !Ref Vpc
ApplicationSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref ApplicationSubnet2
RouteTableId: !Ref ApplicationRouteTable2
ApplicationSubnet2NACLAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref ApplicationSubnet2
NetworkAclId: !Ref NetworkACL
#------------------------------------------------------------------------------
DatastoreSubnet1:
#------------------------------------------------------------------------------
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 0, "Fn::GetAZs": { Ref: "AWS::Region" } ]
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, DatastoreSubnet1 ]}]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-datastore1-subnet
VpcId: !Ref Vpc
DatastoreSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref DatastoreSubnet1
RouteTableId: !Ref DataStoreRouteTable
DatastoreSubnet1NACLAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref DatastoreSubnet1
NetworkAclId: !Ref NetworkACL
#------------------------------------------------------------------------------
DatastoreSubnet2:
#------------------------------------------------------------------------------
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 1, "Fn::GetAZs": { Ref: "AWS::Region" } ]
CidrBlock: !Sub [ "${VpcCidr}${Subnet}", { Subnet: !FindInMap [ VpcConfig, !Ref Env, DatastoreSubnet2 ]}]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-datastore2-subnet
VpcId: !Ref Vpc
DatastoreSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref DatastoreSubnet2
RouteTableId: !Ref DataStoreRouteTable
DatastoreSubnet2NACLAssociation:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId: !Ref DatastoreSubnet2
NetworkAclId: !Ref NetworkACL
#------------------------------------------------------------------------------
NatGateway1:
#------------------------------------------------------------------------------
Type: AWS::EC2::NatGateway
Condition: EnableNatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP1.AllocationId
SubnetId: !Ref FrontendSubnet1
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-ngw1
NatGatewayEIP1:
Type: AWS::EC2::EIP
Condition: EnableNatGateway
Properties:
Domain: vpc
#------------------------------------------------------------------------------
NatGateway2:
#------------------------------------------------------------------------------
Type: AWS::EC2::NatGateway
Condition: EnableNatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP2.AllocationId
SubnetId: !Ref FrontendSubnet2
Tags:
- Key: Name
Value: !Sub ${SystemName}-${Env}-ngw2
NatGatewayEIP2:
Type: AWS::EC2::EIP
Condition: EnableNatGateway
Properties:
Domain: vpc
#------------------------------------------------------------------------------
Outputs:
#------------------------------------------------------------------------------
Vpc:
Value: !Ref Vpc
Export:
Name: !Sub ${SystemName}-${Env}-vpc
FrontendSubnet1:
Value: !Ref FrontendSubnet1
Export:
Name: !Sub ${SystemName}-${Env}-frontend1-subnet
FrontendSubnet2:
Value: !Ref FrontendSubnet2
Export:
Name: !Sub ${SystemName}-${Env}-frontend2-subnet
ApplicationSubnet1:
Value: !Ref ApplicationSubnet1
Export:
Name: !Sub ${SystemName}-${Env}-application1-subnet
ApplicationSubnet2:
Value: !Ref ApplicationSubnet2
Export:
Name: !Sub ${SystemName}-${Env}-application2-subnet
DatastoreSubnet1:
Value: !Ref DatastoreSubnet1
Export:
Name: !Sub ${SystemName}-${Env}-datastore1-subnet
DatastoreSubnet2:
Value: !Ref DatastoreSubnet2
Export:
Name: !Sub ${SystemName}-${Env}-datastore2-subnet
まとめ
NAT Gatewayを簡単に作成、削除できることが確認できました。条件の使い過ぎは可読性や保守性が落ちるのでおすすめしませんが、用途によっては非常に便利なことがわかりました。
なにかの参考になれば幸いです。