如何避免在 AWS CloudFormation 中出现“Unable to validate the following destination configurations”错误?
上次更新时间:2020 年 9 月 9 日
我订阅 Amazon Simple Storage Service (Amazon S3) 事件通知的 Amazon Simple Notification Service (Amazon SNS) 主题或 AWS Lambda 函数。然后,我收到以下错误:“Unable to validate the following destination configurations.” 尝试从模板中的 S3 存储桶设置与 SNS 主题策略的依赖关系时,我收到循环依赖关系验证错误。
简短描述
因为 AWS CloudFormation 处理依赖关系排序的方式,Amazon S3 事件通知定义为 S3 存储桶的属性。在创建 S3 存储桶资源时会建立这些通知。
要避免错误,您必须按以下顺序创建资源:
- 创建 SNS 主题,因为 S3 存储桶参考 SNS 主题。
- 创建 S3 存储桶,因为 SNS 主题策略同时参考 S3 存储桶和 SNS 主题。
在订阅 S3 事件通知的 SNS 主题之前,您必须使用适当的权限指定主题策略 (AWS::SNS::TopicPolicy)。创建订阅之前,该主题策略必须已存在。
请尝试以下其中一个策略,以避免出现“Unable to validate the following destination configurations”错误:
- 在 AWS CloudFormation 模板中指定 BucketName 的值
- 创建堆栈,然后执行堆栈更新
解决方法
在 AWS CloudFormation 模板中指定 BucketName 的值
通过为 AWS CloudFormation 模板的 S3Bucket 资源中的 BucketName 属性指定值,来使用 S3 存储桶的静态名称。这样,就不需要将 {"Ref": "paramBucketName"} 包括在 SNS 主题策略中,并删除 SNS 主题策略与 Amazon S3 之间的建立内部依赖关系。
以下示例 AWS CloudFormation 模板指定了 BucketName 属性的硬编码值 (-Bucket-Name-)。在您的模板中,将 -Bucket-Name- 的所有实例替换为您的存储桶名称。
{
"Resources": {
"SNSTopic": {
"Type": "AWS::SNS::Topic"
},
"SNSTopicPolicy": {
"Type": "AWS::SNS::TopicPolicy",
"Properties": {
"PolicyDocument": {
"Id": "MyTopicPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement-id",
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": "sns:Publish",
"Resource": { "Ref": "SNSTopic" },
"Condition": {
"ArnLike": {
"aws:SourceArn": { "Fn::Join": [ "", [ "arn:aws:s3:::", "-Bucket-Name-" ]]} }
}
}
]
},
"Topics": [ { "Ref": "SNSTopic" } ]
}
},
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"DependsOn": ["SNSTopicPolicy"],
"Properties": {
"AccessControl": "BucketOwnerFullControl",
"BucketName": "-Bucket-Name-",
"NotificationConfiguration": {
"TopicConfigurations": [
{
"Topic": { "Ref": "SNSTopic" },
"Event": "s3:ObjectCreated:Put"
}
]
}
}
}
}
}
注意:S3Bucket 资源具有已设置为 SNSTopicPolicy 的显式 DependsOn 值。此属性会指定模板将在 S3Bucket 资源之前创建 SNSTopicPolicy 资源。
要将相同的 AWS CloudFormation 模板用于名称不同的 S3 存储桶,请为存储桶名称定义参数。通过参数可以将相同模板用于不同的存储桶名称,方法是在堆栈创建期间以参数形式传递存储桶名称。
要使用以下示例模板,您必须在堆栈创建期间以 paramBucketName 参数形式传递存储桶名称。
{
"Parameters": {
"paramBucketName": {
"Type": "String",
"Description": "Bucket Name"
}
},
"Resources": {
"SNSTopic": {
"Type": "AWS::SNS::Topic"
},
"SNSTopicPolicy": {
"Type": "AWS::SNS::TopicPolicy",
"Properties": {
"PolicyDocument": {
"Id": "MyTopicPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement-id",
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": "sns:Publish",
"Resource": { "Ref": "SNSTopic" },
"Condition": {
"ArnLike": {
"aws:SourceArn": { "Fn::Join": [ "", [ "arn:aws:s3:::", {"Ref": "paramBucketName"} ]]} }
}
}
]
},
"Topics": [ { "Ref": "SNSTopic" } ]
}
},
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"DependsOn": ["SNSTopicPolicy"],
"Properties": {
"AccessControl": "BucketOwnerFullControl",
"BucketName": {"Ref": "paramBucketName"},
"NotificationConfiguration": {
"TopicConfigurations": [
{
"Topic": { "Ref": "SNSTopic" },
"Event": "s3:ObjectCreated:Put"
}
]
}
}
}
}
}
创建堆栈,然后执行堆栈更新
将堆栈创建分成两个阶段。首先,创建堆栈,但不在 S3Bucket 资源中指定 NotificationConfiguration 属性。然后,执行堆栈更新以添加 S3 事件通知。这可避免在创建 SNS 主题策略之前设置 S3 事件通知。
1. 创建所有资源,包括 SNS 主题策略。例如:
{
"Resources": {
"SNSTopic": {
"Type": "AWS::SNS::Topic"
},
"SNSTopicPolicy": {
"Type": "AWS::SNS::TopicPolicy",
"Properties": {
"PolicyDocument": {
"Id": "MyTopicPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement-id",
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": "sns:Publish",
"Resource": { "Ref": "SNSTopic" },
"Condition": {
"ArnLike": {
"aws:SourceArn": { "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "S3Bucket" } ] ]
}
}
}
}
]
},
"Topics": [ { "Ref": "SNSTopic" } ]
}
},
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "BucketOwnerFullControl"
}
}
}
}
2. 更新堆栈以添加 S3 事件通知。例如:
{
"Resources": {
"SNSTopic": {
"Type": "AWS::SNS::Topic"
},
"SNSTopicPolicy": {
"Type": "AWS::SNS::TopicPolicy",
"Properties": {
"PolicyDocument": {
"Id": "MyTopicPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement-id",
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": "sns:Publish",
"Resource": { "Ref": "SNSTopic" },
"Condition": {
"ArnLike": {
"aws:SourceArn": { "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "S3Bucket" } ] ]
}
}
}
}
]
},
"Topics": [ { "Ref": "SNSTopic" } ]
}
},
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "BucketOwnerFullControl",
"NotificationConfiguration": {
"TopicConfigurations": [
{
"Topic": { "Ref": "SNSTopic" },
"Event": "s3:ObjectCreated:Put"
}
]
}
}
}
}
}
注意:您可以使用 AWS CloudFormation Template Flip,在 JSON 和 YAML 格式之间转换 AWS CloudFormation 模板。