25

I have an AWS lambda function that I created via apex. I've also created a SNS topic and a subscription through terraform.

My topic is: arn:aws:sns:ap-southeast-1:178284945954:fetch_realm_auctions

I have a subscription: arn:aws:sns:ap-southeast-1:178284945954:fetch_realm_auctions:2da1d182-946d-4afd-91cb-1ed3453c5d86 with a lambda type and the endpoint is: arn:aws:lambda:ap-southeast-1:178284945954:function:wowauctions_get_auction_data

I've confirmed this is the correct function ARN. Everything seems wired up correctly:

SNS picture

I trigger SNS manually:

aws sns publish 
  --topic-arn arn:aws:sns:ap-southeast-1:178284945954:fetch_realm_auctions 
  --message '{"endpoint": "https://us.api.battle.net", "realm": "spinebreaker"}'

It returns the message ID but no invocation happens. Why?

MikeW
  • 5,504
  • 1
  • 34
  • 29
Kit Sunde
  • 35,972
  • 25
  • 125
  • 179
  • Does the Lambda **Monitoring** tab show an invocation count? If so, does it show an error count? Have you tried putting another subscription on the SNS topic (eg email) to confirm that the message is being sent in SNS? – John Rotenstein Sep 26 '16 at 02:43
  • 2
    Does the Lambda function have the permission to be invoked by SNS? There is an example here: http://mobile.awsblog.com/post/Tx1VE917Z8J4UDY/Invoking-AWS-Lambda-functions-via-Amazon-SNS – at0mzk Sep 26 '16 at 06:47
  • @BretzL Ah that's the issue. Thanks. – Kit Sunde Sep 26 '16 at 07:17
  • Sorry about title change; I wanted to re-tag, to remove the Apex, going with Tag guidelines, but was then also required to re-write the title text ! – MikeW Dec 17 '20 at 11:01

7 Answers7

16

I added an inline policy to allow the lambda to be invoked:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1474873816000",
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "arn:aws:lambda:ap-southeast-1:178284945954:function:wowauctions_get_auction_data"
            ]
        }
    ]
}

And it's now working.

Kit Sunde
  • 35,972
  • 25
  • 125
  • 179
  • 1
    You can also just add `"Principal": "sns.amazonaws.com"` to allow any sns topic to invoke any lambda function – Robo Sep 26 '16 at 09:33
  • 10
    Took me a moment to work out that this inline policy should be applied to the _SNS topic_. – giraffe.guru Nov 07 '17 at 03:02
15

The SNS topic needs to have the permission to invoke the Lambda.

Here is an example how you can express that in Terraform:

# Assumption: both SNS topic and Lambda are deployed in the same region
# resource "aws_sns_topic" "instance" { ... }
# resource "aws_lambda_function" "instance" {... }

# Step 1: Allow the SNS topic to invoke the Lambda
resource "aws_lambda_permission" "allow_invocation_from_sns" {
  statement_id  = "AllowExecutionFromSNS"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.instance.function_name}"
  principal     = "sns.amazonaws.com"
  source_arn    = "${aws_sns_topic.instance.arn}"
}

# Step 2: Subscribe the Lambda to the SNS topic
resource "aws_sns_topic_subscription" "instance" {
  topic_arn = "${aws_sns_topic.instance.arn}"
  protocol  = "lambda"
  endpoint  = "${aws_lambda_function.instance.arn}"
}

Some general tips for troubleshooting this problem (a Lambda not being fired):

  1. Does my message arrive at the Lambda? -- Subscribe your email address to the SNS topic. If you get emails, you will know when messages arrive at the topic.
  2. Is the Lambda subscribed to the topic? -- Check in the AWS console (under SNS -> Topic) whether the subscription is correct (the endpoint must exactly match the ARN of the Lambda)

Once you confirmed these basic checks and you still see no invocations, it has to be a permission error. When you open the Lambda in the AWS console, you should see SNS listed as a trigger:

enter image description here

For comparison, if the permission is missing, you will not see SNS:

enter image description here

If you are not using an automated deployment (e.g., with CloudFormation or Terraform), you can also manually add the missing permission:

  1. Choose SNS under Add triggers (you will need to scroll down in the list to see it)
  2. In Configure triggers, select the SNS topic
  3. Click Add and save the Lambda
Community
  • 1
  • 1
Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239
5

For me the problem was that I specified SourceAccount parameter inside AWS::Lambda::Permission in my cloudformation template and documentation states the following:

Do not use the --source-account parameter to add a source account to the Lambda policy when adding the policy. Source account is not supported for Amazon SNS event sources and will result in access being denied. This has no security impact as the source account is included in the source ARN.

As soon as I removed SourceAccount, everything worked fine.

dmitryb
  • 281
  • 2
  • 12
  • 1
    Sure this saved me a couple of hours, wish there was some level of uniformity with AWS permissions, as having the SourceAccount worked for Cloud Watch Events: where I was copying from :) – peter n Jan 27 '19 at 23:50
3

As Robo mentioned in the comments, adding a Principal based permission is the simplest way of doing this:

"FooFunctionPermission" : {
    "Type" : "AWS::Lambda::Permission",
    "Properties" : {
        "Action" : "lambda:InvokeFunction",
        "FunctionName" : { "Ref" : "FooFunction" },
        "Principal" : "sns.amazonaws.com"
    }
}
Jonathan
  • 13,947
  • 17
  • 94
  • 123
0

Had the same issue: 1) Created and deployed simple lambda 2) Created aws sns topic manually from java sdk 3) Created sns subscription from java sdk (subscription between sns topic and lambda)

Then I had a problem, when pushed some message to the topic from the console - it was not intercepted by lambda. And more, sns trigger was not even registered in the lambda.

So I fixed this simply by using this command: https://docs.aws.amazon.com/cli/latest/reference/lambda/add-permission.html

After running aws lambda add-permission ......, everything were picked up and working fine.

Gleb S
  • 403
  • 1
  • 5
  • 13
0

This post helped me to get farther, but there is a missing piece. Terraform will create the wrong subscription. You must drop $LATEST

resource "aws_sns_topic" "cloudwatch_notifications" {
  name = "aws-${var.service_name}-${var.stage}-alarm"
}

data "aws_lambda_function" "cloudwatch_lambda" {
  function_name = "sls-${var.service_name}-${var.stage}-cloudwatch-alarms"
}

resource "aws_lambda_permission" "with_sns" {
  statement_id  = "AllowExecutionFromSNS"
  action        = "lambda:InvokeFunction"
  function_name = "${replace(data.aws_lambda_function.cloudwatch_lambda.arn, ":$LATEST", "")}"
  principal     = "sns.amazonaws.com"
  source_arn    = "${aws_sns_topic.cloudwatch_notifications.arn}"
}

resource "aws_sns_topic_subscription" "cloudwatch_subscription" {
  topic_arn = "${aws_sns_topic.cloudwatch_notifications.arn}"
  protocol  = "lambda"
  endpoint  = "${replace(data.aws_lambda_function.cloudwatch_lambda.arn, ":$LATEST", "")}"
}
0

This is a specific answer to this question - I have removed my other answer elsewhere !

For Terraform users, see also here: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission

which shows use of the 'aws_lambda_permission' resource; SNS is covered in one of the examples, copied here:

resource "aws_lambda_permission" "with_sns" {
  statement_id  = "AllowExecutionFromSNS"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.func.function_name
  principal     = "sns.amazonaws.com"
  source_arn    = aws_sns_topic.default.arn
}
MikeW
  • 5,504
  • 1
  • 34
  • 29