AWS SNS 应该触发我的 lambda,但没有

AWS SNS ought to trigger my lambda, but does not

我有一个通过 apex 创建的 AWS lambda 函数。我还通过 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:

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

它 returns 消息 ID 但没有调用发生。为什么?

我添加了一个内联策略以允许调用 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"
            ]
        }
    ]
}

现在可以使用了。

正如 Robo 在评论中提到的,添加基于 Principal 的权限是最简单的方法:

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

对我来说,问题是我在我的 cloudformation 模板中的 AWS::Lambda::Permission 中指定了 SourceAccount 参数,并且 documentation 声明如下:

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.

我删除 SourceAccount 后,一切正常。

有同样的问题: 1) 创建并部署简单的 lambda 2) 从 java sdk 手动创建 aws sns 主题 3) 从 java sdk 创建 sns 订阅(sns 主题和之间的订阅 λ)

然后我遇到了一个问题,当从控制台向主题推送一些消息时 - 它没有被 lambda 拦截。更重要的是,sns 触发器甚至没有在 lambda 中注册。

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

在 运行 aws lambda add-permission ...... 之后,一切都被拾起并且工作正常。

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 -> Topic 下)检查订阅是否正确(endpoint 必须与 Lambda 的 ARN 完全匹配)

一旦您确认了这些基本检查,但您仍然没有看到任何调用,这一定是权限错误。当您在 AWS 控制台中打开 Lambda 时,您应该会看到 SNS 列为触发器:

对比一下,如果缺少权限,你将看不到SNS:

如果您没有使用自动部署(例如,使用 CloudFormation 或 Terraform),您也可以手动添加缺少的权限:

  1. Add triggers下选择SNS(您需要在列表中向下滚动才能看到它)
  2. Configure triggers、select SNS 主题中
  3. 单击 Add 并保存 Lambda

这个post帮助我走得更远,但还缺了一块。 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", "")}"
}

这是对这个问题的具体回答 - 我已经在其他地方删除了我的其他回答!

对于 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
}