CDK Stacks to Multiple AWS Accounts
Deploying a single CDK application to multiple AWS accounts isn’t just about repeating cdk deploy; it’s about mastering the art of managing environments and ensuring consistency across your infrastructure.
Let’s see it in action with a simple S3 bucket.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "A CDK Stack deploying an S3 bucket",
"Resources": {
"MyS3Bucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "my-cdk-example-bucket-prod-123456789012"
}
}
}
}
This JSON represents the CloudFormation template generated by CDK for a single S3 bucket. When you deploy this to multiple accounts, the key is to parameterize or dynamically generate properties like the BucketName to avoid collisions and ensure each account gets its own unique resource.
The core problem CDK solves here is reproducibility and maintainability of infrastructure across different deployment targets. Instead of manually creating buckets in dev, staging, and prod accounts, or maintaining separate CloudFormation files, CDK allows you to define your infrastructure as code once and deploy it everywhere.
Internally, CDK synthesizes your code into CloudFormation templates. For multi-account deployments, you typically use constructs like Account or Environment to specify target AWS accounts and regions. You might also leverage CDK Pipelines for automated deployments to these different environments.
Consider this cdk.json configuration snippet:
{
"app": "npx ts-node bin/my-app.ts",
"context": {
"account": "123456789012",
"region": "us-east-1",
"envName": "prod"
}
}
When deploying to a different account, say staging, you’d override the context values:
cdk deploy --context account=987654321098 --context region=us-west-2 --context envName=staging
This tells CDK to synthesize a CloudFormation template with different account and region parameters, which are then used to configure resource properties like the S3 bucket name or VPC CIDR blocks.
The cdk.json file is crucial for managing these environment-specific configurations. You can have multiple cdk.json files or use a single one with context variables to differentiate deployments. Another powerful approach is to use CDK Pipelines, which abstract away much of this manual context management by defining deployment stages for different accounts and regions.
A common pattern is to define a Stage in your CDK application that represents a complete deployment to a specific environment. This stage would contain your application’s stacks and be configured with the target account and region.
import * as cdk from 'aws-cdk-lib';
import { MyAppStack } from './my-app-stack';
interface MyAppStageProps extends cdk.StageProps {
readonly envName: string;
}
export class MyAppStage extends cdk.Stage {
constructor(scope: cdk.Construct, id: string, props: MyAppStageProps) {
super(scope, id, props);
new MyAppStack(this, 'MyApp', {
envName: props.envName,
// Other stack props
});
}
}
Then, in your bin/my-app.ts, you instantiate this stage for each environment:
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { MyAppStage } from '../lib/my-app-stage';
const app = new cdk.App();
new MyAppStage(app, 'ProdStage', {
envName: 'prod',
account: process.env.CDK_DEFAULT_ACCOUNT, // Or specific account ID
region: process.env.CDK_DEFAULT_REGION, // Or specific region
});
new MyAppStage(app, 'StagingStage', {
envName: 'staging',
account: '987654321098',
region: 'us-west-2',
});
When you run cdk deploy, CDK will synthesize and deploy separate CloudFormation stacks for each MyAppStage instance, effectively deploying your application to multiple accounts and regions.
Most people assume cdk deploy automatically handles account switching or requires manual cdk.json overrides for every single deployment. The reality is that CDK’s strength lies in its ability to define application stages that can be programmatically deployed to different target environments. You can even use CDK Pipelines to automate this process, creating a CI/CD pipeline that deploys your application across all your defined accounts and regions based on code changes.
The next frontier is managing cross-account resource dependencies and permissions, often involving AWS Organizations and IAM roles.