如何避免在 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 存储桶资源时会建立这些通知。

要避免错误,您必须按以下顺序创建资源:

  1. 创建 SNS 主题,因为 S3 存储桶参考 SNS 主题。
  2. 创建 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 模板。