AWS CDK Architecture

The CDK is essentially a code generator for CloudFormation. You write infrastructure in a real programming language (C#, TypeScript, Python, Java, Go), and CDK synthesizes it into CloudFormation templates that AWS then deploys. You’re not replacing CloudFormation — you’re getting a much nicer way to author it.

The Core Hierarchy

There are three layers of abstraction you’ll encounter, and they nest inside each other:

App → Stack → Construct

An App is the root of your CDK program. In your C# project, this is the new App() call in Program.cs. It’s the container for everything CDK will synthesize.

A Stack is a deployable unit and maps 1:1 to a CloudFormation stack in AWS. When you run cdk deploy, each Stack becomes a separate CloudFormation deployment. Stacks are also the boundary for things like region and account — a single Stack deploys to one region in one account. Most real projects have multiple stacks (e.g., a NetworkStack, a DatabaseStack, an AppStack) so you can deploy them independently.

Constructs are the building blocks inside stacks. They’re classes that represent one or more AWS resources. A Stack itself is actually a Construct — the whole system is a tree of constructs with the App at the root. This tree structure is central to how CDK works; constructs know their parent and can walk the tree.

The Three Levels of Constructs

This trips up almost everyone at first. Constructs come in three tiers:

L1 (Cfn-prefixed) — These are auto-generated 1:1 mappings of CloudFormation resources. CfnBucket, CfnInstance, etc. They take every property CloudFormation does, nothing more. Ugly but complete — if a new AWS feature exists in CloudFormation, it’s available as L1 immediately.

L2 — Hand-written, opinionated wrappers around L1s. Bucket (instead of CfnBucket) gives you sensible defaults, helper methods like grantRead(someRole), and type-safe properties. This is what you’ll use 90% of the time.

L3 (Patterns) — Higher-level compositions that wire multiple resources together for common use cases. ApplicationLoadBalancedFargateService creates a Fargate service, ALB, target groups, security groups, and the networking glue, all from a handful of properties.

How It Relates to CloudFormation

When you run cdk synth, CDK walks your construct tree and produces a CloudFormation template (JSON/YAML) per stack. That template goes in cdk.out/. When you run cdk deploy, the CLI uploads that template and calls CloudFormation’s CreateStack/UpdateStack API.

So at runtime in AWS, there is no CDK — just CloudFormation stacks. CDK is purely a build-time tool. This has a few practical implications:

  • Drift detection, rollback behavior, change sets, and stack events all work the same as regular CloudFormation.
  • Anything CloudFormation can’t do, CDK can’t do either (with rare escape hatches via custom resources).
  • You can import existing CloudFormation stacks and vice versa.

A Few Concepts Worth Knowing Early

Escape hatches — If an L2 doesn’t expose a property you need, you can drop down to the underlying L1 with bucket.Node.DefaultChild as CfnBucket and set raw CloudFormation properties. Useful when you hit the edges.

Tokens — When you reference something like bucket.BucketName at synth time, the actual name isn’t known yet (CloudFormation assigns it at deploy time). CDK uses placeholder strings called tokens that get resolved into CloudFormation Ref/GetAtt intrinsics in the synthesized template. This is why you can pass bucket.BucketArn into another construct even though the ARN doesn’t “exist” yet.

Bootstrapping — Before your first deploy to an account/region, you run cdk bootstrap. This creates an S3 bucket, ECR repo, and IAM roles CDK needs to upload assets and run deploys. One-time setup per account/region.

Context — Values like available AZs or existing VPC lookups get cached in cdk.context.json so synthesis is deterministic. Commit this file.