AWS Lambda 関数を AWS CloudFormation スタック内の Amazon Simple Storage Service (Amazon S3) イベント通知、または Amazon Simple Notification Service (Amazon SNS) トピックにサブスクライブできません。AWS::Lambda::EventSourceMapping リソースを使用すると、「イベントソースを認識できません。kinesis または dynamodb ストリームにする必要があります」というエラーが表示されます。
簡単な説明
AWS::Lambda::EventSourceMapping リソースは、Amazon DynamoDB イベントストリームや Amazon Kinesis などのプルベースのイベントソース用に設計されています。Amazon S3 イベント通知や Amazon SNS メッセージなどのプッシュベースのイベントソースでは、イベントソースが Lambda 関数を呼び出す必要があります。プッシュイベントソースが Lambda 関数を呼び出すには、その関数のリソースポリシーがサポートされているイベントソースを承認している必要があります。
解決方法
AWS CloudFormation テンプレートで、AWS::Lambda::Permission リソースを使ってリソースベースのポリシーを追加します。
たとえば、次のリソースベースのポリシーは、Amazon SNS トピックが Lambda 関数を呼び出せるようにします。
"LambdaResourcePolicy": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName" : { "Ref" : "MyFunction" },
"Principal": "sns.amazonaws.com",
"Action": "lambda:InvokeFunction",
"SourceArn" : { "Ref" : "MySNSTopic" }
}
}
Amazon SNS トピックのイベントソースには、必要な許可を持つトピックポリシーを定義する必要があります。
Amazon S3 のイベントソースについては、Lambda 関数を Amazon S3 バケットにサブスクライブする通知設定文が必要です。例:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"BucketPrefix": {
"Type": "String",
"Default": "test-bucket-name"
}
},
"Resources": {
"EncryptionServiceBucket": {
"DependsOn": "LambdaInvokePermission",
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": {
"Fn::Sub": "${BucketPrefix}-encryption-service"
},
"NotificationConfiguration": {
"LambdaConfigurations": [
{
"Function": {
"Fn::GetAtt": [
"AppendItemToListFunction",
"Arn"
]
},
"Event": "s3:ObjectCreated:*",
"Filter": {
"S3Key": {
"Rules": [
{
"Name": "suffix",
"Value": "zip"
}
]
}
}
}
]
}
}
},
"LambdaInvokePermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"AppendItemToListFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "s3.amazonaws.com",
"SourceAccount": {
"Ref": "AWS::AccountId"
},
"SourceArn": {
"Fn::Sub": "arn:aws:s3:::${BucketPrefix}-encryption-service"
}
}
},
"AppendItemToListFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"",
[
"exports.handler = function(event, context) {",
"console.log('Received event: ', JSON.stringify(event, null, 2));",
"};"
]
]
}
},
"Runtime": "nodejs8.10"
}
},
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "root",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:log-group:/path/<log-group-name>:log-stream:<log-stream-name>"
}
]
}
}
]
}
}
}
}
前出の例では、S3 バケットと通知設定が同時に作成されました。この例は、1) IAM ロール、2) Lambda 関数、3) Lambda のアクセス許可、そして 4) S3 バケットという順序でリソースを作成するために Fn::Join 組み込み関数と DependsOn 属性を使用することによって、循環依存を回避します。詳細については、AWS CloudFormation で Lambda イベント通知を使用する際の「Unable to validate the following destination configurations」(次の送信先設定を検証できません) というエラーを回避するにはどうすればよいですか?を参照してください。
関連情報
CloudFormation のベストプラクティス
Lambda のアクセス許可
スタックリソースの更新動作