DEV Community

Binh Bui
Binh Bui

Posted on • Edited on • Originally published at codewithyou.com

AWS CDK Removing automatic cross-stack references

What is automatic cross-stack references?

The automatic references created by CDK when you use resources across stacks are convenient, but may block your deployments if you want to remove the resources that are referenced in this way. You will see an error like:

Export Stack1:ExportsOutputFnGetAtt-****** cannot be deleted as it is in use by Stack1
Enter fullscreen mode Exit fullscreen mode

Let's say there is a Bucket in the stack1, and the stack2 references its bucket.bucketName.
You see full demo code in the GitHub repository

export class Stack1 extends Stack {
  readonly bucket: s3.Bucket
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    // Let's say there is a Bucket in the stack1, and the stack2 references its bucket.bucketName.
    const bucket = new s3.Bucket(this, 'bucket', {
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    })

    this.bucket = bucket
  }
}

export class Stack2 extends Stack {
  constructor(scope: Construct, id: string, props: Stack2Props) {
    super(scope, id, props)

    // comment out the following line and re-deploy to see the error
    //  ❌  Stack1 failed: Error: The stack named Stack1 failed to deploy: UPDATE_ROLLBACK_COMPLETE
    new CfnOutput(this, 'bucketName', {
      value: props.bucket.bucketName,
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Deploy all stacks by running npx cdk deploy --all

You now want to remove the bucket due to not using any more. It's not safe to remove stack1.bucket while stack2 is still using it.
To reproduce this, You can remove all references to stack1.bucket then re-deploy by running npx cdk deploy --all. It will show the following error:

❌  Stack1 failed: Error: The stack named Stack1 failed to deploy: UPDATE_ROLLBACK_COMPLETE
Enter fullscreen mode Exit fullscreen mode

So unblocking yourself from this is a two-step process. This is how it works:

DEPLOYMENT 1: break the relationship

  • Make sure stack2 no longer references bucket.bucketName (maybe the consumer stack now uses its own bucket, or it writes to an AWS DynamoDB table, or maybe you just remove the Lambda Function altogether).
  • In the stack1 class, call this.exportValue(this.bucket.bucketName). This will make sure the CloudFormation Export continues to exist while the relationship between the two stacks is being broken.
export class Stack1 extends Stack {
  readonly bucket: s3.Bucket
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props)

    // Let's say there is a Bucket in the stack1, and the stack2 references its bucket.bucketName.
    const bucket = new s3.Bucket(this, 'bucket', {
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    })

    this.bucket = bucket

    // Uncomment the following line
    // This will make sure the CloudFormation Export continues to exist while the relationship between the two stacks is being broken.
    this.exportValue(bucket.bucketName)
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Deploy (this will effectively only change the stack2, but it's safe to deploy both).
npx cdk deploy --all
Enter fullscreen mode Exit fullscreen mode

DEPLOYMENT 2: remove the resource

  • You are now free to remove the bucket resource from stack1.
  • Don't forget to remove the exportValue() call as well.
  • Deploy again (this time only the stack1 will be changed -- the bucket will be deleted).

That all, don't forget to remove clean all resources to avoid any unexpected AWS cost.

npx cdk destroy --all
Enter fullscreen mode Exit fullscreen mode

Thanks for reading! I hope you found this helpful. If you have any questions please leave a comment. I will try to reply as soon as possible.

You can find the original post on My Blog.

Top comments (0)