如何避免在 CloudFormation 中使用 Lambda 事件通知时出现的“Unable to validate the following destination configurations”错误?
部署 AWS CloudFormation 模板时,我的堆栈出现故障。然后,我收到类似以下内容的错误:“Unable to validate the following destination configurations”。
简短描述
使用以下资源部署 CloudFormation 模板时,您收到此错误:
- AWS Lambda 函数资源
- Amazon Simple Storage Service (Amazon S3) 存储桶资源,其具有引用 Lambda 函数的 NotificationConfiguration 属性
- Lambda 权限资源,其具有匹配 Lambda 函数和 S3 存储桶的 FunctionName 和 SourceArn 属性
Amazon S3 在创建存储桶时必须验证通知配置。通过检查存储桶是否具有将事件推送到 Lambda 函数的权限来完成验证。权限资源(通过此检查必须存在的权限资源)需要存储桶名称。这也就表示,权限资源与存储桶互相依赖。
**注意:**如果您尝试通过类似于以下示例的代码来实施 DependsOn 资源属性以解决此问题,则会收到“Circular dependency between resources”错误。
以下示例显示了 S3 存储桶资源与 Lambda 权限资源的 SourceArn 属性之间的循环依赖关系。
"Resources": { "MyS3BucketPermission": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", ... ... "SourceArn": { "Ref": "MyS3Bucket" } } }, "MyS3Bucket": { "DependsOn" : "MyS3BucketPermission", ... ...
**重要提示:**最佳实践是向 Amazon S3 事件源的 Lambda 权限资源添加 SourceAccount 属性。添加属性是因为 Amazon S3 的 Amazon Resource Name (ARN) 不包括账户 ID。SourceArn 属性足以满足大多数其他事件源的需求,但对于 Amazon S3 事件源,请考虑添加 SourceAccount 属性。这样可以防止以下情况:用户在您删除存储桶后重新创建存储桶,然后向新的存储桶拥有者授予调用您的 Lambda 函数的完全权限。
解决方法
您可以使用 Fn::Sub 内部函数与堆栈参数来避免循环依赖。您也可以使用 Fn::Join 组合字符串。
在以下示例模板中,S3 存储桶名称 BucketPrefix 是 AWS::S3::Bucket 和 AWS::Lambda::Permission 资源的参数。
**注意:**以下示例假定您的 AWS 账户之前并未使用过该存储桶名称。如果您需要对模板重复使用此代码段,每次都必须提供不同的存储桶前缀。
{ "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": [ "", [ "var response = require('cfn-response');", "exports.handler = function(event, context) {", " var responseData = {Value: event.ResourceProperties.List};", " responseData.Value.push(event.ResourceProperties.AppendedItem);", " response.send(event, context, response.SUCCESS, responseData);", "};" ] ] } }, "Runtime": "nodejs12.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:*:*:*" } ] } } ] } } } }
此模板可以避免循环依赖,因为它会按以下顺序创建资源:
- AWS Identity and Access Management (IAM) 角色
- Lambda 函数
- Lambda 权限
- S3 存储桶
现在,Amazon S3 可正常验证通知配置并创建存储桶。
您还可以尝试以下解决方法:
- 创建没有通知配置的 S3 存储桶,然后将该存储桶添加到下一次堆栈更新中。
- 创建约束较少的 Lambda 权限。例如,通过省略 SourceArn 来允许调用特定 AWS 账户。
- 创建自定义资源以在堆栈工作流程结束时运行。所有其他资源均已创建后,此资源会将通知配置添加到 S3 存储桶。
相关信息
如何避免在 AWS CloudFormation 中出现的“Unable to validate the following destination configurations”错误?
相关内容
- AWS 官方已更新 3 年前