I can't subscribe my AWS Lambda function to an Amazon Simple Storage Service (Amazon S3) event notification or Amazon Simple Notification Service (Amazon SNS) topic in my AWS CloudFormation stack. If I use the AWS::Lambda::EventSourceMapping resource, I receive the following error: "Unrecognized event source, must be kinesis or dynamodb stream."
Short description
The AWS::Lambda::EventSourceMapping resource is designed for pull-based event sources, such as Amazon DynamoDB event streams and Amazon Kinesis. With push-based event sources, such as Amazon S3 event notifications or Amazon SNS messages, the event source is responsible for invoking the Lambda function. For a push event source to invoke a Lambda function, the function’s resource policy must authorize a supported event source.
Resolution
In your AWS CloudFormation template, add a resource-based policy using the AWS::Lambda::Permission resource.
For example, the following resource-based policy allows an Amazon SNS topic to invoke a Lambda function:
"LambdaResourcePolicy": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName" : { "Ref" : "MyFunction" },
"Principal": "sns.amazonaws.com",
"Action": "lambda:InvokeFunction",
"SourceArn" : { "Ref" : "MySNSTopic" }
}
}
For an Amazon SNS topic event source, you must define a topic policy with the required permissions.
For an Amazon S3 event source, you must have a notification configuration statement that subscribes the Lambda function to the Amazon S3 bucket. For example:
{
"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>"
}
]
}
}
]
}
}
}
}
In the preceding example, the S3 bucket and notification configuration are created at the same time. The example avoids a circular dependency by using the Fn::Join intrinsic function and the DependsOn attribute to create the resources in the following order: 1) IAM role, 2) Lambda function, 3) Lambda permission, and then 4) S3 bucket. For more information, see How do I avoid the "Unable to validate the following destination configurations" error with Lambda event notifications in AWS CloudFormation?
Related information
CloudFormation best practices
Lambda permissions
Update behaviors of stack resources