問題点
Amazon S3バケットを設定して、ユーザーがフォルダに新しいファイルをドロップするたびにLambda関数をトリガーするようにしました。一見、設定はすべて正しく見えます。しかし実際には、関数が実行されることはありません。CLIを介して手動でトリガーをテストしようとすると、次のエラーに直面します:
An error occurred (AccessDeniedException) when calling the Invoke operation: User: s3.amazonaws.com is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:123456789012:function:my-processor-func
根本原因:ゲストリストの問題
この失敗は、リソースベースのポリシーが不足していることに起因します。多くの開発者はIAM実行ロール(Lambdaが何にアクセスできるか)に集中しがちですが、S3は「プッシュ」モデルを使用します。そのため、S3が関数を呼び出すための明示的な許可が必要になります。
IAMロールをLambdaの身分証明書(その関数ができること)だと考えてください。一方でリソースベースのポリシーは、ドアにあるゲストリストのようなものです。S3サービスプリンシパル(s3.amazonaws.com)がそのゲストリストに載っていなければ、リクエストは即座に拒否されます。
修正方法
方法 1:クイックフィックス(AWS CLI)
add-permissionコマンドを実行することで、数秒で解決できます。これにより、JSONファイルを手動で編集することなく、Lambdaの内部ポリシーにステートメントが追加されます。
aws lambda add-permission \
--function-name my-processor-func \
--statement-id AllowS3Invocation \
--action lambda:InvokeFunction \
--principal s3.amazonaws.com \
--source-arn arn:aws:s3:::my-app-uploads-123 \
--source-account 123456789012
- **--statement-id**: このルールのユニークな名前です(例:`ProductionS3Trigger`)。
- **--source-arn**: セキュリティ上非常に重要です。これにより、*特定の*バケットのみが関数をトリガーできるようになり、他アカウントからの「混乱した代理人(confused deputy)」攻撃を防ぐことができます。
方法 2:視覚的な方法(AWSコンソール)
ウェブインターフェースを好む場合は、S3ダッシュボードではなくLambdaダッシュボードを使用してください。LambdaのUIは、S3の通知ペインよりもはるかに確実に、バックグラウンドでの「権限のハンドシェイク」を処理します。
- **Lambdaコンソール**で関数を開きます。
- **「設定」**タブを選択し、**「権限」**をクリックします。
- **「リソースベースのポリシーステートメント」**セクションを確認します。
- 空の場合は、ページ上部の**「トリガーを追加」**をクリックします。
- **S3**を選択し、バケットを選んで**「追加」**をクリックします。これでAWSが自動的に必要なポリシーステートメントを挿入します。
方法 3:恒久的な修正(Terraform)
手動でボタンをクリックすると「設定のドリフト(乖離)」が発生し、コードと実際のインフラが一致しなくなる可能性があります。aws_lambda_permissionリソースを使用して、この変更を永続的かつ再現可能にします。
resource "aws_lambda_permission" "allow_s3" {
statement_id = "AllowS3Invoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.processor.function_name
principal = "s3.amazonaws.com"
source_arn = "arn:aws:s3:::${var.bucket_name}"
}
検証
生のJSONを確認して、ポリシーが実際に有効であることを再確認します:
aws lambda get-policy --function-name my-processor-func
出力の中で "Service": "s3.amazonaws.com" を探します。確認できたら、1KBのテストファイルをバケットにアップロードしてください。すぐに CloudWatch Logs を確認すると、5〜10秒以内に新しいログストリームが表示されるはずです。
予防のためのプロのヒント
- **ARNを制限する**:`--source-arn`を空のままにしないでください。空にすると、関数のARNを知っている世界中のあらゆるS3バケットが関数をトリガーできる可能性があります。
- **リージョンに注意する**:バケットとLambdaが同じリージョン(例:`us-east-1`)にあることを確認してください。リージョンを跨いだトリガーも存在しますが、権限設定が完璧でない場合、音もなく失敗することがよくあります。
- **循環依存関係を確認する**:CloudFormationを使用している場合、バケット通知とLambda権限を同時に作成すると、デプロイループが発生することがあります。常に権限を先に作成するようにしてください。

