Reputation: 4168
Help me brothers and sisters!
I've got a CF stack that's pushing some SNS topics and subscriptions. Some of those subscriptions call Lambda functions. After I've pushed the stack, I'll test the SNS topics that calls a lambda by pushing a notification to it manually. The Lambda never fires. When I log the delivery status on the topic, I get something like the following:
{
"notification": {
"messageMD5Sum": "dfdd100c8699626047a347c435c981fa",
"messageId": "423e1faf-088a-55f8-b2dc-4a86703224c9",
"topicArn": "arn:aws:sns:us-east-2:643112374624:Foobar",
"timestamp": "2019-04-11 17:16:40.096"
},
"delivery": {
"deliveryId": "aae702b2-4787-5c7c-87e6-579b3f3f7f67",
"destination": "arn:aws:lambda:us-east-2:642113479024:function:SomeLambdaFunction",
"providerResponse": "{\"ErrorCode\":\"AccessDeniedException\",\"ErrorMessage\":\"User: sns.amazonaws.com is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-2:642113479024:function:SomeLambdaFunction\",\"lambdaRequestId\":\"Unrecoverable\"}",
"dwellTimeMs": 35,
"attempts": 1,
"statusCode": 403
},
"status": "FAILURE"
}
So, it makes complete sense to me that I need to grant SNS (or at least that SNS topic) rights to execute the specific lambda function. How I grant that is a mystery to me. Any help?
When you create the Lambda through the web console, you get this option. What's CF equivalent.
I tried adding a permission to my CloudFormation stack template:
PolicyName:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt { MyLambdaFunctionName, Arn }
Action: 'lambda:InvokeFunction'
Principal: sns.amazonaws.com
SourceArn: !GetAtt { MySNSTopicName, Arn }
When I try to deploy the stack template with the policy in it, it fails with the error:
Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Invalid template property or properties [PolicyName]
Upvotes: 0
Views: 1270
Reputation: 7397
You need to add a AWS::Lambda::Permission
resource with the following properties (JSON):
{
"Type" : "AWS::Lambda::Permission",
"Properties" : {
"Action" : "lambda:InvokeFunction",
"FunctionName" : {
"Fn::GetAtt": [
"<YOURLAMBDA>",
"Arn"
]
},
"Principal" : "sns.amazonaws.com",
"SourceArn": {
"Ref": "<YOURTOPIC>"
}
}
}
or if using YAML:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt YOURLAMBDA.Arn
Principal: sns.amazonaws.com
SourceArn: !Ref YOURTOPIC
This will effectively allow SNS to invoke your Lambda function when the notification comes from your topic.
Upvotes: 2
Reputation: 4168
For the benefit of other people with the same issue looking using the solution in this thread, I've consolidated the resolution below.
We're creating three things:
I also have a Lambda function (FunctionToDoSomething) not defined here.
The CloudFormation template to create these services:
MyTopicSNS:
Type: AWS::SNS::Topic
Properties:
DisplayName: MyTopicSNS
TopicName: MyTopicSNS
SendMessageToLambda:
Type: AWS::SNS::Subscription
Properties:
Endpoint: !GetAtt [ FunctionToDoSomething, Arn ]
Protocol: lambda
TopicArn: !Ref MyTopicSNS
Alarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: Alarm
Namespace: "AWS/SQS"
MetricName: "ApproximateNumberOfMessagesVisible"
Dimensions:
- Name: "QueueName"
Value: { "Fn::GetAtt" : [ "ErrorQueue", "QueueName"] }
Statistic: Sum
Period: 300
EvaluationPeriods: 1
Threshold: 0
ComparisonOperator: GreaterThanThreshold
TreatMissingData: "notBreaching"
AlarmActions:
- !Ref MyTopicSNS
Then I added the following IAM policy to grant the SNS topic rights to execute the Lambda function:
GrantPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt [ FunctionToDoSomething, Arn ]
Principal: 'sns.amazonaws.com'
SourceArn: !Ref MyTopicSNS
Once I did that, messages to the topic triggered the Lambda function.
Upvotes: 0