AWS CDK
Defining infrastructure using a real programming language instead of YAML/JSON — CDK compiles down to CloudFormation, giving you loops, functions, and type checking for infrastructure.
Want a visual for this topic?
Generate a diagram tailored to AWS CDK — the AI picks whichever visual (architecture, flowchart, ER diagram, etc.) best fits this specific AWS concept.
Sign in to generate a visual →🎓 Learning objectives
- •Explain what CDK adds on top of raw CloudFormation
- •Explain what a CDK Construct is and the three levels of abstraction (L1, L2, L3)
- •Explain the relationship between CDK code and the CloudFormation it generates
- •Recognize when raw CloudFormation might still be preferable to CDK
What is it?
AWS CDK (Cloud Development Kit) lets you define AWS infrastructure using a real, general-purpose programming language (TypeScript, Python, Java, C#, and others) instead of writing YAML/JSON CloudFormation templates directly — CDK code compiles ('synthesizes') down to a standard CloudFormation template under the hood, which is then deployed exactly like any other CloudFormation stack.
Why it exists
Raw CloudFormation templates, while powerful, are declarative text with no real programming constructs — no loops, no functions, no real type checking, no ability to express 'create one of these for each item in this list' without verbose, repetitive template code or complex intrinsic functions. CDK exists to let infrastructure be defined using the same programming language features and tooling (loops, conditionals, functions, classes, IDE autocomplete, type checking) that application developers already use daily, while still ultimately producing standard, safe, reviewable CloudFormation underneath.
Problem it solves
It solves the verbosity/repetition problem (a for loop can create N similar resources instead of hand-writing N nearly-identical template blocks), the type-safety problem (CDK's language bindings provide compile-time checking that catches many configuration mistakes before deployment, unlike YAML which has no inherent type checking), the abstraction problem (CDK's higher-level Constructs bundle multiple related resources with sensible best-practice defaults into one reusable unit, rather than requiring every low-level resource to be defined individually every time), and the reusability problem (CDK constructs can be packaged, shared, and reused across teams/projects like any other code library).
Intuition
Writing raw CloudFormation is like writing a document entirely in a form-filling template language — precise, but repetitive and inflexible for anything beyond simple, static structures. CDK is like writing a program that generates that same document — you can use loops, variables, and functions to express 'do this for each item in this list' concisely, and the program still ultimately produces the same kind of precise, structured output at the end.
Analogy
The difference between writing a wedding invitation by hand for every single guest (raw CloudFormation, verbose and repetitive for anything beyond a small handful of similar resources) versus writing a mail-merge program that generates a personalized invitation for each name on a guest list programmatically (CDK) — the final printed invitations (the generated CloudFormation template) look essentially the same either way, but one approach scales far better and is much less error-prone as the guest list (your infrastructure's complexity) grows.
Technical explanation
CDK Constructs come in three levels: L1 ("CFN Resources") are auto-generated, 1:1 wrappers directly mirroring every CloudFormation resource type with no added abstraction or opinion — full control, but no less verbose than raw CloudFormation for that resource. L2 Constructs are hand-crafted by the CDK team, wrapping one or a few L1 resources with sensible defaults and a more convenient, higher-level API (e.g. an L2 S3 Bucket construct handles common configuration like encryption and public access blocking with sensible defaults, requiring far less code than the equivalent L1/raw CloudFormation). L3 Constructs (sometimes called 'patterns') bundle multiple resources into a complete, opinionated solution for a common use case (e.g. 'a Fargate service behind an Application Load Balancer,' wiring together many individual resources with production-ready defaults in just a few lines of code). Because CDK ultimately generates standard CloudFormation, all of CloudFormation's underlying behavior applies — Change Sets, automatic rollback, drift detection — CDK doesn't replace or bypass any of that, it just changes how the template itself is authored.
Architecture
A platform team builds a custom L3 CDK Construct encapsulating their company's standard pattern for a new microservice — an ECS Fargate service, an ALB target group, appropriate IAM roles, CloudWatch alarms, and standard tagging — all bundled into one reusable class. Individual application teams then create a new microservice by writing just a few lines of CDK code instantiating this shared construct with their service-specific parameters, automatically inheriting the platform team's security and operational best practices without needing to understand or replicate the full underlying CloudFormation complexity themselves.
Workflow
- Choose a supported language (TypeScript and Python are the most commonly used) matching your team's existing skills. 2) Use existing AWS-provided L2/L3 constructs wherever they cover your need, rather than dropping down to L1/raw resource definitions unnecessarily. 3) For patterns repeated across multiple projects or teams, build custom, shared Constructs encapsulating your organization's specific best practices. 4) Use
cdk diff(CDK's equivalent of reviewing a Change Set) before deploying, to see exactly what will change. 5) Deploy viacdk deploy, which synthesizes the CloudFormation template and applies it through the normal CloudFormation deployment mechanism.
Example
A team needing to create 12 nearly-identical S3 buckets (one per customer-facing region-specific data store) writes a simple for loop in their CDK TypeScript code iterating over a list of region names, instantiating an L2 Bucket construct once per iteration with region-specific naming — a few lines of code replacing what would otherwise be 12 largely duplicated blocks of raw CloudFormation YAML, with the added benefit of TypeScript's compile-time type checking catching a typo in a property name before ever attempting deployment.
Real-world usage
CDK has become a popular choice among AWS customers who prefer defining infrastructure in a general-purpose language over YAML/JSON, particularly among teams already comfortable with TypeScript or Python; AWS itself publishes and maintains a large library of L2/L3 constructs, and the broader CDK community publishes many more reusable, shareable constructs for common patterns.
Trade-offs
CDK's programming-language expressiveness is a major advantage for complex, repetitive, or highly parameterized infrastructure, but for genuinely simple, small, and rarely-changed infrastructure, raw CloudFormation YAML can be more directly transparent and requires no additional tooling/language runtime to author or understand. Using higher-level L2/L3 constructs trades some fine-grained control for significantly less code and built-in best-practice defaults — appropriate for the majority of common use cases, though genuinely unusual requirements sometimes still need to drop down to L1 constructs or raw CloudFormation for full control.
Visual explanation
Picture a CDK application written in TypeScript or Python, containing a class that defines a 'Construct' — perhaps a reusable pattern combining a Lambda function, an API Gateway route, and a DynamoDB table, all wired together with sensible defaults. Running cdk synth compiles this code down into a standard CloudFormation YAML/JSON template, and running cdk deploy synthesizes that template and deploys it via CloudFormation exactly as if you'd written and deployed it directly — CDK is fundamentally a code-generation and deployment-orchestration layer on top of CloudFormation, not a separate deployment mechanism.
Advantages
- —
Real programming language features (loops, conditionals, functions, classes) eliminate the repetition and verbosity of raw CloudFormation for anything beyond simple, static infrastructure
- —
Compile-time type checking catches many configuration mistakes before ever attempting deployment, unlike YAML's lack of inherent type safety
- —
L2/L3 constructs provide sensible, AWS-recommended defaults out of the box, reducing the chance of a common security or configuration mistake
- —
Custom constructs can be packaged and shared like any other code library, letting a platform team's best practices be easily reused across many application teams
Disadvantages
- —
Adds a real learning curve on top of understanding CloudFormation itself — CDK developers still benefit from understanding the underlying CloudFormation concepts (Change Sets, stack behavior, resource replacement) that CDK ultimately generates
- —
Debugging an issue sometimes requires understanding both the CDK code AND the generated CloudFormation template it produces, an extra layer of indirection compared to writing CloudFormation directly
- —
The power and flexibility of a full programming language can be used to create infrastructure code that's harder to reason about than more constrained, purely declarative YAML, if not used with discipline
- —
Higher-level L2/L3 constructs' sensible defaults, while usually good, may not always match a specific team's exact requirements, sometimes requiring deeper knowledge to override correctly
Common mistakes
- —
Treating CDK as if it were a fundamentally different deployment mechanism from CloudFormation, rather than understanding it as a code-generation layer that still produces and deploys standard CloudFormation with all the same underlying behavior
- —
Not reviewing
cdk diffoutput before deploying, missing the same kind of dangerous resource-replacement risk that reviewing a raw CloudFormation Change Set would have caught - —
Overusing the full power of a general-purpose programming language to build overly clever, hard-to-follow infrastructure code, when simpler, more explicit code would be easier for teammates to understand and maintain
- —
Reaching for L1 constructs by default out of unfamiliarity with the higher-level L2/L3 options, missing out on their sensible defaults and significantly more concise code
- —
Not understanding enough of the underlying CloudFormation concepts to correctly interpret and debug an issue when the CDK abstraction leaks through during a deployment problem
In the AWS Console
- 1
Terminal → npm install -g aws-cdk (or pip install aws-cdk-lib)
Install the CDK CLI and initialize a new project with `cdk init app --language typescript` (or your chosen language).
Choose the language your team already has the most existing expertise in — CDK's core concepts are identical across all supported languages.
- 2
Terminal (within your CDK project)
Run `cdk synth` to generate and inspect the CloudFormation template your CDK code produces, before deploying anything.
This is a genuinely useful way to understand exactly what CloudFormation your code will actually generate, especially while learning CDK.
- 3
Terminal (within your CDK project)
Run `cdk diff` to see exactly what would change in the deployed stack, then `cdk deploy` to actually apply it.
`cdk diff` is CDK's equivalent of reviewing a CloudFormation Change Set — review it with the same care before deploying to a production stack.
🎤 Interview questions
What does CDK actually generate and deploy under the hood? (Listen for: CDK code compiles/synthesizes into a standard CloudFormation template, which is then deployed through CloudFormation exactly as if written directly — CDK doesn't bypass CloudFormation's underlying mechanics.)
What's the difference between L1, L2, and L3 CDK constructs? (Listen for: L1 = auto-generated 1:1 wrapper of a CloudFormation resource, no added abstraction; L2 = hand-crafted, higher-level API with sensible defaults for one or a few resources; L3/patterns = complete, opinionated solutions bundling many resources for a common use case.)
Why might a team choose CDK over writing raw CloudFormation YAML directly? (Listen for: real programming language features — loops, functions, type checking — reduce repetition and catch mistakes earlier, especially valuable for complex or highly parameterized infrastructure.)
What CDK command is the equivalent of reviewing a CloudFormation Change Set, and why does it matter? (Listen for: cdk diff, showing exactly what will change before deploying — same importance as reviewing a raw Change Set, including catching potential resource replacements.)
When might raw CloudFormation still be preferable to CDK? (Listen for: genuinely simple, small, rarely-changing infrastructure where CDK's added tooling/language layer provides little benefit and more direct transparency of raw YAML might be preferred.)