AWS LambdaでVPCを紐付けした時の外部APIを使用する方法

よくLambdaからSlackへ投稿する方法など外部のAPIを使用したり、ウェブスクレイピングして定期的に情報を取得するなどの方法が、ネットで調べると出てきたりします。
通常ただLambdaで構築するだけだったら、何も気にせずに行えるのですが、Lambdaで、VPCの紐付けをしていると外部との接続ができなくなります。
当たり前の事かもしれませんが、Lambdaの勉強中にここでハマってしまいました。
どうやら、Lambdaに紐付けするVPC（正確にはサブネット）にNAT Gatewayを使用して外部接続が出来る様にしないと駄目との事でした。
なので、今後の為にメモ代わりに記事にして残しておきたいと思います。
これまでの記事同様、CloudFormation を利用してテンプレート化しています。

## NAT GatewayとElastic IPを利用する上での注意点

NAT GatewayとElastic IPが必要な為料金が発生する可能性が存在します。
私は、学習の為の必要経費だと思い気にしませんが注意が必要だと思います。

## テンプレートの作成

今回VPCの作成から行っています。
構成は以下の通りです。

1. IAM ロールの作成
1. VPC作成
1. サブネットの作成(public)
1. サブネットの作成(private)
1. ルートテーブルの作成
1. Internet Gatewayの作成
1. Elastic IPの作成
1. NAT Gatewayの作成
1. WebSecurityGroupの作成
1. API GatewayでのREST APIの作成
1. AWS Lambdaの作成(API Gateway、WebSecurityGroup、VPC,Layerの紐付け含む)
1. Layerの作成
1. 
LambdaでVPCを紐付けして行う場合公式で2つ以上のSubNetを紐付けする事が推奨されているとの事でした。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-vpc.html

なので、外部接続を行うサブネットを２つ、実際にLambdaに紐付けするサブネットを２つ用意します。

```templete.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lberc Slack API
Globals:
  Api:
    OpenApiVersion: 3.0.2
Resources:
  #ここからロールの設定
  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      ManagedPolicyArns:
        - !Sub 'arn:aws:iam::aws:policy/AmazonVPCFullAccess'
  # ここからEIP の設定
  NATGatewayEIP:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
  # ここからEIP の設定
  NATGateway02EIP:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
  # ここからNAT Gatewayの設定
  NATGateway: 
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId: !GetAtt NATGatewayEIP.AllocationId
      SubnetId: !Ref PublicSubNet01a
      Tags: 
        - Key: Name
          Value: Sample-VPC-NATGateway-a
  NATGateway02: 
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId: !GetAtt NATGateway02EIP.AllocationId
      SubnetId: !Ref PublicSubNet02a
      Tags: 
        - Key: Name
          Value: Sample-VPC-NATGateway02-a
  # ここからVPCの作成
  Sample-Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      Tags:
      - Key: Name
        Value: Sample--VPC
  # ここからSubnet Publcの設定
  PublicSubNet01a:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.10.0/24
      Tags:
      - Key: Name
        Value: PublicSubNet
      VpcId: !Ref Sample-Vpc
  # ここからSubnet Publcの設定
  PublicSubNet02a:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.20.0/24
      Tags:
      - Key: Name
        Value: PublicSubNet02
      VpcId: !Ref Sample-Vpc
  # ここからSubnet Privateの設定
  PrivateSubNet01b:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.30.0/24
      Tags:
      - Key: Name
        Value: PrivateSubNet
      VpcId: !Ref Sample-Vpc
  # ここからSubnet Privateの設定
  PrivateSubNet02b:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.40.0/24
      Tags:
      - Key: Name
        Value: PrivateSubNet02
      VpcId: !Ref Sample-Vpc
  # ここからインターネットゲートウェイの設定
  TpInternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
      - Key: Name
        Value: Sample-VPC
  TpAttachGateway:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      InternetGatewayId: !Ref TpInternetGateway
      VpcId: !Ref Sample-Vpc
  # ここからルートテーブル(PublicSubNet01a)の設定
  TpRouteTable01:
    Type: "AWS::EC2::RouteTable"
    Properties:
      Tags:
      - Key: Name
        Value: Techpit01
      VpcId: !Ref Sample-Vpc
  TpRouteTableAssociation01a:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref TpRouteTable01
      SubnetId: !Ref PublicSubNet01a
  TpRoute01:
    Type: "AWS::EC2::Route"
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref TpInternetGateway
      RouteTableId: !Ref TpRouteTable01
  # ここからルートテーブル(PublicSubNet02a)の設定
  TpRouteTable02:
    Type: "AWS::EC2::RouteTable"
    Properties:
      Tags:
      - Key: Name
        Value: Techpit02
      VpcId: !Ref Sample-Vpc
  TpRouteTableAssociation02a:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref TpRouteTable02
      SubnetId: !Ref PublicSubNet02a
  TpRoute02:
    Type: "AWS::EC2::Route"
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref TpInternetGateway
      RouteTableId: !Ref TpRouteTable02
  # ここからルートテーブル(PrivateSubNet01b)の設定
  TpRouteTable03:
    Type: "AWS::EC2::RouteTable"
    Properties:
      Tags:
      - Key: Name
        Value: Techpit03
      VpcId: !Ref Sample-Vpc
  TpRouteTableAssociation03a:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref TpRouteTable03
      SubnetId: !Ref PrivateSubNet01b
  TpRoute03:
    Type: "AWS::EC2::Route"
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      RouteTableId: !Ref TpRouteTable03
      NatGatewayId: !Ref NATGateway
  # ここからルートテーブル(PrivateSubNet02b)の設定
  TpRouteTable04:
    Type: "AWS::EC2::RouteTable"
    Properties:
      Tags:
      - Key: Name
        Value: Techpit04
      VpcId: !Ref Sample-Vpc
  TpRouteTableAssociation04a:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref TpRouteTable04
      SubnetId: !Ref PrivateSubNet02b
  TpRoute04:
    Type: "AWS::EC2::Route"
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      RouteTableId: !Ref TpRouteTable04
      NatGatewayId: !Ref NATGateway02
# WebSecurityGroupの作成  
  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "sample API SecurityGroup"
      GroupName: sample-API-SecurityGroup
      VpcId: !Ref Sample-Vpc
# ここからAPI Gatewayの設定
  RestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      OpenApiVersion: 3.0.2
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: ./sample-api-dev-swagger-apigateway.yaml # API　Gatewayの構成のパス
# AWS Lambdaの作成(API Gateway、WebSecurityGroup、VPC,Layerの紐付け含む)
  SampleFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: sample-function
      Handler: lambda_function.lambda_handler
      Runtime: python3.8
      Role: !GetAtt LambdaExecutionRole.Arn # Roleを紐付け
      CodeUri: ./sample-function/ # Lambdaのコードのパス
      Description: ''
      MemorySize: 128
      Timeout: 25
      Events:
        Api:
          Type: Api
          Properties:
            RestApiId: !Ref RestApi # RestApiと紐付け
            Path: '/v1/sample-function/{param}'
            Method: GET
      VpcConfig:
        SecurityGroupIds:
          - !Ref WebSecurityGroup # WebSecurityGroupと紐付け
        SubnetIds:
          - !Ref PrivateSubNet01b #Subnet(private)と紐付け
          - !Ref PrivateSubNet02b #Subnet(private)と紐付け
      Environment:
        Variables:
          ID: 1234 # 環境変数その１
          PASS: hogehoge # 環境変数その2
      Tags:
        STAGE: dev
      Layers:
      - Ref: SampleLayers # Layersと紐付け
# Layerの作成  
  SampleLayers:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: sample-layers
      Description: Sample API Resource Layer
      CompatibleRuntimes: 
      - python3.8
      ContentUri: ./sample-layers # Layersのパス
```

後は、MakeFileを使用するかや直接コマンドを入力してデプロイを行えば完了です。
MakeFileを利用したデプロイの方法に関しては、弊社グループの@shimajiriや私が以前投稿した以下の記事を参考にしてください。

https://qiita.com/shimajiri/items/e22b18188051c2199582

https://qiita.com/lberc_yuki_ono/items/2a2d6f0cf7982bf3fbfb