Home
Published on

Cross-stack references in CDK

Author
    Roman Naumenko
    Name
    Roman Naumenko

What are cross-stack references, and why can they be convenient in CDK apps and libraries?

Here is a real-world example of a CDK application with a cross-stack reference. A stack deploys an application that backups up and restores configuration from a file. Developers can create environments and deploy their own stacks. However, they prefer to have backups in one location. Maybe in S3 bucket, to have CDK provision applications from a backup passed as a parameter.

Let's create a "producer" stack with an S3 bucket to store backups - this is a shared stack. Here is the code (in TypeScript):

Stack that produces resource

producer.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { IBucket, Bucket } from 'aws-cdk-lib/aws-s3';

export class producerStack extends Stack {
  public readonly bucket: IBucket;
  
  constructor(scope: Construct, id: string, props: StackProps = {}) {
    super(scope, id, props);

    this.bucket = new Bucket(this, 'Bucket');
  }
}

In the application ("consumer") stack, create a new interface that extends the StackProps interface to information about the output. Pass bucket name to the "consumer" stack as an object of props in the stack constructor: 

{ bucket: bucketProducer.bucket }

Here is the code for the "consumer" stack:

Stac that consumes resource.

main.ts
import { App, CfnOutput, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { producerStack } from "./producer";
import { IBucket } from "aws-cdk-lib/aws-s3";
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';

interface consumerStackProps extends StackProps {
  readonly bucket: IBucket;
}

export class consumerStack extends Stack {
  constructor(scope: Construct, id: string, props: consumerStackProps) {
    super(scope, id, props);
  
    new BucketDeployment(this, 'uploadFile', {
      sources: [Source.asset('./src')],
      destinationBucket: props.bucket
    });
    new CfnOutput(this, 'bucketName', { value: props.bucket.bucketName });
  }
}

const app = new App();

const bucketProducer = new producerStack(app, "producer");
new consumerStack(app, "consumer", { bucket: bucketProducer.bucket });

app.synth();

The CDK will do quite a few things under the covers:

Deploy stacks with "cdk deploy --all": voilà, new application stacks can reference the same bucket!

What happens when the "consumer" stack does not need the bucket anymore? Perhaps developers found another way to manage an application.

In earlier versions of CDK, if the reference is removed as shown below and stacks deployed - Cloudformation would fail. Cloudformation has internal validations to prevent removing exports referenced in other stacks. Cloudformation documentation page lists a few "restrictions apply to cross-stack references" - it might be a good idea to check those out before developing complex (for example chained) stacks references.

I tested this example code with CDK version 2.8.0, it removes references automatically. In earlier versions, a tedious manual sequence required:

  • remove the reference in consuming stack
  • deploy consuming stack 
  • create exports manually in producing stack with this.exportValue(this.bucket.bucketArn)
  • deploy all changes with cdk deploy *

GitHub repository provides a minimalistic code to create cross-stack references. There is the remove-dependency branch for testing cross-stack reference.

Conclusion: CDK handles cross-stack references in a convenient, development- and testing-friendly way. And this CDK feature works well in real-world scenarios when stacks share resources.

Next Post

← Back to the blog

Services

Overview
AWS CDK CourseNew

Catenary Cloud

© 2021 Catenary Cloud LLC. Made with ❤️ in Philadelphia