如何修复 AWS CloudFormation 中出现的“Unable to validate the following destination configurations(无法验证以下目标配置)”错误?

上次更新日期:2022 年 5 月 31 日

我订阅 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 存储桶的静态名称。这样就不需要在 SNS 主题策略中包含 {"Ref": "paramBucketName"}。同样可避免 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 模板。