skip to content

AWS CDK

AWS infrastructure written in code that synthesizes down to CloudFormation, with constructs layering sensible defaults over raw resources. Interviewers ask about the L1/L2/L3 construct levels and about what the synth step actually produces.

on this pageshow

questions

16

In the AWS CDK, what are an App, a Stack and a Construct, and which of the three corresponds to something AWS actually creates when you deploy?

level: juniorimportance: must knowfreq 78%

answer

  1. root, unit, building block
  2. only one layer is deployable
  3. one instance equals one CloudFormation stack
  4. the App is never deployed
  5. synth writes one template per stack

basics

~20 s

Constructs are the composable building blocks of the tree; a Stack is the deployment unit that synthesizes into exactly one CloudFormation stack; the App is the root object holding every stack. Only stacks become real AWS deployments.

solid answer

~50 s

An AWS CDK program builds a tree of **constructs** — objects constructed with `(scope, id, props)`, where `scope` is the parent. Two nodes in that tree have special meaning. `App` is the root you create first and never deploy directly. `Stack` is the boundary where the description becomes deployable: one `Stack` instance synthesizes into exactly one CloudFormation template and deploys as exactly one CloudFormation stack, in one account and one region. Everything else is a construct that contributes resources to whichever stack it is scoped under. `cdk synth` walks the tree and writes a cloud assembly — one template per stack, in `cdk.out`. After that, AWS only ever sees CloudFormation; the App, the classes and the construct tree are gone. That is why stack boundaries are an architectural decision: the stack is the unit of deploy, of rollback and of resource limits.

code

typescript · 16 lines
typescript
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';

class StorageStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);
    new s3.Bucket(this, 'Data', { versioned: true });
  }
}

const app = new cdk.App();
const env = { account: '111122223333', region: 'eu-west-1' };
new StorageStack(app, 'Storage', { env });
new StorageStack(app, 'StorageBackup', { env: { ...env, region: 'eu-central-1' } });
app.synth();

go deeper

for a junior

Be able to name the three layers in order and say plainly that the Stack is the one that becomes a CloudFormation stack in AWS. Know that you run cdk deploy against a stack name, not against the app.

for a middle

Explain what synthesis produces — a cloud assembly with one template per stack — and that AWS never sees your constructs. Be ready to say where env attaches and why one app can span accounts and regions.

for a senior

Justify a stack split on lifecycle, blast radius and resource limits rather than on code tidiness, and explain why frequently deployed services should not share a stack with long-lived networking.

for a principal

Own the estate shape: how many stacks per service, which boundaries teams can deploy independently, and the tradeoff between few large stacks with simple wiring and many small stacks with more inter-stack coupling.

## Three layers, one tree An AWS CDK program is not a script that calls AWS APIs. It is a program that builds an in-memory tree of objects and then prints CloudFormation. The nodes of that tree are **constructs**: objects whose constructor signature is `(scope, id, props)`, where `scope` is the parent construct. A construct by itself creates nothing in AWS — it is a piece of a description. Two constructs carry special meaning. **`Stack`** is the boundary at which description turns into a deployable artifact. One `Stack` instance synthesizes into exactly one CloudFormation template and is deployed as exactly one CloudFormation stack, into one AWS account and one region. **`App`** is the root. You create it first, scope your stacks to it, and never deploy it — there is no AWS object called an App. ```typescript const app = new cdk.App(); const env = { account: '111122223333', region: 'eu-west-1' }; new NetworkStack(app, 'Network', { env }); new ServiceStack(app, 'Service', { env }); app.synth(); ``` ## What synthesis produces `cdk synth` walks the tree, lets each construct contribute CloudFormation resources to the stack it lives under, and writes a **cloud assembly**: a directory (`cdk.out` by default) containing one template file per stack plus metadata the CLI uses. From that moment on, AWS sees only CloudFormation. Your classes, your loops, your `if` statements and the construct tree itself do not exist at deploy time — they ran already, during synthesis, on your machine or your build runner. This is the reason the question has exactly one answer: the Stack is the thing AWS creates. `cdk deploy Network` calls CloudFormation with the template that stack produced. ## Why the Stack boundary is the one that matters Because a stack is the CloudFormation unit, it is simultaneously the unit of several things you care about: - **Deployment.** `cdk deploy Service` deploys one stack; `cdk deploy --all` deploys every stack in the app. `cdk list` shows you the names. - **Rollback and blast radius.** A failed update rolls that stack back. Resources in a different stack are untouched. - **Limits.** CloudFormation caps the number of resources in a single stack, so a large estate has to be split. - **Lifecycle.** A VPC that changes twice a year and a service that deploys twice a day do not belong in the same stack, because every deploy of the fast-moving thing puts the slow-moving thing into an update. So splitting an app into stacks is an architectural decision, not code tidying. The rule of thumb is: things that must deploy together and roll back together go in one stack; things with independent lifecycles go in separate stacks. Constructs are the opposite — they are free to reorganise. Wrapping several resources in your own construct class does not create a new deployment unit; those resources still belong to whichever stack the construct is scoped under. Composition is a code-structure tool; stacks are a deployment tool. ## Where the environment attaches `env` — the account and region a stack targets — is a property of a **Stack**, not of the App and not of an individual construct. Two stacks in one app can target two different accounts, or two regions, because each one becomes its own CloudFormation stack in its own environment. This is also why an App is not "an account": one app routinely spans several. Between App and Stack there is an optional layer, `Stage`: a group of stacks you can instantiate more than once, typically once per environment. `App` is itself a subclass of `Stage`, which is why stages nest naturally under it. ## What candidates get wrong The two common errors are collapsing the layers in either direction. One is treating the App as the deployable thing ("the app is my CloudFormation stack"), which makes stack boundaries look like folders and leads to one enormous stack. The other is imagining that each construct maps to a stack, which makes the resource limit and rollback behaviour incomprehensible. A third, subtler error is believing the CDK talks to AWS while your code runs. It does not — apart from synth-time context lookups, the CDK's entire job is to emit templates, and every deployment decision is CloudFormation's.

  • If you instantiate a construct but it is not scoped under any stack, what gets deployed?
    Nothing. Only constructs in a stack's scope contribute resources to a template. Most higher-level constructs resolve their stack internally via `Stack.of(this)` and fail at synthesis time if there is no stack ancestor, so the mistake usually shows up as a synth error rather than a silent omission.
  • What is a Stage, and where does it sit relative to App and Stack?
    A `Stage` is a composable group of stacks that can be instantiated more than once — typically once per environment — and it synthesizes into its own cloud assembly. It sits between the App and the stacks. `App` is itself a subclass of `Stage`, which is why a stage nests under an app naturally.
  • How does the CDK CLI decide which stacks to deploy?
    `cdk list` enumerates the stacks in the synthesized assembly by their construct path. You then name one (`cdk deploy Service`), pass a glob, or use `--all`. The CLI orders deployments by the dependencies between stacks, so a producer stack goes before a stack that references it.

The App is the build; the constructs are source files and functions; the Stack is the artifact that actually gets shipped and can be rolled back on its own.

saying these in an interview costs you the question

  • Says one App equals one CloudFormation stack
  • Thinks every construct becomes its own stack
  • Assumes all stacks in an app must deploy together
  • Says a Stack maps to an AWS account, not a CloudFormation stack
  • Believes the CDK calls AWS resource APIs directly at runtime

context

open as a page

In the AWS CDK, what are L1, L2 and L3 constructs, how do you recognise an L1 class in code, and what do you give up by dropping to one?

level: juniorimportance: must knowfreq 78%

basics

~20 s

AWS CDK constructs come in three levels: L1 Cfn classes mirror CloudFormation resources one-to-one with no defaults, L2 constructs add sensible defaults and helper methods such as the grant helpers, and L3 patterns assemble many resources into one opinionated component.

open as a page

In the AWS CDK, what does the `cdk synth` command actually do, and what does it leave behind in the cdk.out directory?

level: juniorimportance: must knowfreq 70%

basics

~10 s

cdk synth runs your CDK program and writes a cloud assembly into cdk.out: one CloudFormation template per stack, asset manifests, and staged asset files. It deploys nothing and changes no AWS resources.

open as a page

You define an AWS CDK stack without passing the `env` property. Where does it deploy, what do `stack.account` and `stack.region` resolve to, and what stops working?

level: middleimportance: must knowfreq 68%

basics

~20 s

An environment-agnostic stack deploys wherever the CDK CLI's credentials point. Its account and region become unresolved tokens backed by CloudFormation pseudo-parameters, so synth-time lookups such as Vpc.fromLookup fail and the stack assumes only two availability zones.

open as a page

In an AWS CDK app, what does calling bucket.grantRead(myFunction) actually put into the synthesized CloudFormation template, and why is that preferred over hand-writing the policy document?

level: middleimportance: must knowfreq 62%

basics

~20 s

Calling grantRead adds a policy statement to the Lambda function's execution role in the synthesized template, scoped to that bucket's ARN and its objects, and it also grants decrypt on the bucket's encryption key when the CDK app knows about one.

open as a page

A colleague runs `cdk deploy` for the first time in a fresh AWS account and region, and it fails saying the environment has not been bootstrapped. What does `cdk bootstrap` create, and why does the AWS CDK need it?

level: middleimportance: must knowfreq 72%

basics

~20 s

cdk bootstrap deploys a CloudFormation stack named CDKToolkit into each target account and region. It provisions the asset staging S3 bucket, an ECR repository for image assets, and the IAM roles the CDK CLI assumes to publish assets and run CloudFormation.

open as a page

The AWS CDK writes a file called `cdk.context.json` in your project. What goes into it, and should it be committed to version control?

level: middleimportance: should knowfreq 45%

basics

~20 s

It caches the results of synthesis-time lookups against a real AWS account — VPC ids, availability zones, hosted zones, AMI ids, SSM values. Commit it, so synthesis is reproducible without AWS access; the cost is staleness until you reset an entry.

open as a page

A CDK stack defines a Lambda function with `lambda.Code.fromAsset('./handler')` and a container image built from a local Dockerfile. Walk through what `cdk deploy` does with those assets before CloudFormation ever updates the stack.

level: middleimportance: should knowfreq 48%

basics

~20 s

Synthesis stages each asset into cdk.out and records a content hash in the stack's asset manifest. cdk deploy then publishes the zip to the bootstrap staging S3 bucket and the image to the bootstrap ECR repository, and only then updates the stack.

open as a page

In the AWS CDK, what does `cdk diff` compare, and what will it fail to warn you about before a deploy?

level: middleimportance: should knowfreq 55%

basics

~20 s

cdk diff synthesizes locally and compares that template against the template CloudFormation currently stores for the deployed stack, printing resource, IAM and security-group changes. It compares templates, not live resources, so console-made drift is invisible to it.

open as a page

In one AWS CDK app, stack B uses `bucket.bucketArn` from a bucket defined in stack A. What do the two synthesized CloudFormation templates contain, and why can removing that reference later break your deployment?

level: seniorimportance: should knowfreq 48%

basics

~20 s

The CDK adds an Output with an Export to stack A and an Fn::ImportValue to stack B. CloudFormation refuses to delete an export that is still imported, so dropping the reference fails unless the consumer is updated first or the export is pinned for one deploy.

open as a page

An AWS CDK pull request renames a construct id from 'Table' to 'MainTable', and cdk diff now shows the live DynamoDB table being destroyed and a new one created. Explain how the CDK derives logical IDs from the construct tree, and how you would make that rename safe.

level: seniorimportance: should knowfreq 52%

basics

~20 s

The CDK derives each resource's CloudFormation logical ID from the construct's path in the tree — the chain of ids from the stack down — plus a hash of that path. Renaming a construct id changes the path, so CloudFormation sees a different resource and replaces it.

open as a page

An AWS CDK L2 construct does not expose a CloudFormation property you need to set. How do you set it anyway, and what do you give up by doing so?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Reach the underlying L1 resource through the construct's node.defaultChild, cast it to its Cfn type, and call addPropertyOverride or addOverride with the raw CloudFormation property path. The override is applied at synth time and the CDK does not type-check or validate it.

open as a page

Your platform team wants every service team's AWS CDK app to instantiate one shared internal construct instead of copy-pasting the same dozen resources. How would you design and ship that construct, and what does making it a shared library cost you?

level: principalimportance: should knowfreq 32%

basics

~20 s

Ship it as a versioned library exporting a Construct subclass that takes scope, id and a narrow props interface, and exposes the children it creates. The cost is that its internal construct tree becomes a public contract: renaming a child inside it changes consumers' logical IDs and replaces their resources.

open as a page

Your AWS CDK stack uses the L3 pattern ApplicationLoadBalancedFargateService, and you now need to change a setting its props do not expose, such as the target group's health check path. How do you get at the resource the pattern created?

level: middleimportance: nice to knowfreq 40%

basics

~20 s

An L3 pattern is an ordinary construct that instantiated L2 children and exposes them as public properties, so you reach the child — the target group, service or load balancer — and call its normal L2 methods on it rather than looking for a prop on the pattern.

open as a page

In the AWS CDK, when is a `NestedStack` the right choice instead of another top-level `Stack`, and what do you give up by nesting?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

A NestedStack becomes a single AWS::CloudFormation::Stack resource inside its parent, which buys room under the per-stack resource limit and parameter-based wiring instead of exports. You give up independent deployment: every change goes through a parent update.

open as a page

Your team deploys a CDK app to dev and prod accounts from a CI job that runs `cdk deploy`. What would moving to a self-mutating CDK Pipeline (the `pipelines.CodePipeline` construct) change, and when would you keep the plain CI job?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

A CDK Pipeline defines the delivery pipeline in CDK itself and updates itself: each run synthesizes, redeploys the pipeline stack from that assembly, then runs the application stages. You gain pipeline-as-code across accounts; you take on AWS-native lock-in, a privileged self-modifying pipeline, and slow feedback.

open as a page