☰ All modules Tier 2 · CDK (Cloud Development Kit) GuideCheat sheet ‹ samsqs-sns-kinesis ›
Contents
  1. CDK fundamentals and synthesis
  2. Constructs and the three levels
  3. Apps, stacks and logical IDs
  4. Grants and IAM permissions
  5. Escape hatches, aspects and tags
  6. Assets and Lambda bundling
  7. Removal policies and stateful resources
  8. Bootstrapping environments
  9. The CDK CLI workflow
  10. Hotswap and watch
  11. Environments and context
  12. Testing with the assertions module
  13. CDK vs SAM and local testing
  14. Numbers to know
  15. Decision rules
  16. Memory hooks

CDK (Cloud Development Kit) — Cheat Sheet (DVA-C02)

Verified against AWS documentation on 2026-10-12 · ⚠️ = changed vs older material: learn the value shown here (older practice material may say otherwise; full list in notes/cdk-guide.md#changed-vs-older-material) · (volatile) = changes often · explanations and examples: notes/cdk-guide.md.

CDK fundamentals and synthesis

Topic Current facts Memory hook
What the CDK is Open-source IaC framework: code → cdk synth → one CloudFormation template per stack → deployed by CloudFormation Code in, CloudFormation out
Supported languages ⚠️ TypeScript, JavaScript, Python, Java, C#/.NET, Go — all stable (older: no Go) Six languages, one template
Installing the CLI ⚠️ npm install -g aws-cdk (Node.js 22.x+ (volatile) needed for every app language); check with cdk --version (older: aws-cdk-lib) CLI = aws-cdk, library = aws-cdk-lib
CDK v2 packages aws-cdk-lib holds every stable module and all L1s; Construct comes from constructs v10; experimental modules are separate -alpha packages One lib, one constructs, alphas aside
CDK v1 status Maintenance from 2022-06-01, end of support 2023-06-01 v1 is gone
CLI versioning ⚠️ CLI ships separately (aws-cdk-cli, 2.1xxx versions); any newer CLI works with a supported, older aws-cdk-lib Upgrade the CLI, never fear
Cloud assembly schema version mismatch The CLI is older than the app's library → upgrade the CLI Old CLI can't read a new assembly
Cloud assembly Templates + asset copies + manifest in cdk.out (-o changes it); cdk deploy --app cdk.out skips re-synthesis cdk.out = build output
App lifecycle Construction → preparation (Aspects) → validation → synthesis (app.synth()); deployment is a separate CLI step Build, prepare, check, write
Tokens Deploy-time values such as bucket.bucketName are placeholders (${Token[TOKEN.21]}) at synth; test with Token.isUnresolved() Tokens = IOUs from CloudFormation
cdk.json app key Command that runs the app (npx ts-node --prefer-ts-exts bin/app.ts, python app.py); missing → --app is required… cdk.json tells the CLI how to run you
AWS::CDK::Metadata Added to every template for version reporting; opt out with --no-version-reporting

Constructs and the three levels

Topic Current facts Memory hook
Construct arguments (scope, id, props): scope = parent (usually this/self), id unique within scope, props optional Where, who, how
L1 (CFN resources) Cfn* classes, 1:1 with a CloudFormation resource type, generated, no defaults; new types and properties can take up to a week to appear L1 = literal CloudFormation
L2 (curated) One primary resource, intent-based props, secure defaults, helpers, generated glue (roles, policies, keys) L2 = leveraged defaults
L3 (patterns) Several resources for one task: apigateway.LambdaRestApi, ecs_patterns.ApplicationLoadBalancedFargateService L3 = lots of resources
LambdaRestApi REST API with Lambda proxy integration; handler required, proxy default true ({proxy+} + ANY) One handler, every route
ALB + Fargate L3 name ⚠️ ecs_patterns.ApplicationLoadBalancedFargateService (older: ApplicationLoadBalancerFargateService, which does not exist) Load-Balanced, not Balancer
Lifecycle helper name ⚠️ bucket.addLifecycleRule() (older: addLifeCycleRule())
lambda.Function default role No role prop → dedicated execution role with AWSLambdaBasicExecutionRole
s3.Bucket with KMS and no key Creates a new kms.Key automatically
Experimental vs beta Experimental L2/L3 = separate @aws-cdk/aws-xxx-alpha packages; new APIs in stable modules = Beta1 suffix Alpha outside, Beta inside
Mixins ⚠️ .with(new s3.mixins.BucketVersioning()) on L1 or L2; Mixins.of(scope).apply(); unsupported targets skipped unless requireAll() Mixins = L2 features for L1s
Construct Hub constructs.dev: public registry of CDK construct libraries

Apps, stacks and logical IDs

Topic Current facts Memory hook
App, Stack, Stage App = the only root; Stack = one CloudFormation stack in one environment; Stage = group of stacks (CDK Pipelines)
Logical ID Path components (stack excluded) + 8-character hash; max 255 characters; an L1 directly in a stack keeps its plain id Path in, ID out
Renaming or moving a construct Logical ID changes → CloudFormation replaces the resource (data loss for stateful resources) New name, new resource
Keeping a resource's identity Inner id Default (dropped from ID and hash), overrideLogicalId(), cdk refactor --unstable=refactor (preview) ⚠️ Default disappears
Explicit physical names Make replacement updates fail; leave names to CloudFormation; PhysicalName.GENERATE_IF_NEEDED for cross-environment refs Named = stuck
Strong cross-stack reference Same environment: export in producer + Fn::ImportValue in consumer (default)
Export in use error Export … cannot be deleted as it is in use by … → keep it one deployment with this.exportValue() Deadly embrace
Weak cross-stack reference ⚠️ Fn::GetStackOutput (no export lock, cross-account and cross-Region); @aws-cdk/core:defaultCrossStackReferences: weak Weak = no lock
Stack dependencies Inferred from references; stack.addDependency(); cdk deploy B deploys B's dependencies unless --exclusively
NestedStack Counts as 1 resource in its parent, holds up to 500; not in cdk list; not deployable alone Way past 500
CfnParameter Discouraged (value known only at deploy time); cdk deploy --parameters Stack:Key=Value Decide at synth, not deploy
Stack outputs new CfnOutput(this, 'Url', { value }); cdk deploy --outputs-file outputs.json

Grants and IAM permissions

Topic Current facts Memory hook
Grants ⚠️ bucket.grants.read(fn), table.grants.readWriteData(fn); L1: s3.BucketGrants.fromBucket(cfnBucket).read(role); grantRead()-style still works, discouraged grants.dot.verb
Lambda invoke permission fn.grantInvoke(principal) is still the documented method
What a grant adds Minimal actions on that resource, plus resource policy or KMS key permissions when needed (bucket read adds decrypt) Grants remember KMS
Custom statements fn.addToRolePolicy(stmt); role.addToPolicy(stmt) (default policy); bucket.addToResourcePolicy(stmt) (needs a principal)
Imported resources Bucket.fromBucketName(), Role.fromRoleArn() are references: their own configuration is never changed Imported = reference only
Imported role grants mutable default true → grants attach a new policy to the role; { mutable: false } → grants dropped (or addGrantsToResources: true)
Deploy approval --require-approval broadening (default) pauses on broadened IAM or security group changes; also never, any-change Broadening asks first
Security-only diff cdk diff --security-only; security changes inside nested stacks are not shown
Grant ordering Grants return iam.Grant; custom.node.addDependency(grant) when a custom resource needs it at deploy time

Escape hatches, aspects and tags

Topic Current facts Memory hook
Reach the L1 inside an L2 const cfn = bucket.node.defaultChild as s3.CfnBucket, then set its typed property (cfn.analyticsConfigurations = […]) defaultChild = the L1 inside
Raw overrides Only when the L1 class lacks the property too, or to delete one. Syntax: cfn.addPropertyOverride('VersioningConfiguration.Status', 'Enabled') with CloudFormation PascalCase paths; addOverride, addDeletionOverride, list index Tags.0.Value Overrides speak CloudFormation
CloudFormation options on an L1 cfn.cfnOptions.metadata, .deletionPolicy, .updatePolicy, .condition
Resource type with no Cfn* class new CfnResource(this, 'X', { type: 'AWS::…', properties: { … } })
Feature missing from CloudFormation Custom resource; AwsCustomResource for a single SDK call Last resort
Un-escape hatch s3.Bucket.fromCfnBucket(cfn) returns an L2 proxy; editing the proxy doesn't change the template
Aspects Aspects.of(scope).add(aspect) with visit(node); run in the preparation phase; stop at Stage; Annotations.of(node).addError() fails synth Aspects inspect everything
Tags Tags.of(scope).add('k', 'v') / .remove(); priority add 100 (direct L1 50), remove 200; exclude beats include; stop at Stage

Assets and Lambda bundling

Topic Current facts Memory hook
File assets lambda.Code.fromAsset(dir) (directory zipped) → bootstrap S3 bucket at cdk deploy
Docker image assets DockerImageAsset({ directory }) or ecs.ContainerImage.fromAsset(dir) (Dockerfile) → bootstrap ECR repository Files to S3, images to ECR
When assets are published Staged at synth; built and published at deploy, before CloudFormation; cdk publish-assets publishes only Synth stages, deploy ships
Change detection Source hash: unchanged content → no re-upload and no function update Same hash, no change
NodejsFunction esbuild locally if installed, else a Lambda-compatible Docker container; default entry <file>.<id>.ts; @aws-sdk/* excluded on Node 18+ No esbuild → Docker
Asset permissions asset.grantRead(principal); asset.repository.grantPull(principal)
--no-staging Assets are not copied into cdk.out, so the SAM CLI debugs the original source
cdk gc Needs --unstable=gc; --type s3, ecr or all; --action full (default), print, tag or delete-tagged; buffers 1 day created / 0 days rollback gc = garbage-collect assets

Removal policies and stateful resources

Topic Current facts Memory hook
Default for stateful resources RemovalPolicy.RETAIN (orphaned on stack deletion) for S3 buckets, DynamoDB tables and most data stores; CloudFormation's own default is Delete The CDK keeps your data
RemovalPolicy values DESTROY, RETAIN, SNAPSHOT (some resources: databases, EBS volumes), RETAIN_ON_UPDATE_OR_DELETE
Deleting a non-empty bucket DESTROY fails on objects → add autoDeleteObjects: true (requires DESTROY; adds custom resource + Lambda) Empty it to delete it
RETAIN_ON_UPDATE_OR_DELETE Kept on stack deletion and on replacement; deleted if its creation rolls back
Other ways to set it resource.applyRemovalPolicy(); on an L1 cfnOptions.deletionPolicy = CfnDeletionPolicy.DELETE
cdk destroy Asks y/n; --force skips the prompt (not retain policies); --all; leaves CDKToolkit and retained resources Force skips the question, not the policy
Stack termination protection terminationProtection: true in stack props (default false)

Bootstrapping environments

Topic Current facts Memory hook
Scope Run once for each account + Region pair; the result is the CDKToolkit stack Every Region, its own toolkit
Command cdk bootstrap aws://ACCOUNT/REGION (prefix optional, several environments at once); in a project without args → the app's environments
CDKToolkit contents ⚠️ S3 bucket, ECR repo, 5 IAM roles, SSM /cdk-bootstrap/hnb659fds/version; no KMS key by default (older: bucket + roles only) Bucket, repo, five roles, one parameter
Bootstrap roles CloudFormationExecutionRole, DeploymentActionRole, FilePublishingRole, ImagePublishingRole, LookupRole
Qualifier hnb659fds (9 characters); names cdk-hnb659fds-<description>-<account>-<region>; --qualifier isolates names only
Unbootstrapped deploy error ⚠️ SSM parameter /cdk-bootstrap/hnb659fds/version not found … Please run 'cdk bootstrap'
Pipeline target not bootstrapped ⚠️ Policy contains a statement with one or more invalid principals Invalid principals = roles not there
Version check Every template has a BootstrapVersion parameter and a CheckBootstrapVersion rule; fix by re-bootstrapping with a recent CLI
Re-running bootstrap Safe (upgrade or no-op); never delete and recreate CDKToolkit; --termination-protection
Execution policies --cloudformation-execution-policies default AdministratorAccess; mandatory with --trust Admin unless you say otherwise
Trust options --trust <acct> may deploy (re-list all accounts on update); --trust-for-lookup <acct> lookups only
Default deployment flow Caller assumes DeploymentActionRole → passes CloudFormationExecutionRole; publishing roles upload assets; LookupRole serves lookups
Permissions to bootstrap cloudformation:*, ecr:*, ssm:*, s3:*, iam:*
Bootstrapping many accounts cdk bootstrap --show-template → StackSets, Control Tower or aws cloudformation create-stack … --capabilities CAPABILITY_NAMED_IAM

The CDK CLI workflow

Command Current facts Memory hook
cdk init Templates app (default), lib (TypeScript only), sample-app (SQS + SNS); --language; initialises git; --generate-only
cdk ls Lists stack IDs; --long adds stack names and environments
cdk synth Writes the cloud assembly; one stack → YAML on stdout; -q quiet, -j JSON, -e no dependencies Synth = template, no deploy
cdk diff ⚠️ App vs deployed stack (or --template file); --method auto default (read-only change set) or template; --fail exit 1; --security-only Diff = code vs deployed
cdk deploy Synth → publish assets → change set (--method change-set default; direct; prepare-change-set + execute-change-set); unchanged stacks skipped unless --force
Stack selection Names, --all, '*' top-level only, '**' the whole CDK Pipelines hierarchy Two stars for pipelines
Rollback control --no-rollback/-R keeps partial progress (replacements always fail); cdk rollback returns to the last stable state
Other deploy options --outputs-file, --parameters Stack:K=V, --concurrency (default 1), --exclusively, --require-approval, --revert-drift, --express
cdk destroy Confirmation prompt; --force, --all, --exclusively, --concurrency
cdk import Adopts existing resources into a stack via CloudFormation import; model them exactly; no nested stacks; bootstrap v12+ Import = adopt into your stack
cdk migrate Preview; --from-stack, --from-path or --from-scan; --stack-name required → new app with one stack of L1s Migrate = generate a new app
cdk drift Runs CloudFormation drift detection on deployed stacks; --fail exit 1 Drift = reality vs record
Settings precedence Command line > project cdk.json > ~/.cdk.json; credentials and Region come from the AWS CLI env vars and shared files (default profile unless --profile)
CDK Pipelines pipelines.CodePipeline + ShellStep synth (npx cdk synth); self-updating (selfMutation default true); addStage(stage) deploys a Stage, stages in sequence; addWave() stages in parallel Pipeline that rebuilds itself

Hotswap and watch

Topic Current facts Memory hook
cdk deploy --hotswap ⚠️ Calls service APIs instead of CloudFormation; non-hotswappable changes are skipped (older: automatic fallback) Hot = skip CloudFormation
--hotswap-fallback Hotswap where possible, otherwise a full CloudFormation deployment Fallback = safety net
Hotswap coverage ⚠️ (volatile) Lambda code/config/versions/aliases, Step Functions, ECS assets, S3 BucketDeployment, CodeBuild, AppSync, API Gateway, Bedrock, EventBridge rules, DynamoDB, SQS, CloudWatch
Hotswap credentials Your own credentials, not the bootstrap roles
Hotswap side effects Drift, no rollback, ECS minimum healthy 0 % → development only; reconcile with cdk deploy --revert-drift Never in prod
cdk watch = cdk deploy --watch; hotswap on, no fallback unless the flag; streams logs; watch include/exclude and build keys in cdk.json
Express mode cdk deploy --express: CloudFormation without the stabilization wait; rollback off unless --rollback; development only Express = CloudFormation in a hurry

Environments and context

Topic Current facts Memory hook
Environment Account + Region, set per stack with env: { account, region }
Production environments Hard-code env (recommended) Pin prod
CDK_DEFAULT_ACCOUNT / CDK_DEFAULT_REGION Set by the CLI from the active profile; fine for development, not recommended for production Defaults follow whoever runs it
Environment precedence Hard-coded env > env vars used in env > --profile > default profile
Environment-agnostic stacks No lookups, no branching on stack.region; 2 AZs; resolved with AWS::Region / AWS::AccountId Agnostic = two AZs, no lookups
Lookup methods Vpc.fromLookup, HostedZone.fromLookup, StringParameter.valueFromLookup, stack.availabilityZones, LookupMachineImage; need env + credentials (LookupRole)
Lookup cache cdk.context.json: commit it with cdk.json; never hand-edit cached values Commit the cache
SSM value: synth vs deploy time StringParameter.valueFromLookup() = read at synthesis, frozen in cdk.context.json; StringParameter.valueForStringParameter() = token resolved during deployment, no cache Lookup freezes, token follows
cdk context Lists values; --reset <key or number>; --clear (only cdk.context.json)
--context / -c key=value, repeatable; always strings; passed to every stack synthesized; read with node.tryGetContext() CLI context is a string
--no-lookups Synthesis fails instead of querying AWS for a missing lookup
Feature flags Context values in cdk.json; cdk init enables all current flags; survive cdk context --clear; manage with cdk flags
Context sources (6) AWS account lookups, --context, cdk.context.json, cdk.json, ~/.cdk.json, node.setContext()

Testing with the assertions module

Topic Current facts Memory hook
Module and frameworks aws-cdk-lib/assertions (aws_cdk.assertions) with Jest (TS/JS) or pytest (Python)
Fine-grained assertions Check specific properties and counts; most used; catch regressions; suit TDD Fine-grained finds regressions
Snapshot tests Saved full copy of the template, any later difference fails; for refactoring; also break on CDK upgrades, flags, context Snapshot spots any change
Loading a template Template.fromStack(stack); Template.fromJSON(obj) / Template.fromString(jsonText) wrap a template the CDK did not generate
Resource assertions hasResourceProperties (partial), hasResource (incl. DeletionPolicy), resourceCountIs, allResourcesProperties, findResources
Matchers Match.objectLike, objectEquals, arrayWith, anyValue, absent, not, serializedJson, stringLikeRegexp
Capture new Capture() in a pattern, then asString(), asNumber(), asObject()
Annotations Annotations.fromStack(stack).hasError(path, msg), hasWarning, hasNoError
Jest snapshot expect(template.toJSON()).toMatchSnapshot(); accept intended changes with npx jest -u
Running tests npm test, python -m pytest; in-memory synthesis, no deployment or bootstrap

CDK vs SAM and local testing

Topic Current facts Memory hook
AWS SAM Declarative YAML/JSON with Transform: AWS::Serverless-2016-10-31; serverless-first; sam deploy SAM = serverless YAML
AWS CDK Code in 6 languages; every AWS service; cdk deploy CDK = code for everything
Common ground SAM and CDK both end in CloudFormation templates
Local Lambda test of a CDK app cdk synth, then sam local invoke <FunctionId> -t ./cdk.out/<Stack>.template.json (--event, --no-event, --env-vars) Synth first, then SAM
Other SAM local commands sam local start-api -t … (127.0.0.1:3000), sam local start-lambda -t …; Docker required
sam build on a CDK app Needed only for container-image functions; skips NodejsFunction and bundled assets
Function in a nested stack Identify it as NestedStackId/FunctionId
SAM app → CDK Deploy it, then cdk migrate --from-stack; or convert with sam validate --debug, then cdk migrate --from-path

Numbers to know

Item Value Note
CDK v1 end of support 2023-06-01 maintenance from 2022-06-01
Supported languages 6 ⚠️ TypeScript, JavaScript, Python, Java, C#/.NET, Go
Node.js version every CDK developer needs 22.x or later (volatile) whatever the app language
Construct levels 3 L1, L2, L3
Construct arguments 3 scope, id, props
constructs major version for CDK v2 10
Delay before new CloudFormation types appear as L1s Up to 1 week
Logical ID hash length 8 characters appended to the path-based name
Logical ID maximum length 255 characters readable part ≤ 240
Resources per CloudFormation stack 500 a nested stack counts as 1 and holds up to 500
Stacks an unbootstrapped environment accepts None with the v2 default synthesizer (bootstrap version check in every template) "no assets, template < 51,200 bytes" is the CDK v1 rule
Default bootstrap qualifier hnb659fds (9 characters)
Bootstrap stack name CDKToolkit --toolkit-stack-name to change
Bootstrap IAM roles 5 execution, deployment, file publishing, image publishing, lookup
Bootstrap version parameter /cdk-bootstrap/hnb659fds/version SSM Parameter Store
Latest bootstrap template version 32 (CLI 2.1120.0) (volatile)
Bootstrap version needed by cdk rollback v23 or later
Bootstrap version needed by cdk import v12 or later
Noncurrent object retention in the bootstrap bucket 30 days (365 before v24) incomplete multipart uploads deleted after 1 day
Default CloudFormation execution policy AdministratorAccess
Default cloud assembly directory cdk.out
--require-approval default broadening
cdk deploy --method default change-set
cdk diff --method default auto
--concurrency default 1 deploy, destroy, watch
Exit code of cdk diff --fail / cdk drift --fail 1 when differences or drift exist
AZs an environment-agnostic stack sees 2
Context value sources 6
Default prepared change set name cdk-deploy-change-set
cdk gc default buffers created 1 day, rollback 0 days --unstable=gc
Tag priorities add 100 (direct L1 50), remove 200 higher wins
sam local start-api endpoint 127.0.0.1:3000
ECS minimum healthy percent during a hotswap 0 %

Decision rules

Memory hooks