跳至内容

如何解决将 Lambda 函数订阅到 CloudFormation 中基于推送的事件源时收到的错误?

2 分钟阅读
0

我无法将我的 AWS Lambda 函数订阅到我的 AWS CloudFormation 堆栈中的 Amazon Simple Storage Service (Amazon S3) 事件通知或 Amazon Simple Notification Service (Amazon SNS) 主题。

简短描述

如果您使用 AWS::Lambda::EventSourceMapping 资源订阅您的 Lambda 函数,则可能会收到以下错误:"Unrecognized event source, must be kinesis or dynamodb stream."

该资源专为基于拉取的事件源而设计,例如 Amazon DynamoDB 事件流和 Amazon Kinesis。当您使用基于推送的事件源(例如 Amazon S3 事件通知或 Amazon SNS 消息)时,事件源会调用 Lambda 函数。要使推送事件源调用 Lambda 函数,该函数的资源策略必须授权可以调用 Lambda 函数的服务

解决方法

在您的 CloudFormation 模板中,使用 AWS::Lambda::Permission 资源添加基于资源的策略

例如,以下基于资源的策略允许 Amazon SNS 主题调用 Lambda 函数:

"LambdaResourcePolicy": {  "Type": "AWS::Lambda::Permission",
  "Properties": {
    "FunctionName" : { "Ref" : "MyFunction" },
    "Principal": "sns.amazonaws.com",
    "Action": "lambda:InvokeFunction",
    "SourceArn" : { "Ref" : "MySNSTopic" }
  }
}

对于 Amazon SNS 主题事件源,您必须定义具有所需权限的主题策略

对于 Amazon S3 事件源,您必须有将 Lambda 函数订阅到 Amazon S3 存储桶的通知配置语句。例如:

{  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "BucketPrefix": {
      "Type": "String",
      "Default": "test-bucket-name"
    }
  },
  "Resources": {
    "EncryptionServiceBucket": {
      "DependsOn": "LambdaInvokePermission",
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::Sub": "${BucketPrefix}-encryption-service"
        },
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Function": {
                "Fn::GetAtt": [
                  "AppendItemToListFunction",
                  "Arn"
                ]
              },
              "Event": "s3:ObjectCreated:*",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "suffix",
                      "Value": "zip"
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    },
    "LambdaInvokePermission": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "AppendItemToListFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": "s3.amazonaws.com",
        "SourceAccount": {
          "Ref": "AWS::AccountId"
        },
        "SourceArn": {
          "Fn::Sub": "arn:aws:s3:::${BucketPrefix}-encryption-service"
        }
      }
    },
    "AppendItemToListFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Role": {
          "Fn::GetAtt": [
            "LambdaExecutionRole",
            "Arn"
          ]
        },
        "Code": {
          "ZipFile": {
            "Fn::Join": [
              "",
              [
                "exports.handler = function(event, context) {",
                "console.log('Received event: ', JSON.stringify(event, null, 2));",
                "};"
              ]
            ]
          }
        },
        "Runtime": "nodejs20.x"
      }
    },
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "Policies": [
          {
            "PolicyName": "root",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "logs:*"
                  ],
                  "Resource": "arn:aws:logs:*:*:log-group:/path/<log-group-name>:log-stream:<log-stream-name>"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

在此示例中,S3 存储桶和通知配置是同时创建的。该示例避免了通过 Fn::GetAtt 内置函数和 DependsOn 属性来创建资源的循环依赖。

注意: 如果未指定 DependsOn 属性,CloudFormation 会同时创建 S3 存储桶和 Lambda 权限资源。

资源按以下顺序创建:

  • AWS Identity and Access Management (IAM) 角色
  • Lambda 函数
  • Lambda 权限
  • S3 存储桶

有关详细信息,请参阅如何修复 CloudFormation 中的错误?"Unable to validate the following destination configurations"

相关信息

CloudFormation 最佳实践

Lambda 资源操作权限

更新堆栈资源的行为

AWS 官方已更新 2 年前