How to share Resources between Stacks in AWS CDK

avatar

Borislav Hadzhiev

Wed May 05 20213 min read

banner

Photo by redcharlie

A complete example of how to share resources between CDK stacks, in the same CDK App.

Sharing Resources between Stacks in AWS CDK #

In order to share resources between stacks, in the same CDK app, we have to:

  1. assign the resources we want to share as class properties on stackA
  2. add the types of the class properties to the props object of stackB
  3. instantiate stackA so we can access the class properties
  4. pass the stackA class properties as props when instantiating stackB
  5. reference the properties from the props object of stackB

Let's look at an example where we create 2 stacks and share an S3 bucket between them.

The code for this article is available on GitHub

The code snippet defines the following 2 CDK stacks:

  1. BucketStack provisions an S3 bucket
  2. LambdaStack creates a lambda function and references the shared bucket resources from the BucketStack
lib/cdk-starter-stack.ts
import * as lambda from '@aws-cdk/aws-lambda';
import * as s3 from '@aws-cdk/aws-s3';
import * as cdk from '@aws-cdk/core';
import * as path from 'path';

export class BucketStack extends cdk.Stack {
  // ๐Ÿ‘‡ set a property for the bucket
  public readonly bucket: s3.Bucket;

  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // ๐Ÿ‘‡ assign an S3 bucket to the class property
    this.bucket = new s3.Bucket(this, 'my-bucket', {
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
  }
}

interface LambdaStackProps extends cdk.StackProps {
  bucket: s3.Bucket;
}

export class LambdaStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props: LambdaStackProps) {
    super(scope, id, props);

    const {bucket} = props;

    // ๐Ÿ‘‡ tag the shared bucket
    cdk.Tags.of(bucket).add('environment', 'staging');
    cdk.Tags.of(bucket).add('department', 'accounting');

    const lambdaFunction = new lambda.Function(this, 'lambda-function', {
      runtime: lambda.Runtime.NODEJS_14_X,
      handler: 'index.main',
      code: lambda.Code.fromAsset(path.join(__dirname, '/../src/my-lambda')),
      environment: {
        // ๐Ÿ‘‡ pass bucket name to lambda
        BUCKET_NAME: bucket.bucketName,
      },
    });
  }
}

Let's go over the code snippet.

  1. we defined a BucketStack, which provisions an S3 bucket. The bucket resource is assigned as a class property, so we can access it when we instantiate the class.

  2. we extended the props object of our second stack, by adding the bucket type to it

  3. we defined our LambdaStack, which will receive the shared bucket in the props object. In our LambdaStack we add some tags to the shared bucket and pass its name as an environment variable to a lambda function

The code for this article is available on GitHub

Now let's look at how we instantiate the CDK stacks:

bin/cdk-starter-stack.ts
import * as cdk from '@aws-cdk/core';
import {BucketStack, LambdaStack} from '../lib/cdk-starter-stack';

const app = new cdk.App();

const bucketStack = new BucketStack(app, 'bucket-stack', {
  stackName: 'bucket-stack',
  env: {
    region: process.env.CDK_DEFAULT_REGION,
    account: process.env.CDK_DEFAULT_ACCOUNT,
  },
});

const lambdaStack = new LambdaStack(app, 'lambda-stack', {
  // ๐Ÿ‘‡ pass the S3 bucket from the other stack
  bucket: bucketStack.bucket,
  stackName: 'lambda-stack',
  env: {
    region: process.env.CDK_DEFAULT_REGION,
    account: process.env.CDK_DEFAULT_ACCOUNT,
  },
});

In the code snippet we first instantiate the BucketStack and assign the instance to a variable.

We then instantiate the LambdaStack, passing in the S3 bucket.

At this point we can reference the bucket on the props object of our LambdaStack.

The code for this article is available on GitHub

Lastly, let's add the code for the lambda function at src/my-lambda/index.js:

src/my-lambda/index.js
async function main(event) {
  console.log('BUCKET_NAME ๐Ÿ‘‰', process.env.BUCKET_NAME);

  return {
    body: JSON.stringify({message: `${process.env.BUCKET_NAME} ๐ŸŽ‰`}),
    statusCode: 200,
  };
}

module.exports = {main};

The lambda simply prints the name of the shared bucket.

When deploying the stacks, we have to make sure to deploy the BucketStack first, because we are trying to reference it in our LambdaStack.

Let's deploy the stacks and look at the results:

shell
npx cdk deploy bucket-stack

npx cdk deploy lambda-stack

After the stacks have been deployed, we can see, that CDK has automatically created an Output with the S3 bucket's name to enable us to reference it in our other stack:

bucket stack outputs

The Tags section of our shared S3 bucket shows that the tags we added to it from our second stack have been applied:

shared bucket tags

Finally, if we test our function via the Lambda management console we can see that the function returns the name of the shared bucket:

shared bucket name

Clean up #

When deleting the stacks we have to first delete the LambdaStack and then the BucketStack, because we can't delete a stack that exports an output that is referenced in another stack.

shell
npx cdk destroy lambda-stack

npx cdk destroy bucket-stack

Further Reading #

Join my newsletter

I'll send you 1 email a week with links to all of the articles I've written that week

Buy Me A Coffee