☰ All modules Tier 2 · CDK (Cloud Development Kit) GuideCheat sheet ‹ samsqs-sns-kinesis ›
Contents
  1. How CDK (Cloud Development Kit) appears on DVA-C02
  2. 1. CDK fundamentals and the synthesis model
  3. 2. Constructs and the three construct levels
  4. 3. Apps stacks and logical IDs
  5. 4. Grants and IAM permissions
  6. 5. Escape hatches aspects and tags
  7. 6. Assets and Lambda bundling
  8. 7. Removal policies and stateful resources
  9. 8. Bootstrapping environments
  10. 9. The CDK CLI workflow
  11. 10. Hotswap and watch for fast iteration
  12. 11. Environments and context
  13. 12. Testing with the assertions module
  14. 13. CDK vs SAM and local testing with the SAM CLI
  15. Quick decision rules
  16. Changed vs older material

CDK (Cloud Development Kit) — DVA-C02 Mastery Guide

  • Sources: course chapter 19 (CDK). The course serves as the syllabus only; all wording, examples and questions here are original.
  • Written 2026-10-12; values checked against AWS docs on 2026-10-12 (AWS CDK v2 Developer Guide, CDK API Reference, CloudFormation and SAM guides). The marker ⚠️ Changed: <current>. Older material may say <old>. flags a value or behaviour that differs from the course or older practice tests: learn the current value. ⚠️ unverified marks a value that could not be confirmed; (volatile) marks a value AWS changes often. Every change is listed in Changed vs older material; rows you confirm are added to notes/current-facts.md.
  • Tier 2 (deployment and messaging), bank target 30 questions. Domain: Deployment.
  • How to use it: /explain <topic> teaches from these sections (concept → exam facts → developer angle → traps → 3 check questions); /quiz cdk asks bank questions whose source_note links point at the ## N. headings below; /drill cdk fires short recall prompts from notes/cdk-cheat-sheet.md. Suggested loop: read a section and answer its practice questions before opening the answers → /explain what is still unclear → /quiz cdk → /drill cdk.

How CDK (Cloud Development Kit) appears on DVA-C02

DVA-C02 treats the CDK as one of the ways a developer packages and ships an application, next to SAM and plain CloudFormation. It does not test framework internals; it asks whether you know what each command produces, what must exist in the account first, and which construct or setting meets a stated constraint:

Typical question shapes: a scenario with one deciding constraint; an error message to diagnose (SSM parameter /cdk-bootstrap/hnb659fds/version not found, an export that "is in use"); "Which TWO statements…" with five options; the exact command, construct class or method that does the job.

Domain map (Deployment): prepare application artifacts → §1, §6, §9 · test in development environments → §10, §12, §13 · automate deployment testing → §9, §12 · deploy with CI/CD services → §8, §9. The CDK also appears inside Security questions (least-privilege grants §4, bootstrap trust §8) and Troubleshooting questions (logical-ID replacement §3, bootstrap errors §8).

Out of scope here (owned by other modules): CloudFormation template sections, change sets, StackSets, drift and custom resources in depth → see notes/cloudformation-guide.md; SAM templates, sam build/deploy/sync and CodeDeploy traffic shifting → see notes/sam-guide.md; CodePipeline and CodeBuild → see notes/cicd-guide.md; Lambda settings and limits → see notes/lambda-guide.md; IAM policy evaluation and iam:PassRole → see notes/advanced-identity-guide.md; the CLI credential chain and profiles → see notes/ec2-mastery-guide.md §1 and notes/cli-sdk-policies-guide.md. No OS administration or network design.

# Topic Why it is on the exam
1 CDK fundamentals and the synthesis model Code becomes CloudFormation; install, packaging, versions, tokens
2 Constructs and the three construct levels Identify L1 / L2 / L3 and choose the level for "least code" or "full control"
3 Apps stacks and logical IDs Stacks map to CloudFormation stacks; renaming a construct can replace a resource
4 Grants and IAM permissions Least privilege with one method call; security approval of IAM changes
5 Escape hatches aspects and tags Reach CloudFormation features the L2 lacks; enforce rules across an app
6 Assets and Lambda bundling How code and images are packaged and published at deploy time
7 Removal policies and stateful resources Why buckets and tables survive cdk destroy, and how to change that
8 Bootstrapping environments What CDKToolkit contains, per-environment rule, trust for pipelines, bootstrap errors
9 The CDK CLI workflow init, synth, diff, deploy, destroy and the options exam stems use
10 Hotswap and watch for fast iteration Fast dev loops and why they never belong in production
11 Environments and context Account and Region per stack, lookups, cdk.context.json, feature flags
12 Testing with the assertions module Fine-grained vs snapshot tests on the synthesized template
13 CDK vs SAM and local testing with the SAM CLI Choosing the tool; cdk synth then sam local invoke

1. CDK fundamentals and the synthesis model

Concept

Exam facts

Developer angle

npm install -g aws-cdk               # the CLI
cdk --version
mkdir orders-infra && cd orders-infra
cdk init app --language typescript   # bin/, lib/, test/, cdk.json, package.json; git repo if Git is installed
npm run build                        # optional: surface TypeScript errors before synthesis
cdk synth                            # runs the app; YAML to stdout, JSON templates in cdk.out/
#!/usr/bin/env node
import { App, CfnOutput, Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as sqs from 'aws-cdk-lib/aws-sqs';

class OrdersStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const queue = new sqs.Queue(this, 'OrdersQueue', {
      visibilityTimeout: Duration.seconds(300),
    });
    // queueUrl is a token here; CloudFormation supplies the real URL at deploy time
    new CfnOutput(this, 'QueueUrl', { value: queue.queueUrl });
  }
}

const app = new App();
new OrdersStack(app, 'OrdersStack');
app.synth();

Traps

Practice questions

  1. A developer upgrades aws-cdk-lib in a team project to the newest release. Every cdk synth now fails with Cloud assembly schema version mismatch and a message that the maximum supported schema version is lower than the one found. What is the MOST appropriate fix?

    • A. Pin aws-cdk-lib back to the previous release in package.json.
    • B. Upgrade the CDK CLI, for example with npm install -g aws-cdk.
    • C. Delete cdk.context.json so cached values are recomputed during synthesis.
    • D. Run cdk bootstrap again so the environment gets a newer bootstrap template.
  2. A developer new to the CDK reviews how a TypeScript app becomes deployed infrastructure. Which TWO statements are correct? (Select TWO.)

    • A. cdk synth writes a CloudFormation template for each stack into the cloud assembly directory, cdk.out by default.
    • B. cdk deploy creates each resource by calling that service's API directly, without CloudFormation.
    • C. A generated bucket name read in CDK code during synthesis is a token that CloudFormation resolves at deploy time.
    • D. The CDK app keeps running during deployment so it can react when each resource is created.
    • E. A CDK v2 app installs one npm package per AWS service, such as @aws-cdk/aws-s3.
Answers
  1. B. The error means the CLI cannot read the cloud assembly schema the newer library writes; any CLI newer than the library is compatible. Pinning the library back works but is the discouraged direction and loses the upgrade; deleting the context cache and re-bootstrapping do not touch the schema version.
  2. A, C. Synthesis produces templates (and asset copies) in cdk.out, and deploy-time values are tokens. Deployments go through CloudFormation, the app exits before CloudFormation starts (deploy-time code needs a custom resource), and v2 ships all stable modules in the single aws-cdk-lib package.

2. Constructs and the three construct levels

Concept

Exam facts

Developer angle

import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigateway';

export class LevelsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // L1: CloudFormation-shaped; nothing is set unless you set it
    new s3.CfnBucket(this, 'RawBucket', {
      versioningConfiguration: { status: 'Enabled' },
    });

    // L2: intent-based props, secure defaults and helper methods
    const reports = new s3.Bucket(this, 'Reports', {
      versioned: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
    });
    reports.addLifecycleRule({ expiration: Duration.days(90) });

    // L3: REST API + Lambda proxy integration + invoke permissions in one construct
    const handler = new lambda.Function(this, 'ApiHandler', {
      runtime: lambda.Runtime.NODEJS_24_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda'),
    });
    new apigw.LambdaRestApi(this, 'Api', { handler });
  }
}

Traps

Practice questions

  1. A developer must expose one Lambda function through an Amazon API Gateway REST API that forwards every path and method to the function. Which CDK construct meets the requirement with the LEAST code?

    • A. apigateway.CfnRestApi together with CfnResource, CfnMethod and CfnDeployment constructs.
    • B. apigateway.RestApi, calling addResource() and addMethod() for each path.
    • C. apigateway.LambdaRestApi with the function passed as handler.
    • D. fn.addFunctionUrl() with authType set to FunctionUrlAuthType.NONE.
  2. A developer reviews a pull request that mixes s3.CfnBucket, s3.Bucket and ecs_patterns constructs. Which TWO statements are correct? (Select TWO.)

    • A. L3 constructs are published only on Construct Hub and are never part of aws-cdk-lib.
    • B. Classes prefixed with Cfn map one-to-one to a CloudFormation resource type and apply no defaults.
    • C. L1 constructs are hand-written, so a new CloudFormation resource type is usable only after an L2 exists.
    • D. s3.Bucket can create supporting resources, such as a KMS key when KMS encryption is requested without a key.
    • E. Experimental L2 constructs ship inside aws-cdk-lib with a Beta1 suffix on the class name.
Answers
  1. C. The L3 pattern creates the REST API, a proxy resource, the integration and the invoke permission from one handler prop. The L1 route needs every resource spelled out; RestApi needs explicit resources and methods; a function URL is not an API Gateway REST API.
  2. B, D. Cfn* classes are the generated L1 layer with no defaults, and L2s generate glue such as keys. Stable L3 patterns (for example aws-ecs-patterns) are part of aws-cdk-lib; L1s are generated, not hand-written; experimental modules are separate -alpha packages.

3. Apps stacks and logical IDs

Concept

Exam facts

Developer angle

import { App, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as lambda from 'aws-cdk-lib/aws-lambda';

class DataStack extends Stack {
  public readonly table: dynamodb.Table;
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    this.table = new dynamodb.Table(this, 'Orders', {
      partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
    });
  }
}

interface ApiStackProps extends StackProps {
  table: dynamodb.ITable;
}

class ApiStack extends Stack {
  constructor(scope: Construct, id: string, props: ApiStackProps) {
    super(scope, id, props);
    new lambda.Function(this, 'ReadOrders', {
      runtime: lambda.Runtime.NODEJS_24_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda'),
      environment: { TABLE_NAME: props.table.tableName }, // export + Fn::ImportValue
    });
  }
}

const app = new App();
const env = { account: '111122223333', region: 'eu-west-1' };
const data = new DataStack(app, 'DataStack', { env });
new ApiStack(app, 'ApiStack', { env, table: data.table }); // dependency inferred

Preserving a logical ID while refactoring into a construct:

class OrdersData extends Construct {
  public readonly table: dynamodb.Table;
  constructor(scope: Construct, id: string) {
    super(scope, id);
    // 'Default' is dropped from the logical ID, so the table keeps its identity
    this.table = new dynamodb.Table(this, 'Default', {
      partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
    });
  }
}
// in the stack: new OrdersData(this, 'Orders');  // same ID the table used before

Traps

Practice questions

  1. A developer moves a DynamoDB table, previously created in the stack with the construct ID Orders, into a new reusable construct. cdk diff now shows the table being destroyed and a new one created. Which change keeps the existing table with the FEWEST code changes?

    • A. Create the wrapper construct with the ID Orders and give the table the ID Default inside it.
    • B. Set removalPolicy: RemovalPolicy.RETAIN on the table in its new location.
    • C. Set tableName to the current physical table name in the new construct.
    • D. Deploy with cdk deploy --no-rollback so the existing table is left in place.
  2. A team splits a CDK app into NetworkStack and ServiceStack in the same account and Region; ServiceStack receives the VPC object through its props. Which TWO statements describe what the CDK does? (Select TWO.)

    • A. It synthesizes both stacks into a single CloudFormation template.
    • B. It requires crossRegionReferences: true on both stacks before the reference can synthesize.
    • C. It creates an export in NetworkStack and an Fn::ImportValue in ServiceStack.
    • D. It records a dependency, so cdk deploy ServiceStack also deploys NetworkStack unless --exclusively is given.
    • E. It copies the VPC definition into ServiceStack, creating a second VPC.
Answers
  1. A. Default is removed from the logical-ID computation, so Orders/Default produces the same logical ID as the old Orders table. Retaining would orphan the old table and still create a new empty one; an explicit name makes the replacement fail on a name conflict; disabling rollback does not change the planned replacement.
  2. C, D. Same-environment references become an export plus Fn::ImportValue, and the reference adds a deployment dependency. Each stack keeps its own template, crossRegionReferences is only for references across Regions, and the VPC is referenced, not duplicated.

4. Grants and IAM permissions

Concept

Exam facts

Developer angle

The course's image pipeline (upload to S3 → Lambda analyses the image → results stored in DynamoDB), wired with grants:

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';

export class ImageStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const uploads = new s3.Bucket(this, 'Uploads', { encryption: s3.BucketEncryption.KMS });
    const results = new dynamodb.Table(this, 'Results', {
      partitionKey: { name: 'imageId', type: dynamodb.AttributeType.STRING },
    });
    const analyzer = new lambda.Function(this, 'Analyzer', {
      runtime: lambda.Runtime.PYTHON_3_12,
      handler: 'app.handler',
      code: lambda.Code.fromAsset('functions/analyzer'),
      environment: { TABLE_NAME: results.tableName },
    });

    uploads.grants.read(analyzer);           // S3 read actions + kms:Decrypt on the bucket key
    results.grants.readWriteData(analyzer);  // DynamoDB data actions on this table only
    analyzer.addToRolePolicy(new iam.PolicyStatement({
      actions: ['ssm:GetParameter'],
      resources: [`arn:aws:ssm:${this.region}:${this.account}:parameter/images/*`],
    }));
    uploads.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(analyzer));
  }
}
cdk diff ImageStack --security-only           # only broadened IAM / security group changes
cdk deploy ImageStack --require-approval any-change

Traps

Practice questions

  1. A Lambda function defined in a CDK stack must read objects from an S3 bucket in the same stack. The bucket is encrypted with a customer managed KMS key that the stack also creates. Which approach is the MOST secure and needs the least code?

    • A. Attach the AWS managed policy AmazonS3ReadOnlyAccess to the function's execution role.
    • B. Call fn.addToRolePolicy() with s3:GetObject on bucket.arnForObjects('*').
    • C. Add a bucket policy that allows s3:* for the function's execution role.
    • D. Call bucket.grants.read(fn) on the bucket.
  2. A developer reviews the IAM behaviour of an app that defines a lambda.Function, uses a role imported with Role.fromRoleArn(..., { mutable: false }), and deploys with the default CLI settings. Which TWO statements are correct? (Select TWO.)

    • A. Without a role prop, the function gets a dedicated execution role with AWSLambdaBasicExecutionRole.
    • B. addToPolicy() on the imported role attaches the new statement to that role in the account.
    • C. Grant methods always change the resource policy and never the principal's identity policy.
    • D. cdk diff omits IAM changes; only cdk drift reports them.
    • E. cdk deploy asks for confirmation when a change broadens IAM permissions or security group rules.
Answers
  1. D. The grant adds S3 read actions scoped to that bucket and decrypt permission on its key. The managed policy covers every bucket and lacks KMS; the single s3:GetObject statement fails on the encrypted objects without kms:Decrypt; s3:* is a wildcard action.
  2. A, E. L2 functions create their own role, and --require-approval broadening is the default. A non-mutable imported role is not modified, grants work on the principal's policy (adding resource-policy or key permissions only where needed), and cdk diff shows IAM statement and policy tables.

5. Escape hatches aspects and tags

Concept

Exam facts

Developer angle

import { Annotations, Aspects, IAspect, Stack, StackProps, Tags } from 'aws-cdk-lib';
import { Construct, IConstruct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';

class RequireBucketVersioning implements IAspect {
  public visit(node: IConstruct): void {
    if (node instanceof s3.CfnBucket && node.versioningConfiguration === undefined) {
      Annotations.of(node).addError('S3 buckets must enable versioning');
    }
  }
}

export class LogsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    const logs = new s3.Bucket(this, 'Logs', { versioned: true });

    // escape hatch: s3.Bucket has no analytics prop, so set the typed property on the L1 inside it
    const cfnLogs = logs.node.defaultChild as s3.CfnBucket;
    cfnLogs.analyticsConfigurations = [{ id: 'all-objects', storageClassAnalysis: {} }];
    cfnLogs.cfnOptions.metadata = { owner: 'platform-team' };

    Aspects.of(this).add(new RequireBucketVersioning());
    Tags.of(this).add('cost-center', 'orders');
  }
}

Traps

Practice questions

  1. A developer uses the s3.Bucket L2 construct and needs a property that the AWS::S3::Bucket CloudFormation resource supports but the L2 does not expose. Which approach requires the FEWEST code changes?

    • A. Replace the bucket with an AwsCustomResource that calls the S3 API after deployment.
    • B. Cast bucket.node.defaultChild to s3.CfnBucket and set the property on it.
    • C. Export the template, edit it, and convert it back to code with cdk migrate --from-path.
    • D. Edit the synthesized template in cdk.out before each cdk deploy.
  2. A platform team must make synthesis fail for any S3 bucket without versioning and tag every resource in the app's stacks with owner=payments. Which TWO approaches meet these requirements? (Select TWO.)

    • A. Deploy the AWS Config managed rule s3-bucket-versioning-enabled in each account.
    • B. Add a CloudFormation stack policy to every stack that denies updates to buckets without versioning.
    • C. Register an Aspect with Aspects.of(app).add() that calls Annotations.of(node).addError() for non-compliant CfnBucket nodes.
    • D. Run cdk drift --fail in the pipeline before each deployment.
    • E. Call Tags.of(app).add('owner', 'payments').
Answers
  1. B. The escape hatch keeps the L2 and sets the missing CloudFormation property on its L1 in one line (addPropertyOverride() with the CloudFormation path only if even the L1 class lacks it). A custom resource is for things CloudFormation cannot express; regenerating the app with cdk migrate rewrites everything as L1s; cdk deploy re-synthesizes and overwrites hand edits in cdk.out.
  2. C, E. An Aspect with an error annotation fails synthesis, and Tags.of(app) tags every taggable resource below the app. AWS Config detects non-compliance after deployment, stack policies protect resources during updates, and drift detection compares deployed resources with their templates.

6. Assets and Lambda bundling

Concept

Exam facts

Developer angle

import * as path from 'path';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as ecs from 'aws-cdk-lib/aws-ecs';

export class AssetsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // directory asset: zipped and uploaded to the bootstrap bucket during cdk deploy
    new lambda.Function(this, 'Resize', {
      runtime: lambda.Runtime.PYTHON_3_12,
      handler: 'app.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../functions/resize')),
    });

    // bundled asset: esbuild (local, or in Docker if esbuild is missing) builds one file
    new NodejsFunction(this, 'Api', {
      entry: path.join(__dirname, '../functions/api/index.ts'),
      runtime: lambda.Runtime.NODEJS_24_X,
    });

    // Docker image asset: built locally, pushed to the bootstrap ECR repository
    const taskDef = new ecs.FargateTaskDefinition(this, 'TaskDef', { cpu: 512, memoryLimitMiB: 1024 });
    taskDef.addContainer('web', {
      image: ecs.ContainerImage.fromAsset(path.join(__dirname, '../web')),
    });
  }
}
npm install --save-dev esbuild                    # NodejsFunction bundles locally, no Docker needed
cdk deploy AssetsStack                            # build → publish assets → CloudFormation deploy
cdk gc --unstable=gc --type=s3 --action=print     # count unused assets without deleting anything

Traps

Practice questions

  1. A CI job runs cdk deploy for a stack whose NodejsFunction constructs bundle TypeScript handlers. The build image has Node.js but no Docker daemon, and synthesis fails during bundling. Which change fixes the build with the LEAST operational overhead?

    • A. Switch the job to a build image with Docker installed and privileged mode enabled.
    • B. Replace each NodejsFunction with lambda.Code.fromInline() handlers.
    • C. Add esbuild as a development dependency of the CDK project.
    • D. Pre-build the zip files on a developer laptop and commit them to the repository.
  2. A developer's stack uses lambda.Code.fromAsset('src') and a DockerImageAsset. Which TWO statements are correct? (Select TWO.)

    • A. Assets are uploaded during cdk synth, so cdk deploy can run without AWS credentials.
    • B. During cdk deploy, the CLI uploads the zipped directory to the bootstrap S3 bucket and pushes the image to the bootstrap ECR repository before CloudFormation updates the stack.
    • C. If the content of src is unchanged, its source hash stays the same and the function code is not updated.
    • D. Docker image assets are saved as tar archives in the bootstrap S3 bucket.
    • E. Assets deploy to an environment that was never bootstrapped as long as the stack has fewer than 500 resources.
Answers
  1. C. With esbuild available locally, NodejsFunction bundles without Docker. A privileged Docker build image works but adds configuration and security exposure; inline code loses bundling and dependencies; committed build artifacts drift from the source.
  2. B, C. Publishing happens at deploy time to the bucket (files) and repository (images), and unchanged content keeps its hash. Synthesis does not upload, images live in ECR, and assets always need the bootstrap resources.

7. Removal policies and stateful resources

Concept

Exam facts

Developer angle

import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

export class SandboxStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    // only an explicit dev stage deletes data; a missing or mistyped -c stage keeps it
    const isDev = this.node.tryGetContext('stage') === 'dev';

    new s3.Bucket(this, 'Artifacts', {
      removalPolicy: isDev ? RemovalPolicy.DESTROY : RemovalPolicy.RETAIN,
      autoDeleteObjects: isDev,     // valid only together with DESTROY
    });

    new dynamodb.Table(this, 'Sessions', {
      partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
      removalPolicy: isDev ? RemovalPolicy.DESTROY : RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE,
    });
  }
}
cdk deploy -c stage=dev SandboxStack
cdk destroy SandboxStack --force     # bucket emptied and deleted, table deleted
cdk destroy --all                    # every stack of the app, after a y/n confirmation

Traps

Practice questions

  1. Developers run cdk destroy on their personal sandbox stacks every evening, yet S3 buckets (containing test files) and DynamoDB tables from those stacks keep piling up in the account. Which change removes them with the LEAST ongoing effort?

    • A. Add --force to the evening cdk destroy command.
    • B. Set RemovalPolicy.SNAPSHOT on the buckets and tables in sandbox stacks.
    • C. Schedule a Lambda function that finds and deletes orphaned buckets and tables each night.
    • D. In sandbox stacks, set removalPolicy: RemovalPolicy.DESTROY on both resources and autoDeleteObjects: true on the bucket.
  2. A production stack defines a DynamoDB table and an S3 bucket with default settings. Which TWO statements are correct? (Select TWO.)

    • A. Deleting the stack leaves the table and the bucket in the account, no longer managed by CloudFormation.
    • B. The synthesized template sets DeletionPolicy: Delete on both resources, matching CloudFormation's default.
    • C. Setting RemovalPolicy.DESTROY on the bucket alone lets CloudFormation delete it even when it contains objects.
    • D. cdk destroy --force overrides the retain policy and deletes both data stores.
    • E. With RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE, the table is kept when a property change forces replacement, but not when its initial creation is rolled back.
Answers
  1. D. Destroy policies plus automatic object deletion let the existing cdk destroy remove everything. --force only skips the prompt; snapshots are not available for S3 buckets and would still leave data behind; a cleanup function works but is extra code to run and maintain.
  2. A, E. Stateful resources default to retain and are orphaned on stack deletion, and RETAIN_ON_UPDATE_OR_DELETE behaves as described. The CDK default is the opposite of CloudFormation's, non-empty buckets also need autoDeleteObjects, and --force only skips confirmation.

8. Bootstrapping environments

Concept

Exam facts

Developer angle

cdk bootstrap aws://111122223333/eu-west-1 --profile dev
cdk bootstrap aws://111122223333/eu-west-1 aws://111122223333/us-east-1   # two Regions at once

# production account deployed by a pipeline in the tools account 111122223333
cdk bootstrap aws://444455556666/eu-west-1 --profile prod-admin \
  --trust 111122223333 \
  --cloudformation-execution-policies "arn:aws:iam::444455556666:policy/OrdersDeployPolicy" \
  --termination-protection

cdk bootstrap --show-template > bootstrap-template.yaml   # inspect or customise
aws ssm get-parameter --name /cdk-bootstrap/hnb659fds/version --query Parameter.Value --output text
aws cloudformation describe-stacks --stack-name CDKToolkit --query "Stacks[0].Outputs"

A deployer identity in CI mainly needs to assume the bootstrap roles (the CloudFormation execution role does the resource work):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::111122223333:role/cdk-hnb659fds-*"
  }]
}

Traps

Practice questions

  1. A developer runs cdk deploy against a Region the team has never used and gets SSM parameter /cdk-bootstrap/hnb659fds/version not found. The same app deploys without errors to another Region in the same account. What should the developer do?

    • A. Run cdk bootstrap aws://<account-id>/<new-region>.
    • B. Create the SSM parameter manually with a value of 6 in the new Region.
    • C. Delete cdk.context.json and run cdk deploy again.
    • D. Add --role-arn with an administrator role to the cdk deploy command.
  2. A company deploys CDK apps from a tools account (111122223333) with CDK Pipelines into a production account (444455556666). The security team requires that deployments into production can create only the resource types the application uses. Which TWO actions are required when bootstrapping the production account? (Select TWO.)

    • A. Pass --qualifier with a new value to isolate the pipeline's permissions from other bootstrap stacks.
    • B. Pass --trust 111122223333 so the pipeline account can assume the deployment roles.
    • C. Pass --trust-for-lookup 111122223333 instead of --trust so that deployments use read-only roles.
    • D. Pass --cloudformation-execution-policies with a scoped managed policy instead of relying on the AdministratorAccess default.
    • E. Delete and recreate the existing CDKToolkit stack so the new trust settings take effect.
Answers
  1. A. The missing version parameter means the Region was never bootstrapped; bootstrapping creates it along with the bucket, repository and roles. A hand-made parameter leaves the rest missing; the context cache and an administrator role do not create bootstrap resources.
  2. B, D. Trust lets the pipeline account assume the deployment roles, and a scoped execution policy limits what CloudFormation can create. A new qualifier isolates names only, lookup trust cannot deploy, and re-running cdk bootstrap updates the stack in place.

9. The CDK CLI workflow

Concept

Exam facts

Developer angle

cdk ls --long
cdk synth OrdersStack --quiet
cdk diff OrdersStack --fail                       # non-zero exit code if the deploy would change anything
cdk deploy OrdersStack --require-approval never --outputs-file outputs.json
cdk deploy --all --concurrency 3                  # independent stacks in parallel
cdk deploy OrdersStack --method=prepare-change-set --change-set-name review-123
cdk deploy OrdersStack --no-rollback              # iterate without waiting for rollbacks
cdk rollback OrdersStack                          # back to the last stable state
cdk destroy OrdersStack --force

Traps

Practice questions

  1. In a CodeBuild stage, a team wants the build to fail whenever the CDK app would change the deployed production stack, so that someone reviews the change before a separate deploy stage runs. Which command meets this requirement?

    • A. cdk synth ProdStack --quiet
    • B. cdk diff ProdStack --fail
    • C. cdk drift ProdStack --fail
    • D. cdk deploy ProdStack --method=direct
  2. A developer iterates on a development stack. Failed deployments spend several minutes rolling back before the developer can retry. The developer wants to keep the resources that were created successfully after a failure and, when needed, return the stack to its last stable state. Which TWO commands or options should the developer use? (Select TWO.)

    • A. cdk deploy --force
    • B. cdk deploy --no-rollback
    • C. cdk deploy --method=prepare-change-set
    • D. cdk rollback DevStack
    • E. cdk destroy DevStack --exclusively
Answers
  1. B. --fail turns any difference against the deployed stack into exit code 1. Synthesis does not compare with the account, drift detection checks out-of-band changes to live resources rather than pending code changes, and a direct deploy would apply the change instead of stopping it.
  2. B, D. --no-rollback keeps the progress of a failed deployment, and cdk rollback returns the paused stack to its last stable state. --force only redeploys unchanged stacks, a prepared change set does not deploy, and destroying the stack throws away everything.

10. Hotswap and watch for fast iteration

Concept

Exam facts

Developer angle

cdk deploy DevStack --hotswap            # hotswappable changes only; the rest is skipped
cdk deploy DevStack --hotswap-fallback   # hotswap where possible, otherwise a full deployment
cdk watch DevStack                       # continuous: build, hotswap, stream logs
cdk deploy DevStack --revert-drift       # reconcile drift left by hotswaps
cdk deploy DevStack --express            # CloudFormation express mode (no stabilisation wait)
{
  "app": "npx ts-node --prefer-ts-exts bin/orders.ts",
  "build": "npm run build",
  "watch": {
    "include": ["bin/**", "lib/**", "functions/**"],
    "exclude": ["**/*.test.ts"]
  }
}

Traps

Practice questions

  1. A developer changes only the handler code of a Lambda function in a CDK development stack and wants the change live within seconds, without waiting for a CloudFormation stack update. Which command should the developer run?

    • A. cdk deploy DevStack --method=direct
    • B. cdk deploy DevStack --express
    • C. cdk deploy DevStack --hotswap
    • D. cdk synth DevStack --no-staging
  2. A team runs cdk watch against a shared development stack. Which TWO statements are correct? (Select TWO.)

    • A. Hotswapped changes are recorded in the CloudFormation template, so the stack shows no drift afterwards.
    • B. cdk watch is the recommended way to promote changes to production stacks.
    • C. cdk watch works only for TypeScript CDK apps.
    • D. Changes that cannot be hotswapped are skipped unless --hotswap-fallback is used.
    • E. Hotswap API calls are made with the developer's own credentials instead of the bootstrap deployment role.
Answers
  1. C. A hotswap updates the function code through the Lambda API without a stack update. --method=direct and --express are still CloudFormation deployments (faster ones), and synthesis deploys nothing.
  2. D, E. Watch mode hotswaps by default, skips non-hotswappable changes without the fallback flag, and uses the caller's credentials. Hotswaps cause drift, watch mode is for development only, and it works with any CDK language because it drives the same CLI.

11. Environments and context

Concept

Exam facts

Developer angle

#!/usr/bin/env node
import { App } from 'aws-cdk-lib';
import { OrdersStack } from '../lib/orders-stack';

const app = new App();
const stage: string = app.node.tryGetContext('stage') ?? 'dev';

new OrdersStack(app, `Orders-${stage}`, {
  env: stage === 'prod'
    ? { account: '444455556666', region: 'eu-west-1' }   // pinned for production
    : { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});

Inside the stack, a lookup that needs the explicit environment:

const vpc = ec2.Vpc.fromLookup(this, 'SharedVpc', { tags: { Name: 'shared-services' } });
cdk synth -c stage=prod                 # context from the command line (a string)
cdk deploy --profile sandbox            # CDK_DEFAULT_* resolve from this profile
cdk context                             # list cached lookups in cdk.context.json
cdk context --reset 2                   # forget one cached value; refreshed on next synth
cdk context --clear                     # forget all cached values (feature flags stay)
cdk synth --no-lookups                  # fail instead of querying AWS (CI with a committed cache)

Traps

Practice questions

  1. A developer adds ec2.Vpc.fromLookup() to a stack that has no env property, and cdk synth fails with an error that the account and Region are not specified at the stack level. Which change fixes the error and keeps the synthesized template identical on every developer machine and in CI?

    • A. Set env on the stack to the target account ID and Region as literal values.
    • B. Set env on the stack from process.env.CDK_DEFAULT_ACCOUNT and process.env.CDK_DEFAULT_REGION.
    • C. Pass --profile prod to every cdk synth command.
    • D. Run cdk context --clear before each synthesis.
  2. A CI pipeline produces a different template than developers' laptops because the result of a Vpc.fromLookup() call changed in the account. Which TWO practices make synthesis deterministic? (Select TWO.)

    • A. Add cdk.context.json to .gitignore so each environment computes fresh values.
    • B. Commit cdk.context.json to source control with the rest of the app.
    • C. Run cdk synth --no-lookups in CI so a missing cached value fails the build instead of querying AWS.
    • D. Run cdk context --clear at the start of every pipeline run.
    • E. Pass every looked-up value with --context so the CLI treats it as a number.
Answers
  1. A. Literal account and Region make the stack environment-specific, which lookups require, and the template no longer depends on who runs the CLI. The CDK_DEFAULT_* variables also fix the error but vary with the caller's profile; a profile alone does not give an environment-agnostic stack an environment; clearing context does not help.
  2. B, C. A committed cache freezes lookup results for everyone, and disabling lookups in CI stops silent re-queries. Ignoring or clearing the cache causes exactly the drift described, and command-line context values are strings.

12. Testing with the assertions module

Concept

Exam facts

Developer angle

import { App } from 'aws-cdk-lib';
import { Capture, Match, Template } from 'aws-cdk-lib/assertions';
import { OrdersStack } from '../lib/orders-stack';

test('queue, table and consumer are wired correctly', () => {
  const app = new App();
  const template = Template.fromStack(new OrdersStack(app, 'TestStack'));

  template.resourceCountIs('AWS::SQS::Queue', 2);                    // queue + dead-letter queue
  template.hasResourceProperties('AWS::SQS::Queue', {
    VisibilityTimeout: 300,
    RedrivePolicy: Match.objectLike({ maxReceiveCount: 5 }),
  });
  template.hasResource('AWS::DynamoDB::Table', { DeletionPolicy: 'Retain' });

  const role = new Capture();
  template.hasResourceProperties('AWS::Lambda::Function', {
    Role: { 'Fn::GetAtt': [role, 'Arn'] },
    Environment: { Variables: { TABLE_NAME: Match.anyValue() } },
  });
  expect(role.asString()).toMatch(/^ConsumerServiceRole/);
});

test('template matches the approved snapshot', () => {
  const app = new App();
  const template = Template.fromStack(new OrdersStack(app, 'SnapshotStack'));
  expect(template.toJSON()).toMatchSnapshot();
});

Traps

Practice questions

  1. A developer must verify in a unit test that every Lambda function in a stack has TracingConfig with Mode set to Active, and the test must keep passing when other function properties change. Which assertion fits BEST?

    • A. expect(template.toJSON()).toMatchSnapshot()
    • B. template.resourceCountIs('AWS::Lambda::Function', 3)
    • C. template.hasResourceProperties('AWS::Lambda::Function', Match.objectEquals({ TracingConfig: { Mode: 'Active' } }))
    • D. template.allResourcesProperties('AWS::Lambda::Function', { TracingConfig: { Mode: 'Active' } })
  2. A team keeps a legacy CloudFormation JSON template next to a CDK app and wants Jest tests for both. Which TWO statements are correct? (Select TWO.)

    • A. Template.fromStack() synthesizes the CDK stack in memory, so the tests need no deployment.
    • B. Snapshot tests fail only when application logic changes, never after a CDK library upgrade.
    • C. hasResourceProperties() fails when the resource has any property not listed in the assertion.
    • D. Fine-grained assertions require the target environment to be bootstrapped first.
    • E. Template.fromString() can load the legacy JSON template for the same assertion methods.
Answers
  1. D. allResourcesProperties checks every function for the listed property and ignores the rest. A snapshot fails on any unrelated change, a count says nothing about tracing, and objectEquals demands that the function have no other properties.
  2. A, E. Tests synthesize in memory, and fromString (or fromJSON) wraps external templates. Snapshots also change with CDK upgrades, hasResourceProperties is a partial match, and nothing is deployed, so bootstrapping is irrelevant.

13. CDK vs SAM and local testing with the SAM CLI

Concept

Exam facts

AWS SAM AWS CDK
Authoring YAML/JSON template with the SAM transform TypeScript, JavaScript, Python, Java, C#/.NET or Go
Scope Serverless-first (functions, APIs, tables, state machines) plus any CloudFormation resource Every AWS service through L1/L2/L3 constructs
Output Transformed CloudFormation template Synthesized CloudFormation template + assets
Deploy sam deploy cdk deploy (after cdk bootstrap)
Local Lambda testing sam local invoke / start-api / start-lambda The same SAM CLI commands on the cdk.out template
Fast cloud iteration sam sync --watch cdk watch / --hotswap
Unit tests of infrastructure Template linting and validation assertions module (§12)
Best when Quick start on Lambda, YAML-first team, SAM-specific features Many services, reusable typed components, logic in code

Developer angle

cdk synth --no-staging
sam local invoke ResizeFunction -t ./cdk.out/ImageStack.template.json --event events/upload.json
sam local generate-event apigateway http-api-proxy > events/api.json
sam local start-api -t ./cdk.out/ImageStack.template.json      # http://127.0.0.1:3000 by default
sam build -t ./cdk.out/ImageStack.template.json                # only for container-image functions

The same function written both ways:

Transform: AWS::Serverless-2016-10-31
Resources:
  ResizeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.12
      Handler: app.handler
      CodeUri: functions/resize/
new lambda.Function(this, 'ResizeFunction', {
  runtime: lambda.Runtime.PYTHON_3_12,
  handler: 'app.handler',
  code: lambda.Code.fromAsset('functions/resize'),
});

Traps

Practice questions

  1. A developer wants to run a Lambda handler defined in a CDK app on a laptop with a sample event before deploying anything. Docker and the SAM CLI are installed. Which sequence works?

    • A. Run sam init in the CDK project, then sam local invoke without a template.
    • B. Run cdk synth, then sam local invoke <FunctionId> -t ./cdk.out/<Stack>.template.json --event event.json.
    • C. Run cdk deploy --hotswap, then call the function with aws lambda invoke.
    • D. Run cdk watch with --logs to stream the handler's output.
  2. A team tests the Lambda functions of a CDK app locally with the SAM CLI. Which TWO statements are correct? (Select TWO.)

    • A. sam local invoke runs the function inside the team's AWS account.
    • B. The SAM CLI evaluates the CDK TypeScript constructs directly, so synthesis is not needed.
    • C. The team must run cdk synth before the sam local commands and again after code changes.
    • D. sam build bundles the app's NodejsFunction handlers with esbuild.
    • E. sam build is needed only for functions packaged as container images.
Answers
  1. B. The SAM CLI needs the synthesized template in cdk.out and the function's construct ID; it then runs the handler in Docker with the given event. Without a template there is nothing CDK-defined to run, and the hotswap and watch options deploy to AWS instead of testing locally.
  2. C, E. The SAM CLI reads cdk.out, so synthesis must be current, and only image functions need sam build. Local invokes run in Docker on the laptop, the SAM CLI never executes construct code, and sam build skips NodejsFunction.

Quick decision rules

Fundamentals, constructs and stacks (§1–§3)

Permissions, customisation, assets and data (§4–§7)

Bootstrapping, CLI and iteration (§8–§10)

Environments, testing and SAM (§11–§13)

Changed vs older material

Learn the Current column; older practice material may still show the Course / older material value. Rows you confirm during /ingest are added to notes/current-facts.md.

Topic Course / older material Current (verified 2026-10-12) Source
§1 · Installing the CDK CLI npm install -g aws-cdk-lib installs the CLI and libraries npm install -g aws-cdk installs the CLI; aws-cdk-lib + constructs are project dependencies added by cdk init AWS docs
§1 · Supported languages JavaScript/TypeScript, Python, Java, .NET TypeScript, JavaScript, Python, Java, C#/.NET and Go (all stable) AWS docs
§1 · CLI and library versioning CLI and aws-cdk-lib share one version number CLI (aws-cdk-cli) released separately on a 2.1xxx line; a library release works with the CLI current at its release and any newer CLI AWS docs
§2 · L1 constructs and L2 features L1s can only be configured property by property Mixins (.with(), Mixins.of()) add L2-style features to L1 and L2 constructs; Facades (Grants classes) add permissions helpers AWS docs
§2 · Lifecycle helper name bucket.addLifeCycleRule() bucket.addLifecycleRule() AWS docs
§2 · ALB + Fargate L3 class name aws-ecs-patterns.ApplicationLoadBalancerFargateService ecs_patterns.ApplicationLoadBalancedFargateService ("Balanced"; the "Balancer" name does not exist) AWS docs
§3 · Cross-stack references Always an export + Fn::ImportValue; cross-Region needs crossRegionReferences: true (custom resources) Strong (default, export + Fn::ImportValue) or weak (Fn::GetStackOutput, no export lock, cross-account and cross-Region), set with @aws-cdk/core:defaultCrossStackReferences or CrossStackReferences.of() AWS docs
§3 · Renaming constructs A changed logical ID always means replacement unless you restore the old ID cdk refactor --unstable=refactor (preview) keeps resources when constructs are renamed or moved AWS docs
§4 · Granting access bucket.grantRead(fn), table.grantReadWriteData(fn) bucket.grants.read(fn), table.grants.readWriteData(fn), BucketGrants.fromBucket(l1); old methods still work but are discouraged (fn.grantInvoke() unchanged) AWS docs
§8 · Contents of CDKToolkit An S3 bucket and IAM roles S3 bucket, ECR repository, 5 IAM roles, SSM parameter /cdk-bootstrap/hnb659fds/version; no KMS key by default AWS docs
§8 · Error when an environment isn't bootstrapped Policy contains a statement with one or more invalid principal SSM parameter /cdk-bootstrap/hnb659fds/version not found. Has the environment been bootstrapped? Please run 'cdk bootstrap'; the invalid-principals message comes from CDK Pipelines deploying into an unbootstrapped account AWS docs
§9 · cdk diff Compares template text only (--no-change-set to skip change sets) --method auto (default) creates a read-only change set for accurate replacement info; --method template for a text diff; --change-set deprecated AWS docs
§10 · Hotswap behaviour and coverage --hotswap falls back to CloudFormation by itself; Lambda, Step Functions and ECS only Non-hotswappable changes are skipped unless --hotswap-fallback; coverage includes API Gateway, AppSync, CodeBuild, EventBridge, DynamoDB, SQS, CloudWatch, Bedrock and more AWS docs

Re-checked and unchanged: AWS CDK v1 entered maintenance on 2022-06-01 and reached end of support on 2023-06-01; v2 ships stable constructs in aws-cdk-lib, Construct in constructs v10, experimental modules as -alpha packages, and requires the modern bootstrap template (AWS docs).