How to share resources across stacks in AWS CDK

A complete example of how to share resources between CDK stack, in the same AWS CDK App


When building a CDK App, there is a good chance you want to structurize your project and set up multiple stacks when creating the Infrastructure. Therefore it's good to know how you can reference resources across stacks in AWS CDK.

For the example in this blog post we're going to create two stacks:

  • SharedInfraStack, which contains the VPC resource
  • RdsStack which will import the VPC from the SharedInfraStack

Note: if you're still a beginner with AWS CDK. Then I would first recommend you to read my article on How to setup AWS CDK - complete guide.

https://towardsthecloud.com/blog/how-to-set-up-aws-cdk

To be able to share resources between stacks in AWS CDK we need to:

Create SharedInfraStack which provisions the VPC

In the example below I share the share infra stack which provisions the VPC resource including subnets and routing.

// file: lib/shared-infra-stack.ts import * as cdk from '@aws-cdk/core'; import * as ec2 from '@aws-cdk/aws-ec2'; export class SharedInfraStack extends cdk.Stack { public readonly vpc: ec2.Vpc; constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); // assign a VPC to the class property SharedInfraStack this.vpc = new ec2.Vpc(this, 'TheVPC', { cidr: '10.0.0.0/16', natGateways: 1, maxAzs: 3, subnetConfiguration: [ { cidrMask: 20, name: 'public', subnetType: ec2.SubnetType.PUBLIC, }, { cidrMask: 20, name: 'application', subnetType: ec2.SubnetType.PRIVATE, }, { cidrMask: 20, name: 'data', subnetType: ec2.SubnetType.ISOLATED, }, ], }); } }
ts

If you generate the CloudFormation template by running cdk synth you'll see that the following VPC resources are being exported.

# Generated CloudFormation template of the SharedInfraStack Outputs: ExportsOutputRefTheVPCdataSubnet1Subnet62F6C85A8DFF3A46: Value: Ref: TheVPCdataSubnet1Subnet62F6C85A Export: Name: SharedInfraStack:ExportsOutputRefTheVPCdataSubnet1Subnet62F6C85A8DFF3A46 ExportsOutputRefTheVPCdataSubnet2SubnetAE4EF5CAD340846A: Value: Ref: TheVPCdataSubnet2SubnetAE4EF5CA Export: Name: SharedInfraStack:ExportsOutputRefTheVPCdataSubnet2SubnetAE4EF5CAD340846A ExportsOutputRefTheVPC92636AB00B2A4A70: Value: Ref: TheVPC92636AB0 Export: Name: SharedInfraStack:ExportsOutputRefTheVPC92636AB00B2A4A70
yaml

Pass the props of the VPC to the RdsStack we instantiate

In the bin folder where we instantiate the CDK app, we also declare the CDK stacks. Here we make sure to pass the props we just created from the VPC stack and pass them to the new RdsStack that we're going to create.

// file: bin/index.ts import * as cdk from '@aws-cdk/core'; import { SharedInfraStack } from '../lib/shared-infra-stack'; import { RdsStack } from '../lib/rds-stack'; const app = new cdk.App(); // created the SharedInfraStack with the VPC resource that we're going to share by making a variable const infra = new SharedInfraStack(app, 'SharedInfraStack'); // pass the vpc resource from the SharedInfraStack to the RdsStack new RdsStack(app, 'RdsStack', { vpc: infra.vpc, });
ts

Create the RdsStack and import the VPC as prop

Now we'll create the RdsStack that provisions the RDS with the VPC resource we shared across stacks in the previous two steps.

// file: lib/rds-stack.ts import * as cdk from '@aws-cdk/core'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as rds from '@aws-cdk/aws-rds'; // extend the props of the stack by adding the vpc type from the SharedInfraStack export interface RDSStackProps extends cdk.StackProps { vpc: ec2.Vpc; } export class RdsStack extends cdk.Stack { readonly postgreSQLinstance: rds.DatabaseInstance; private vpc: ec2.Vpc; constructor(scope: cdk.Construct, id: string, props: RDSStackProps) { super(scope, id, props); // make the vpc variable accessible const vpc = props.vpc; const cluster = new rds.DatabaseCluster(this, 'Database', { engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_2_08_1, }), credentials: rds.Credentials.fromGeneratedSecret('clusteradmin'), // Optional - will default to 'admin' username and generated password instanceProps: { // optional , defaults to t3.medium instanceType: ec2.InstanceType.of( ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL ), vpcSubnets: { subnetType: ec2.SubnetType.ISOLATED, }, // select the vpc we imported to define the subnets for the RDS vpc, }, }); } }
ts

This is what the end result looks like when we generate the CloudFormation template with cdk synth command:

DatabaseSubnets56F17B9A: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnets for Database database SubnetIds: - Fn::ImportValue: SharedInfraStack:ExportsOutputRefTheVPCdataSubnet1Subnet62F6C85A8DFF3A46 - Fn::ImportValue: SharedInfraStack:ExportsOutputRefTheVPCdataSubnet2SubnetAE4EF5CAD340846A Metadata: aws:cdk:path: RdsStack/Database/Subnets/Default DatabaseB269D8BB: Type: AWS::RDS::DBCluster Properties: Engine: aurora-mysql DBClusterParameterGroupName: default.aurora-mysql5.7 DBSubnetGroupName: Ref: DatabaseSubnets56F17B9A EngineVersion: 5.7.mysql_aurora.2.08.1 MasterUsername: clusteradmin MasterUserPassword: Fn::Join: - '' - - '{{resolve:secretsmanager:' - Ref: RdsStackDatabaseSecretECD539873fdaad7efa858a3daf9490cf0a702aeb - :SecretString:password::}} VpcSecurityGroupIds: - Fn::GetAtt: - DatabaseSecurityGroup5C91FDCB - GroupId UpdateReplacePolicy: Snapshot DeletionPolicy: Snapshot Metadata: aws:cdk:path: RdsStack/Database/Resource
yaml

As you can see in the CloudFormation template we import the VPC value in the RdsStack that we've exported from the SharedInfraStack template.

TL;DR give me the code!

The code for this article is available on GitHub

Share on Twitter

Subscribe to the newsletter

If you're interested in AWS Cloud, Infrastructure as Code, DevOps, and getting certified in AWS then subscribe to my newsletter to get exclusive tips and tricks on becoming a successful Cloud Engineer.

- subscribers