Adam
Adam

Reputation: 4168

Failing to Execute Lambda Function Subscribed to SNS Topic

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.

enter image description here

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

Answers (2)

jogold
jogold

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

Adam
Adam

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:

  • SNS Topic named "MyTopicSNS"
  • SNS Subscription named "SendMessageToLambda" (subscribed to MyTopicSNS)
  • CloudWatch Alarm submitting message to "MyTopicSNS"

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

Related Questions