AWS SNS应该触发我的Lambda函数,但实际上没有触发。

25

我有一个AWS Lambda函数,是通过Apex创建的。我还通过terraform创建了一个SNS主题和一个订阅。

我的主题是:arn:aws:sns:ap-southeast-1:178284945954:fetch_realm_auctions

我有一个订阅:arn:aws:sns:ap-southeast-1:178284945954:fetch_realm_auctions:2da1d182-946d-4afd-91cb-1ed3453c5d86,类型为lambda,终端节点为:arn:aws:lambda:ap-southeast-1:178284945954:function:wowauctions_get_auction_data

我确认这是正确的函数ARN。一切似乎都被正确链接:

SNS图片

我手动触发SNS:

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

它会返回消息ID,但不会发生调用。为什么?


Lambda的监控选项卡是否显示调用次数?如果是,它是否显示错误次数?您是否尝试在SNS主题上添加另一个订阅(例如电子邮件)以确认消息是否已通过SNS发送? - John Rotenstein
2
Lambda函数是否有被SNS调用的权限?这里有一个例子:http://mobile.awsblog.com/post/Tx1VE917Z8J4UDY/Invoking-AWS-Lambda-functions-via-Amazon-SNS - at0mzk
@BretzL 啊,那就是问题所在。谢谢。 - Kit Sunde
很抱歉标题更改了; 我想重新标记,删除 Apex,按照标记准则进行操作,但随后还需要重新编写标题文本! - MikeW
7个回答

16

我添加了一个内联策略,以允许调用Lambda:

{
    "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"
            ]
        }
    ]
}

现在它正在工作。


1
您也可以只需添加 "Principal": "sns.amazonaws.com",以允许任何 SNS 主题调用任何 Lambda 函数。 - Robo
10
我需要一会儿才能理解这个内联策略应该应用于SNS主题。 - giraffe.guru

16

SNS主题需要有权调用Lambda函数。

以下是如何在 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}"
}

解决Lambda不触发的一些常见提示:

  1. 我的消息是否到达Lambda?--订阅您的电子邮件地址到SNS主题。如果您收到电子邮件,您将知道何时消息到达主题。
  2. Lambda是否已订阅该主题?--在AWS控制台中检查(在SNS->主题下)订阅是否正确(endpoint必须与Lambda的ARN完全匹配)。

一旦您确认了这些基本检查,并且仍然看不到调用,那么就必须是权限错误。当您在AWS控制台中打开Lambda时,您应该会看到SNS被列为触发器:

enter image description here

相比之下,如果缺少权限,则您将无法看到SNS:

enter image description here

如果您未使用自动部署(例如CloudFormation或Terraform),您还可以手动添加缺失的权限:

  1. 添加触发器下选择SNS(您需要向下滚动列表才能看到它)
  2. 配置触发器中,选择SNS主题
  3. 点击添加并保存Lambda

5
对我来说,问题在于我在我的CloudFormation模板中在AWS :: Lambda :: Permission中指定了SourceAccount参数,而文档说明如下:(文档链接)

在添加策略时,请勿使用--source-account参数将源帐户添加到Lambda策略中。源帐户不支持Amazon SNS事件源,并将导致访问被拒绝。这对安全没有影响,因为源帐户包含在源ARN中。

一旦我删除了SourceAccount,一切就正常工作了。

1
当然,这节省了我几个小时的时间,但希望 AWS 权限有一定程度的统一性,因为在 Cloud Watch Events 中使用 SourceAccount 是有效的:我是从那里复制的 :) - peter n

3

如Robo在评论中提到的那样,添加基于Principal的权限是实现此目的的最简单方法:

"FooFunctionPermission" : {
    "Type" : "AWS::Lambda::Permission",
    "Properties" : {
        "Action" : "lambda:InvokeFunction",
        "FunctionName" : { "Ref" : "FooFunction" },
        "Principal" : "sns.amazonaws.com"
    }
}

0

遇到了同样的问题: 1)创建并部署简单的Lambda 2)使用Java SDK手动创建AWS SNS主题 3)使用Java SDK创建SNS订阅(在SNS主题和Lambda之间进行订阅)

然后我遇到了一个问题,当我从控制台向主题推送一些消息时,Lambda没有拦截到它。而且更糟糕的是,SNS触发器甚至没有在Lambda中注册。

所以我通过使用这个命令来解决这个问题: https://docs.aws.amazon.com/cli/latest/reference/lambda/add-permission.html

运行aws lambda add-permission ......后,一切都被捕捉并正常工作。


0

这篇文章帮助了我更进一步,但还有一个遗漏的部分。Terraform会创建错误的订阅。你必须删除$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
这是对这个问题的具体回答 - 我已经在其他地方删除了我的另一个答案!
对于Terraform用户,也可以在此处查看: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission 其中显示了使用“aws_lambda_permission”资源; SNS在其中一个示例中被覆盖,如下所述:
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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接