この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
サーバーレス開発部@大阪の岩田です。早いものでクラスメソッドに入社して2週間が経過しました。
先週から実際にプロジェクトに入って、SAMテンプレートをガリガリ書いているのですが、何かとハマることが多く、トライ&エラーを繰り返していました。
概ねテンプレートが作成できたので、備忘を兼ねてハマった箇所をまとめてみます。
概要
API GatewayからLambdaを起動し、DynamoDBにアクセスするという、至ってシンプルなREST APIを作成する案件です。
APIの仕様書をSwaggerで管理したいという要件があったため、APIの詳細についてはSAMテンプレートからSwaggerの定義ファイルを読み込む様な作りで作成しています。
SAMテンプレートでSwaggerを利用すると、AWS::Serverless::FunctionのEventだけでは定義できない様な詳細まで定義できるのですが、その分記述が複雑になり色々とハマってしまいました。
Invalid permissions on Lambda function
デプロイ完了後に、マネジメントコンソールからテストを行ったところ、
Invalid permissions on Lambda function
というエラーが発生し、テストに失敗しました。
Tue May 15 09:03:02 UTC 2018 : Sending request to https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxx:function:xxxxxx/invocations
Tue May 15 09:03:02 UTC 2018 : Execution failed due to configuration error: Invalid permissions on Lambda function Tue May 15 09:03:02 UTC 2018 : Method completed with status: 500
こちらに記載されているように、API GatewayにLambda の InvokeFunctionのアクセス許可が必要になります。 SAMテンプレートの中で、
SomeFunction:
Type: AWS::Serverless::Function
Properties:
#略
Events:
Get:
Type: Api
Properties:
RestApiId: !Ref SomeApi
Path: /hogehoge
Method: GET
の様にEventsの中にType: Apiを記述していると、SAMがよしなにやってくれるのですが、Eventsを定義する際にPathやMethodを記述するのはSwaggerの定義と重複して冗長だな〜と思っていたので、Eventsを定義していませんでした。 こちらの本で紹介されているのと同じ書き方になります。↓
GitHubに上がっているSAMのソースコードを確認したところ、下記の様になっていました。イベントソースにAPIが指定されている場合は勝手にパーミッション作成までやってくれる様です。
class Api(PushEventSource):
"""Api method event source for SAM Functions."""
resource_type = 'Api'
principal = 'apigateway.amazonaws.com'
#略...
def to_cloudformation(self, **kwargs):
"""If the Api event source has a RestApi property, then simply return the Lambda Permission resource allowing
API Gateway to call the function. If no RestApi is provided, then additionally inject the path, method, and the
x-amazon-apigateway-integration into the Swagger body for a provided implicit API.
:param dict kwargs: a dict containing the implicit RestApi to be modified, should no explicit RestApi \
be provided.
:returns: a list of vanilla CloudFormation Resources, to which this Api event expands
:rtype: list
"""
resources = []
#略...
SAMテンプレートに下記の様な記述を追加し、API GatewayにLambda の InvokeFunctionを許可することで解決しています。
LambdaPermissionxxxx:
Type: "AWS::Lambda::Permission"
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref SomeLambdaFunction
Principal: apigateway.amazonaws.com
Execution failed due to configuration error: Malformed Lambda proxy response
上記の対応でPOSTのAPIについてはテストが成功する様になったのですが、今度はGETのAPIで
Execution failed due to configuration error: Malformed Lambda proxy respons
というエラーが発生し、テストに失敗します。
こちらのドキュメントに記載があるのですが、 Swagger のAPI Gateway 拡張を利用してAPI GatewayからLambdaの呼び出しを設定する場合、API Gateway integrationオブジェクトのhttpMethodにはPOSTを設定する必要があります。
SAMテンプレートを下記の様に修正
x-amazon-apigateway-integrationのhttpMethodをGETからPOSTに変更することでエラーが改善しました。
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SomeLambdaFunction.Arn}/invocations
responses:
default:
statusCode: "200"
passthroughBehavior: "when_no_match"
#httpMethod: "GET"
httpMethod: "POST"
contentHandling: "CONVERT_TO_TEXT"
type: "aws_proxy"
API Gatewayのオーソライザーが設定されない
Cognitoで認証済みのユーザーのみAPIを利用させたい箇所があり、API GatewayのオーソライザーにCognitoユーザープールを指定しようとしていました。 Swaggerの定義は下記の様な内容です。
/xxxx/{id}:
get:
summary: "hogehoge"
description: "hogehoge"
security:
- Cognito_Authorizer: []
#略
securityDefinitions:
Cognito_Authorizer:
type: "apiKey"
name: "Authorization"
in: "header"
x-amazon-apigateway-authtype: cognito_user_pools
x-amazon-apigateway-authorizer:
providerARNs:
- "arn:aws:cognito-idp:ap-northeast-1:xxxxxxxxxxx:userpool/ap-northeast-1_xxxxxxxx"
type: cognito_user_pools
が、デプロイしてみると一向にオーソライザーが反映されません。
オチとしては type: "apiKey"
と書くべきところを、 type: "apikey"
と"k"を小文字で書いていたことが原因でした。
ただ、SAMでデプロイした際に、特にエラー等出なかったので、中々気付くことができませんでした。
この事象ですが、マネジメントコンソールから「Swaggerからインポート」を試すことでの原因を切り分け分けることができました。 SAMのデプロイではエラーが出なかったのですが、マネジメントコンソールからだと、下記の様なエラーメッセージが表示されたため、Swaggerの定義を微調整しながら原因を切り分けることができました。
まとめ
いかがだったでしょうか。 経験不足もあって、かなり苦戦しました。 同じ様な境遇のSAM初心者の方の参考になれば幸いです。