SAM + SwaggerでAPIGateway + Lambdaを構築

SAM + SwaggerでAPIGateway + Lambdaを構築:

aws-sam-cliとSwaggerを組み合わせてAPIGateway+Lambdaのサーバレス構成を構築する際にいろんな罠にハマり、CloudFormationに振り回されたので、この記事では最低限の構成を構築するために必要なテンプレートの記述を紹介たいと思います。


前提

当記事では以下のAPIGateway + Lambdaを構築していきます。


APIGateway

  • リソース名:FugaApi
  • エンドポイント


    • /hoge

      • GET
      • OPTIONS


Lambda

  • HogeFunction


    • /hogeで呼び出されるLambda
これからこの構成を一つのyamlファイルでテンプレートを用意していきます。


LamdbaRole

まずはLambdaにつけるロールを用意します。
ManagedPolicyArnsでAWSが用意しているポリシーをつけられるのは便利ですね。

LambdaRole: 
    Type: AWS::IAM::Role 
    Properties: 
      RoleName: HogeLambdaRole 
      AssumeRolePolicyDocument: 
        Version: '2012-10-17' 
        Statement: 
        - Effect: Allow 
          Principal: 
            Service: 
            - lambda.amazonaws.com 
          Action: 
          - sts:AssumeRole 
      Path: "/" 
      ManagedPolicyArns: 
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess 
 


Lambda

Lambda本体を定義します。
Roleには先程定義したLambdaRoleを指定しています。
EventsにはAPIGatewayを指定するようにしています。
RestApiIdに後述するAPIGatewayのリソースFugaApiを指定しています。
RestApiIdをつけ忘れるとFugaApiとは別のAPIGatewayが作られてしますので注意してください。

(というかSwaggerで定義した場合はEventブロックそのものが要らない可能性もあります。

確認出来次第修正します。)

HogeFunction: 
    Type: AWS::Serverless::Function 
    Properties: 
      CodeUri: HogeFunction/ 
      Role: !GetAtt LambdaRole.Arn 
      FunctionName: HogeFunction 
      Events: 
        Api: 
          Type: Api 
          Properties: 
            RestApiId: !Ref FugaApi 
            Path: /hoge 
            Method: GET 


Globalセクション

実は上記のLambdaテンプレートにはHandlerRuntimeなどのプロパティが不足しています。

今回はそのあたりのプロパティはGlobalセクションに記述しています。

SAMテンプレートではGlobalセクションでテンプレート内のリソースの共有設定を記述できます。

今回の構成ではあまり意味がありませんが、Lambdaを複数定義したい場合などに共通する設定は
Globalセクションに記述できるのでテンプレートの記述が非常に楽になります。

また、各リソースのテンプレートで個別に定義したいプロパティを記述すれば上書きしてくれます。

SAMを利用する場合は是非活用しましょう。

Globals: 
  Function: 
    Timeout: 30 
    Handler: app.lambdaHandler 
    Runtime: nodejs8.10 
    Environment: 
      Variables: 
        TZ: Asia/Tokyo 


Permission

APIGatewayからLambdaにアクセスできるようにPermissonを定義しましょう。

Permissonを定義し忘れても構築自体はできますがAPIGatewayからLambdaにアクセスできないので、

APIを叩いても必ず500エラーが返り、Lambdaも実行自体されないのでログもでないという状態になります。

私はそれで小一時間悩みました。。

HogeFunctionPermission: 
    Type: "AWS::Lambda::Permission" 
    Properties: 
      Action: lambda:InvokeFunction 
      FunctionName: !Ref HogeFunction 
      Principal: apigateway.amazonaws.com 


APIGateway

APIGatewayはSwagger形式でエンドポイントを定義できます。
DefinitionBodyにSwaggerを記述していきましょう。

今回はCORSを利用できるようにするためoptionsメソッドも記述しています。

注意点としてはoptions.responsesのステータスコードは数値でなく文字列で定義してください。

今回のようなSAMテンプレートに直接Swaggerを定義した構成の場合、数値のままだとCloudFormationで失敗します。

FugaApi: 
    Type: AWS::Serverless::Api 
    Properties: 
      Name: FugaApi 
      StageName: api 
      DefinitionBody: 
        swagger: "2.0" 
        schemes: 
          - "https" 
        paths: 
          /hoge: 
            get: 
              responses: {} 
              x-amazon-apigateway-integration: 
                uri: !Join [ '' , [ 'arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/' , !GetAtt  HogeFunction.Arn , '/invocations' ] ] 
                passthroughBehavior: "when_no_match" 
                httpMethod: "POST" 
                type: "aws_proxy" 
            options: 
              consumes: 
              - "application/json" 
              produces: 
              - "application/json" 
              responses: 
                "200": # ここは文字列で定義する 
                  description: "200 response" 
                  schema: 
                    $ref: "#/definitions/Empty" 
                  headers: 
                    Access-Control-Allow-Origin: 
                      type: "string" 
                    Access-Control-Allow-Methods: 
                      type: "string" 
                    Access-Control-Allow-Headers: 
                      type: "string" 
              x-amazon-apigateway-integration: 
                responses: 
                  default: 
                    statusCode: "200" 
                    responseParameters: 
                      method.response.header.Access-Control-Allow-Methods: "'*'" 
                      method.response.header.Access-Control-Allow-Headers: "'*'" 
                      method.response.header.Access-Control-Allow-Origin: "'*'" 
                requestTemplates: 
                  application/json: "{\"statusCode\": 200}" 
                passthroughBehavior: "when_no_match" 
                type: "mock" 


まとめ

最終的に以下のようなテンプレートファイルになります。

特に気をつけたい点、というか私がハマった点は以下になります。

  • LambdaにAPIGatewayのPermmison設定を忘れずに

  • RestApiIdの定義も忘れずに
  • Swaggerでresponseを定義する場合は文字列で定義すること
みなさんは是非CloudFormationに振り回されないようなAWSライフをお過ごしください

samtemplate.yaml
AWSTemplateFormatVersion: '2010-09-09' 
Transform: AWS::Serverless-2016-10-31 
Description: > 
   pokemon web api template 
 
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 
Globals: 
  Function: 
    Timeout: 30 
    Handler: app.lambdaHandler 
    Runtime: nodejs8.10 
    Environment: 
      Variables: 
        TZ: Asia/Tokyo 
 
Resources: 
  LambdaRole: 
    Type: AWS::IAM::Role 
    Properties: 
      RoleName: HogeLambdaRole 
      AssumeRolePolicyDocument: 
        Version: '2012-10-17' 
        Statement: 
        - Effect: Allow 
          Principal: 
            Service: 
            - lambda.amazonaws.com 
          Action: 
          - sts:AssumeRole 
      Path: "/" 
      ManagedPolicyArns: 
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 
      - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess 
  LambdaRoleProfile: 
    Type: AWS::IAM::InstanceProfile 
    Properties: 
      Path: "/" 
      Roles: 
          - !Ref LambdaRole 
 
  HogeFunction: 
    Type: AWS::Serverless::Function 
    Properties: 
      CodeUri: HogeFunction/ 
      Role: !GetAtt LambdaRole.Arn 
      FunctionName: HogeFunction 
      Events: 
        Api: 
          Type: Api 
          Properties: 
            RestApiId: !Ref FugaApi 
            Path: /hoge 
            Method: GET 
  HogeFunctionPermission: 
    Type: "AWS::Lambda::Permission" 
    Properties: 
      Action: lambda:InvokeFunction 
      FunctionName: !Ref HogeFunction 
      Principal: apigateway.amazonaws.com 
 
 
  FugaApi: 
    Type: AWS::Serverless::Api 
    Properties: 
      Name: FugaApi 
      StageName: api 
      DefinitionBody: 
        swagger: "2.0" 
        schemes: 
          - "https" 
        paths: 
          /hoge: 
            get: 
              responses: {} 
              x-amazon-apigateway-integration: 
                uri: !Join [ '' , [ 'arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/' , !GetAtt  HogeFunction.Arn , '/invocations' ] ] 
                passthroughBehavior: "when_no_match" 
                httpMethod: "POST" 
                type: "aws_proxy" 
            options: 
              consumes: 
              - "application/json" 
              produces: 
              - "application/json" 
              responses: 
                "200": 
                  description: "200 response" 
                  schema: 
                    $ref: "#/definitions/Empty" 
                  headers: 
                    Access-Control-Allow-Origin: 
                      type: "string" 
                    Access-Control-Allow-Methods: 
                      type: "string" 
                    Access-Control-Allow-Headers: 
                      type: "string" 
              x-amazon-apigateway-integration: 
                responses: 
                  default: 
                    statusCode: "200" 
                    responseParameters: 
                      method.response.header.Access-Control-Allow-Methods: "'*'" 
                      method.response.header.Access-Control-Allow-Headers: "'*'" 
                      method.response.header.Access-Control-Allow-Origin: "'*'" 
                requestTemplates: 
                  application/json: "{\"statusCode\": 200}" 
                passthroughBehavior: "when_no_match" 
                type: "mock" 
 

コメント

このブログの人気の投稿

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-04-30 23:37:32 RSSフィード2021-04-30 23:00 分まとめ(42件)

投稿時間:2023-02-05 02:09:04 RSSフィード2023-02-05 02:00 分まとめ(9件)