如何将 AWS Lambda 函数订阅到 AWS CloudFormation 中基于推送的事件源?

上次更新时间:2019 年 8 月 23 日

我无法将 AWS Lambda 函数订阅到我的 AWS CloudFormation 堆栈中的 Amazon Simple Storage Service (Amazon S3) 事件通知或 Amazon Simple Notification Service (Amazon SNS) 主题。如果使用 AWS::Lambda::EventSourceMapping 资源,我将收到以下错误:“无法识别的事件源,必须是 kinesis 或 dynamodb 流。”

简短描述

AWS::Lambda::EventSourceMapping 资源为基于提取的事件源设计,例如 Amazon DynamoDB 事件流和 Amazon Kinesis。借助基于推送的事件源,例如 Amazon S3 事件通知或 Amazon SNS 消息,事件源负责调用 Lambda 函数。为了使推送事件源调用 Lambda 函数时,该函数的资源策略必须授权受支持的事件源

解决方法

在您的 AWS 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::Join": [
            "",
            [
              "arn:aws:s3:::",
              {
                "Fn::Sub": "${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": "nodejs8.10"
      }
    },
    "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:*:*:*"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

在前面的示例中,S3 存储桶和通知配置同时创建。该示例通过使用 Fn::Join 内部函数和 DependsOn 属性按照以下顺序创建资源,从而避免循环依赖:1) IAM 角色,2) Lambda 函数,3) Lambda 权限,然后是 4) S3 存储桶。有关更多信息,请参阅通过 CloudFormation 部署 Lambda 函数时如何避免循环依赖?


这篇文章对您有帮助吗?

我们可以改进什么?


需要更多帮助吗?