在 Cloud Formation 中创建然后更改资源

问题描述

首先,我对 Cloud Formation 非常陌生。我正在尝试构建一个模板,该模板将部署一个具有两个服务的相当简单的环境。

我需要一个 S3 存储桶,它可以在创建对象时向 SQS 触发消息。创建这些资产时,S3 配置必须包含指向 SQS 队列的指针。但是 SQS Queue 必须有专门允许 S3 存储桶权限的策略。这会产生循环依赖。为了打破这个圈子,我想做到以下几点:

  1. 创建 S3 存储桶
  2. 创建 SQS 队列,引用 S3 存储桶
  3. 修改 S3 存储桶以引用 SQS 队列。

当我尝试这个时,我收到一条错误消息,告诉我它找不到 SQS 队列。当我在 #3 中放入 DependsOn 命令时,它会在循环依赖中出错。

您能否声明一个资源,稍后在模板中使用新参数重新声明它?如果是这样,你会怎么做。我这样做错了吗?

解决方法

在这种情况下导致循环依赖的原因是使用了诸如 RefFn::GetAtt 之类的内在函数,这些函数要求参考资源可用。为避免这种情况,您可以在不引用资源的情况下指定资源 ARN。这是一个示例模板,其中 CloudFormation 执行以下操作:

  1. 创建队列
  2. 添加队列策略以向不存在的存储桶授予权限
  3. 创建存储桶

模板:

Parameters:
  BucketName:
    Description: S3 Bucket name
    Type: String
    Default: mynewshinybucket

Resources:
  Queue:
    Type: AWS::SQS::Queue

  QueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref Queue
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action: SQS:SendMessage
            Resource: !GetAtt Queue.Arn
            Principal:
              AWS: '*'
            Condition:
              ArnLike:
                # Specify bucket ARN by referring to a parameter instead of the actual bucket resource which does not yet exist
                aws:SourceArn: !Sub arn:aws:s3:::${BucketName}

  Bucket:
    Type: AWS::S3::Bucket
    # Create the bucket after the queue policy to avoid "Unable to validate the following destination configurations" errors
    DependsOn: QueuePolicy
    Properties:
      BucketName: !Ref BucketName
      NotificationConfiguration:
        QueueConfigurations:
        - Event: 's3:ObjectCreated:Put'
          Queue: !GetAtt Queue.Arn

编辑: 当使用 Ref/GetAtt/Sub 从另一个资源检索值时,所有这些都需要该资源可用。 CloudFormation 将确保始终在引用资源之后创建使用该函数的资源。通过这种方式检测循环依赖。 Sub 用于字符串替换,但在与参数或资源 (Source) 一起使用时,其工作方式与 Ref 完全相同。

关键是我们指的是一个参数(而不是一个资源),它总是可用的。 在这种情况下,使用 Sub 稍微简单一些,因为使用 Ref 需要额外的 Join。例如,这会给你相同的结果:

            aws:SourceArn: !Join
              - ''
              - - 'arn:aws:s3:::'
                - !Ref BucketName

另一种方法是在不使用任何内在函数的情况下对存储桶 ARN 进行硬编码。重要的是不要引用bucket本身以避免循环依赖。