状況
AWSにおける典型的なフラストレーションの1つです。Lambda functionをconsoleでテストすると、完璧な200 OKが返ってきます。すべて準備万端に見えます。しかし、PostmanやフロントエンドからAPI Gateway endpointをトリガーした瞬間、リクエストは500 Internal Server Errorという壁に突き当たります。
CloudWatchでAPI Gateway Execution Logsを確認すると、原因が見つかります:
Execution failed due to configuration error: Invalid permissions on Lambda function
このエラーは通常、API Gatewayがドアを叩いているのに、Lambdaが中に入れない状態であることを意味します。たとえLambdaにAdministratorAccessを持つIAM Roleが付与されていても、それはLambdaが他のサービスに対して何ができるかを定義しているに過ぎません。不足しているのは、そもそも誰がLambdaをトリガーできるかを定義するResource-based Policyです。
Permission Gapのデバッグ方法
変更を加える前に、policyが存在するかどうかを確認しましょう。terminalで以下のコマンドを実行してください:
aws lambda get-policy --function-name your-function-name
出力を確認します。ResourceNotFoundExceptionが表示されるか、Principalフィールドにapigateway.amazonaws.comが含まれていない場合、接続はブロックされています。API Gatewayには、いわばパーティーへの「招待状」がない状態です。
解決策
Method 1: AWS CLIを使用する(最速の方法)
1つのコマンドで手動により権限を付与できます。これは、問題のあるdev environmentを修正する際によく使われる最も手っ取り早い方法です。プレースホルダーを、us-east-1や12桁のAWS account IDなどの詳細な情報に置き換えてください:
aws lambda add-permission \
--function-name your-function-name \
--statement-id AllowAPIGatewayInvoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-east-1:123456789012:api-id/*/*/*"
末尾の*/*/*はwildcardです。これにより、任意のstage(prodやdevなど)、任意のHTTP method(GET、POSTなど)、およびそのAPI内の任意のresource pathからfunctionをトリガーできるようになります。
Method 2: AWS Consoleを使用する
- Lambda Consoleを開き、functionを選択します。
- Configurationタブに移動し、Permissionsを選択します。
- Resource-based policy statementsセクションを探し、Add permissionsをクリックします。
- AWS Serviceを選択し、ドロップダウンからAPI Gatewayを選択します。
- Statement IDを割り当てます(例:
ProdApiAccess)。 - Principalを
apigateway.amazonaws.comに設定します。 - Source ARNフィールドにAPI Gateway ARNを貼り付けて、アクセスを制限します。これにより、アカウント内の他のAPIが誤ってこのfunctionをトリガーするのを防ぐことができます。
- Saveをクリックします。
Method 3: Infrastructure as Code (Terraform)
consoleでの手動修正は、次のdeployment時に忘れられがちです。Terraformを使用している場合は、必ずaws_lambda_permission resourceを含めるようにしてください。これがAPIとLambdaを接続する、欠けていたリンクとなります。
resource "aws_lambda_permission" "apigw_lambda" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.my_lambda.function_name
principal = "apigateway.amazonaws.com"
# このARNにより、API内の任意のパスにおける任意のメソッドが許可されます
source_arn = "${aws_api_gateway_rest_api.my_api.execution_arn}/*/*"
}
確認
修正を適用した後、API Gateway consoleに戻ってintegrationをテストします:
- APIを選択し、特定のResourceとMethodを選択します。
- Testタブ(稲妻のアイコン)をクリックします。
- Testボタンを押します。
今度は結果が変わるはずです。configuration errorの代わりに、logsにはEndpoint response body before transformationsが表示され、その後に実際のLambda outputが続きます。
学んだ教訓
AWS Consoleは人を欺きます。手動でLambdaをAPI Gatewayにリンクさせると、UIが裏側で小さなポップアップ確認とともに、これらの権限を自動的に追加することがよくあります。しかし、CLI、SAM、Terraformを使用する場合、その「魔法」は起こりません。チェーンのすべてのリンクに対して、自分自身で責任を持つ必要があります。
SAM templateにこれらの権限を追加する際、YAML formattingに苦労していませんか?私はindentation errorsを見つけるために、よくToolCraftのYAML ↔ JSON Converterを使用します。スペースの配置ミスでCloudFormation stackがroll backされるのを5分間待つよりも、ずっと早いです。
結論として、IAM RolesはLambdaが何をするかを制御します。Resource-based policiesは、誰がLambdaを呼び出せるかを制御します。これら2つのコンセプトを分けて考えることで、デバッグの時間を大幅に節約できるでしょう。

