blindy
blindy

Reputation: 55

Create then change a resource in Cloud Formation

To preface this, I'm very new to Cloud Formation. I'm trying to build a template that will deploy a fairly simple environment, with two services.

I need to have an S3 bucket that triggers a message to SQS whenever an object is created. When creating these assets, the S3 configuration must include a pointer to the SQS queue. But the SQS Queue must have a policy that specifically allows the S3 bucket permission. This creates a circular dependency. In order to break this circle I would like to do the following:

  1. Create S3 bucket
  2. Create SQS queue, reference S3 bucket
  3. Modify the S3 bucket to reference SQS queue.

When I try this I get an error telling me it can't find the SQS queue. When I put a DependsOn command in #3 it errors out in a circular dependency.

Can you declare a resource , the re-declare it with new parameters later in the template? If so, how would you do that. Am I approaching this wrong?

Upvotes: 0

Views: 246

Answers (1)

berenbums
berenbums

Reputation: 1354

What leads to circular dependencies in such scenarios is the use of intrinsic functions like Ref or Fn::GetAtt, which require the reference resources to be available. To avoid this, you can specify a resource ARN without referring to a resource. Here is an example template where CloudFormation does the following:

  1. Create a queue
  2. Add a queue policy to grant permissions to a non-existent bucket
  3. Create the bucket

Template:

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

Edit: When using Ref/GetAtt/Sub to retrieve values from another resource, all of them require this resource to be available. CloudFormation will make sure that the resource that uses the function will always be created after the reference resource. This way circular dependencies are detected. Sub is used for string substitution but works exactly as a Ref when used with parameters or resources (Source).

The point is that we are referring to a parameter (and not a resource), which are always available. Using Sub is a bit simpler in this case, because using Ref would require an additional Join. For example this would give you the same result:

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

Another way would be to hard-code the bucket ARN without using any intrinsic functions. The important thing is not to reference the bucket itself to avoid the circular dependency.

Upvotes: 2

Related Questions