AWS CDK Cheat Sheet
Explains AWS CDK's constructs, stacks, and synthesis model for defining CloudFormation infrastructure using TypeScript or Python.
CDK CLI Basics
Core commands for bootstrapping and deploying a CDK app.
cdk init app --language=typescript # scaffold new projectcdk bootstrap aws://ACCOUNT_ID/REGION # one-time setup per account/regioncdk synth # generate CloudFormation templatecdk diff # compare deployed stack vs local codecdk deploy # deploy the stackcdk destroy # tear down the stack
Define a Stack (TypeScript)
An S3 bucket and Lambda function defined as CDK constructs.
import * as cdk from 'aws-cdk-lib';import * as s3 from 'aws-cdk-lib/aws-s3';import * as lambda from 'aws-cdk-lib/aws-lambda';export class MyStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const bucket = new s3.Bucket(this, 'MyBucket', { versioned: true, removalPolicy: cdk.RemovalPolicy.DESTROY, }); new lambda.Function(this, 'MyFunction', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.handler', code: lambda.Code.fromAsset('lambda'), environment: { BUCKET_NAME: bucket.bucketName }, }); }}
Key Concepts
CDK-specific building blocks.
- Construct- Basic building block; wraps one or more CloudFormation resources
- L1 Construct (CFN Resource)- Direct 1:1 mapping to a CloudFormation resource, e.g. CfnBucket
- L2 Construct- Higher-level, opinionated wrapper with sane defaults, e.g. s3.Bucket
- L3 Construct (Pattern)- Composed of multiple L2 constructs to solve a common architecture pattern
- Stack- Unit of deployment; maps 1:1 to a CloudFormation stack
- App- The root construct tree containing one or more stacks
- Synthesis- The process of turning CDK code into a CloudFormation template (cdk synth)
Aspects for Cross-Cutting Concerns
Use Aspects to visit every construct in a tree and enforce policy, such as mandatory tags or encryption checks.
import { IAspect, Annotations, Tags } from 'aws-cdk-lib';import { IConstruct } from 'constructs';import * as s3 from 'aws-cdk-lib/aws-s3';class RequireBucketEncryption implements IAspect { public visit(node: IConstruct): void { if (node instanceof s3.CfnBucket) { if (!node.bucketEncryption) { Annotations.of(node).addError('Bucket must define encryption'); } } }}// Apply to an entire stack (or app) subtreeAspects.of(myStack).add(new RequireBucketEncryption());// Tags propagate to every taggable resource under the scopeTags.of(myStack).add('CostCenter', 'platform-eng');
Custom Resources for Unsupported APIs
Wrap a Lambda-backed provider to manage resources CloudFormation doesn't natively support.
import * as cr from 'aws-cdk-lib/custom-resources';import * as lambda from 'aws-cdk-lib/aws-lambda';const onEvent = new lambda.Function(this, 'OnEventHandler', { runtime: lambda.Runtime.NODEJS_18_X, handler: 'index.onEvent', code: lambda.Code.fromAsset('custom-resource'),});const provider = new cr.Provider(this, 'Provider', { onEventHandler: onEvent,});new cdk.CustomResource(this, 'ExternalApiCall', { serviceToken: provider.serviceToken, properties: { ApiEndpoint: 'https://internal.example.com/register' },});
Context Values & Environment-Agnostic Lookups
Cache expensive lookups (like existing VPCs) in cdk.context.json so synth doesn't require live AWS calls every run.
import * as ec2 from 'aws-cdk-lib/aws-ec2';// Looked-up value gets cached in cdk.context.json — commit that fileconst vpc = ec2.Vpc.fromLookup(this, 'ExistingVpc', { vpcId: 'vpc-0123456789abcdef0',});// Read a value passed via `cdk deploy -c stage=prod`const stage = this.node.tryGetContext('stage') ?? 'dev';// Force-refresh cached lookups when the real resource changes// $ cdk context --clear && cdk synth
Cross-Stack References
Export a value from one stack and consume it in another within the same App via CloudFormation exports under the hood.
// networking-stack.tsexport class NetworkingStack extends cdk.Stack { public readonly vpc: ec2.Vpc; constructor(scope: cdk.App, id: string) { super(scope, id); this.vpc = new ec2.Vpc(this, 'Vpc'); }}// app-stack.ts — passing the construct reference directlyconst net = new NetworkingStack(app, 'Networking');new AppStack(app, 'App', { vpc: net.vpc });// CDK automatically creates an Fn::ImportValue / Export pair;// destroying the exporting stack while a consumer exists will fail synth
Advanced Concepts
Mechanisms that matter once you move past single-stack apps.
- Aspects- Visitor pattern applied to every node in the construct tree at synth time, used for policy enforcement
- Context (cdk.context.json)- Cached results of environment lookups (AZs, existing VPCs) so synth is deterministic and offline-capable
- Custom Resources- Lambda-backed providers that let CloudFormation manage APIs it has no native resource for
- Escape Hatches- node.defaultChild / addOverride() to modify the underlying CfnResource when an L2 lacks a property
- Asset Bundling- Docker- or command-based bundling of Lambda code/layers at synth time via lambda.Code.fromAsset with bundling options
- Stack Termination Protection- terminationProtection: true prevents accidental cdk destroy or console deletion of critical stacks
- Nested Stacks- NestedStack construct works around the 500-resource-per-stack CloudFormation limit, deployed as a unit with the parent
Run 'cdk diff' before every deploy in CI — it catches unintended resource replacements (like a renamed S3 bucket) that would otherwise cause downtime or data loss during 'cdk deploy'.