CloudFormationで作成したAPIGatewayとLambdaを動かす

# はじめに
今回はAPI GatewayをトリガーとしてLambdaを実行していきたいと思います。
API GatewayとLambdaの作成とテスト実行までやっていきたいと思います。

# 目次
・構成について
・Lambdaの設定項目
・LambdaのCloudFormation作成
・API Gatewayの設定項目
・API GatewayのCloudFormation作成
・Permission設定について
・動作確認
・最後に
・参考

# 構成について
LambdaとAPI Gatewayで構成します。
AWS環境はCloudFormationで作成します。
そのため、LambdaのコードやレイヤーのパッケージについてはS3から取得する必要があるのでS3も作成します。

S3の構築は以前の記事を参考に作成してください。

https://qiita.com/ogataro/items/71dd28babfcdf6410a3c

# Lambdaの設定項目
今回はnodejs.20.xを利用し、「Hello from Lambda!」と返答してくるようにします。
またnodejsのパッケージをLambdaレイヤーに格納し、Lambda Functionと紐づけます。

index.jsの内容は記載しておりますが、node_modulesなどは別途対応をお願いします。
index.jsで使用する環境変数についても設定しています。

上記以外の項目についてはCloudFormationの設定を見てご確認ください

# LambdaのCloudFormation作成
LambdaとIAMロールを作成します
### Lambda_CloudFormation
```yaml:Lambda.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  Lambda Deployment

#------------------------------------------------------------#
#Parameters
#------------------------------------------------------------#
Parameters:
  PJPrefix:
    Description: Project Name
    Type: String
    Default: pj-ogataro
    AllowedPattern: ^[a-zA-Z0-9\-]*$
  Environment:
    Description: Environment Name
    Type: String
    Default: dev
    AllowedPattern: ^[a-zA-Z0-9\-]*$

#------------------------------------------------------------#
#Resources
#------------------------------------------------------------#
Resources:
  ###Lambdaレイヤー
  LambdaLayer:
   Type: 'AWS::Lambda::LayerVersion'
   Properties:
     LayerName: !Sub '${PJPrefix}-${Environment}-Nodejs-Layer'
     CompatibleRuntimes:
       - nodejs20.x
     Content:
       S3Bucket: !ImportValue
         Fn::Sub: '${PJPrefix}-${Environment}-storage-Output'
       S3Key: nodejs.zip
       
  ###Lambdaファンクション
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub '${PJPrefix}-${Environment}-Lambda'
      Runtime: nodejs20.x
      Handler: index.handler
      Layers:
        - !Ref LambdaLayer
      Code:
        S3Bucket: !ImportValue
          Fn::Sub: '${PJPrefix}-${Environment}-storage-Output'
        S3Key: index.zip
      Role: !GetAtt IAMRole.Arn
      Timeout: 10
      MemorySize: 256

  ###IAMロール
  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${PJPrefix}-${Environment}-Role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: LambdaExecutionPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: 'log'
                Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: arn:aws:logs:*:*:*

```

### Lambda_index.mjs
```js:index.mjs
export const handler = async (event) => {
  // TODO implement
  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};
```

# API Gatewayの設定項目
API Gatewayの設定項目はREST APIでREGIONALとしています。
また、Lambdaを呼び出すためGETメソッドを利用します。
それ以外の項目はCloudFormationを見て、確認してください。

# API GatewayのCloudFormation作成
```yaml:APIGateway.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Description: >
  API Gateway deployment

#------------------------------------------------------------#
#Parameters
#------------------------------------------------------------#
Parameters:
  PJPrefix:
    Description: Project Name
    Type: String
    Default: pj-ogataro
    AllowedPattern: ^[a-zA-Z0-9\-]*$
  Environment:
    Description: Environment Name
    Type: String
    Default: dev
    AllowedPattern: ^[a-zA-Z0-9\-]*$

#------------------------------------------------------------#
#Resources
#------------------------------------------------------------#
Resources:
  ##IAMロール
  IAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${PJPrefix}-${Environment}-API-Role'
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
      Path: /

  ##アカウントのAPIGateway設定
  APIGatewayAccount:
    Type: AWS::ApiGateway::Account
    Properties:
      CloudWatchRoleArn: !GetAtt IAMRole.Arn
    DependsOn: IAMRole


  ##API作成
  RestAPI:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub '${PJPrefix}-${Environment}-APIGateway'
      EndpointConfiguration:
        Types:
          - REGIONAL
    DependsOn: APIGatewayAccount

  ##リソース
  Resource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref RestAPI
      ParentId: !GetAtt RestAPI.RootResourceId
      PathPart: "test"
    DependsOn: RestAPI

  #メソッド
  Method:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RestAPI
      ResourceId: !Ref Resource
      HttpMethod: GET
      AuthorizationType: NONE
      ApiKeyRequired: false
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: GET
        Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${PJPrefix}-${Environment}-Lambda/invocations'
      MethodResponses:
        - StatusCode: 200
          ResponseModels:
            application/json: Empty
        - StatusCode: 404
    DependsOn: Resource

  #APIのデプロイ
  Deployment:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref RestAPI
    DependsOn: Method

  #ステージ作成
  Stage:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: !Sub '${PJPrefix}-${Environment}-API-Stage'
      Description: Deploy stage
      RestApiId: !Ref RestAPI
      DeploymentId: !Ref Deployment
      MethodSettings:
        - CacheDataEncrypted: false
          CachingEnabled: false
          DataTraceEnabled: true # CloudWatch ログを有効化
          HttpMethod: '*'
          LoggingLevel: INFO
          MetricsEnabled: true # 詳細 CloudWatch メトリクスを有効化
          ResourcePath: '/*'
    DependsOn: Deployment
```

# Permissionの設定について
正常に構築出来たら、Lambdaに対してリソースベースのポリシーを更新します。
API GatewayのGETメソッドで統合リクエストの編集をクリックします。
そこで関数にて該当のLambdaを選択しなおして、ポリシーの更新をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513082/7f6e9545-e78c-16ff-1492-8ad7c02da808.png)

下記のような表示が出て、保存を押下したら、APIデプロイを実施します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513082/1d2d2183-1432-257a-39a4-245a72f3b31d.png)

Lambdaの設定からアクセス権限にてリソースベースのポリシーステートメントに権限が追加されていれば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513082/af4681bd-8812-163c-4645-77900d49747d.png)

# 動作確認
ここまでできたら動作確認をしていきます。
API Gatewayのメソッドからテスト画面に遷移しテストを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513082/c1f1e549-02a6-3252-336e-b5de4fb43013.png)

下記のようなレスポンスが返ってくれば、成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513082/dab50b28-e97c-582c-26f4-a672cd6d2b87.png)


# 最後に
こちらでCloudFormationで作成したAPI GatewayとLambdaを動かす手順は完了です。
参考になれば、幸いです。

# 参考サイト
Lambda

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html

API Gateway

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html