Lambda用のVPC環境を、CloudFormationで構築する

#🔷 はじめに
外部APIにリクエストを投げるLambda関数を作ったのですが、外部API側でIP制限をかける事になったので、Lambda関数のIPを固定化する必要が出てきました。
そこで、以下を参考にVPC環境を構築して、外部APIにリクエストを投げる際の、リクエスト元のIPアドレスを固定化しました。

[Lambdaの実行IPアドレスを固定し、kintoneセキュアアクセスに対応する方法](https://qiita.com/katsuhisa__/items/cba9e470b0540c88e6a4)

その際、設定のドキュメントも併せて作ろうと思ったのですが、[AWS CloudFormation](https://aws.amazon.com/jp/cloudformation/)のテンプレートファイルを作れば、設定ドキュメント兼環境構築プログラムになると思い、以下を参考にしながらVPC環境を定義したテンプレート作ってみました。

[AWS CloudFormationによるVPC作成 + public及びprivateサブネット作成](https://qiita.com/na0AaooQ/items/251790a7ef311804b77e)

ちなみに、最初は[AWS SAM](https://aws.amazon.com/jp/serverless/sam/)でLambdaも含めて一つのテンプレートを作ろうとしましたが、**AWS SAMからはVPCの構築は対応していない（2019年7月時点）**という事だったので、VPCはCloudFormationから構築する事にしました。

#🔷 作るもの

今回構築するVPC環境は以下のようなイメージです。

使い捨てリソースであるLambda関数に固定IPを割り当てるのは無理なので、VPC環境を構築して、Lambda関数がVPCの外に出る時に通過する、NAT GatewayにIPを割り振ることで、リクエスト元のIPアドレスの固定化を実現しています。

サブネットがAvailability Zone別に2セット作ってあるのは、実際にLambda関数にPrivateサブネットを割り当てた時、**複数のPrivateサブネットを割り当てる事を推奨する警告が表示された**ので、一応2セット用意しました。

![VPCイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38099/fcb2fd47-5529-3af1-c4d4-1a1931a24583.png)


#🔷テンプレートサンプル

CloudFormationのテンプレートは以下の通りです。

[テンプレートリファレンス](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html)に書き方が一通り書いてあるので、時間を掛ければ書くのは難しくないですが、とにかく設定する箇所が多いので、慣れないうちは設定漏れ等で苦戦すると思います。




```yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: hoge環境
Resources:
# ■□ VPC環境構築 □■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
# ■□■□ VPCの作成
  hogeVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: 'false'
      EnableDnsHostnames: 'false'
      InstanceTenancy: default
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-vpc
# ■□■□ インターネットゲートウェイの作成
  hogeIGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-igw
# ■□■□■□ インターネットゲートウェイをVPCにセット
  AttachVpcGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: hogeVPC
      InternetGatewayId:
        Ref: hogeIGW
# ■□ サブネットの設定（2つのAZに作成） □■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
# ■□■□ Publicのルートテーブルの作成
  hogeRTblPublicAZ01:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: hogeVPC
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-rtb-az01-igw
  hogeRTblPublicAZ02:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: hogeVPC
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-rtb-az02-igw
# ■□■□■□ IGW-publicルーティング
  hogeRouteIGWPublicAZ01:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref hogeRTblPublicAZ01
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref hogeIGW
  hogeRouteIGWPublicAZ02:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref hogeRTblPublicAZ02
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref hogeIGW
# ■□■□ Privateのルートテーブルの作成
  hogeRTblPrivateAZ01:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: hogeVPC
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-rtb-az01-nat
  hogeRTblPrivateAZ02:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: hogeVPC
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-rtb-az02-nat
# ■□■□■□ Nat-privateルーティング
  hogeRouteNatPrivateAZ01:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref hogeRTblPrivateAZ01
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: hogeNatAZ01
  hogeRouteNatPrivateAZ02:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref hogeRTblPrivateAZ02
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: hogeNatAZ02
# ■□■□ NATゲートウェイの作成
  hogeNatAZ01:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
        - hogeEipAZ01
        - AllocationId
      SubnetId:
        Ref: hogeSBNPrivateAZ01
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-nat-az01
  hogeNatAZ02:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
        - hogeEipAZ02
        - AllocationId
      SubnetId:
        Ref: hogeSBNPrivateAZ02
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-nat-az02
# ■□■□ EIPの作成
  hogeEipAZ01:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  hogeEipAZ02:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
# ■□■□ Publicサブネットの作成
  hogeSBNPublicAZ01:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: hogeVPC
      CidrBlock: 10.0.10.0/24
      # 指定したregionの最初のazを選択
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-sbn-public-az01
  hogeSBNPublicAZ02:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: hogeVPC
      CidrBlock: 10.0.12.0/24
      # 指定したregionの最初のazを選択
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-sbn-public-az02
# ■□■□■□ サブネットをルートテーブルに関連付け
  hogeSbnRTblAssociationAZ01:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: hogeSBNPublicAZ01
      RouteTableId:
        Ref: hogeRTblPublicAZ01
  hogeSbnRTblAssociationAZ02:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: hogeSBNPublicAZ02
      RouteTableId:
        Ref: hogeRTblPublicAZ02
# ■□■□ Privateサブネットの作成
  hogeSBNPrivateAZ01:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: hogeVPC
      CidrBlock: 10.0.11.0/24
      # 指定したregionの最初のazを選択
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-sbn-private-az01
  hogeSBNPrivateAZ02:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: hogeVPC
      CidrBlock: 10.0.13.0/24
      # 指定したregionの最初のazを選択
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-sbn-private-az02
# ■□■□ セキュリティグループの作成
  hogeSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group for hoge VPC
      VpcId:
        Ref: hogeVPC
      Tags:
      - Key: product
        Value: hoge
      - Key: developer
        Value: jimu
      - Key: Name
        Value: hoge-vpc-sg

```

### 🔶 ポイント
Availability Zoneの指定を以下のように書くことで、配置可能なAvailability Zoneのリストを取ってくきて、その1件目を選択する事ができます。

```yaml
      # 指定したregionの最初のazを選択
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
```
このように書くことで、Availability Zoneをハードコーディングしなくてすむので、このテンプレートファイルはどのリージョンでも使用する事ができるようになります。

また、セキュリティグループは今回は細かく設定していませんが、以下のように最低限の記述にする事で、**インバウンドなし、アウトバウンドはすべてOK**という、デフォルトよりはマシな設定にしています。

```yaml

  hogeSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group for hoge VPC
      VpcId:
        Ref: hogeVPC
```
#🔷 まとめ

以上で設定は完了です。
とりあえず、最初の全体図と、テンプレートファイルがあれば、設計書の代わりになるかと思います。（Elastic IP等の補足メモは必要ですが）

テンプレートファイルなら、見て分からなければ、最悪CloudFomationを使えば現物を再現する事もできます。

最初は作るのに時間が掛ると思いますが、たくさん作っていけば、だんだん流用できる部分が増えてくるので、余裕がある時は挑戦してみてはいかがでしょうか。

#🔷 参考



* [テンプレートリファレンス](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html)
* [Lambdaの実行IPアドレスを固定し、kintoneセキュアアクセスに対応する方法](https://qiita.com/katsuhisa__/items/cba9e470b0540c88e6a4)
* [AWS CloudFormationによるVPC作成 + public及びprivateサブネット作成](https://qiita.com/na0AaooQ/items/251790a7ef311804b77e)