
必要な時だけNAT Gatewayを作成する方法
この記事は公開されてから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を簡単に作成、削除できることが確認できました。条件の使い過ぎは可読性や保守性が落ちるのでおすすめしませんが、用途によっては非常に便利なことがわかりました。
なにかの参考になれば幸いです。


















