AWS CDK and SAM are the higher-abstraction IaC choices on DOP-C02. Where CloudFormation is declarative YAML/JSON and forces you to spell out every resource, CDK lets you write infrastructure in TypeScript, Python, Java, C#, or Go and synthesizes CloudFormation, while SAM is a CloudFormation transform optimized for serverless. The exam tests when each beats plain CloudFormation, the L1/L2/L3 construct hierarchy, CDK Pipelines for self-mutating CD, SAM gradual deployments via CodeDeploy, and the policy templates that make serverless IAM tractable.
This guide assumes you know what a CloudFormation stack is and what a Lambda function is. The DOP-C02 focus: L1 vs L2 vs L3 construct trade-offs, CDK bootstrap and toolkit stacks, CDK Pipelines self-mutation, CDK aspects for org-wide policy injection, SAM transform vs raw CloudFormation, sam local for offline testing, sam deploy --guided, gradual deployment configurations (Canary10Percent5Minutes, Linear10PercentEvery1Minute, AllAtOnce), and policy templates like DynamoDBCrudPolicy.
Why CDK and SAM Matter on DOP-C02
DOP-C02 added CDK explicitly to scope when it superseded DOP-C01 in 2023. Community pass reports flag IaC selection scenarios as one of the top three exam discriminators: "the team has 50 microservices, each with API Gateway + Lambda + DynamoDB, and wants to deploy with a single command and built-in canary" - the answer is SAM. "The platform team wants to enforce that every S3 bucket created by application teams has versioning enabled and KMS-CMK encryption, with the rule expressed as code" - the answer is CDK aspects. "The team writes infrastructure in Python and wants type-checked IDE auto-complete for AWS resources" - the answer is CDK.
The exam also pits CDK against CloudFormation StackSets for multi-account deployment, CDK Pipelines against CodePipeline+CloudFormation actions, and SAM against Serverless Framework. Knowing the precise scoping of each tool is the difference between a 30-second answer and a 4-minute elimination.
- AWS CDK: an open-source IaC framework that lets you define cloud resources in a general-purpose language (TypeScript, Python, Java, C#, Go) and synthesizes CloudFormation templates.
- Construct: the basic CDK building block; a class representing one or more AWS resources.
- L1 construct (CFN resource): an auto-generated 1:1 mapping of a CloudFormation resource (e.g.,
CfnBucket). - L2 construct (curated): a hand-crafted construct with sensible defaults and helper methods (e.g.,
BucketwithgrantRead(role)). - L3 construct (pattern): a high-level pattern combining multiple resources (e.g.,
ApplicationLoadBalancedFargateService). cdk synth: synthesizes CloudFormation templates from CDK code into thecdk.outdirectory.cdk deploy: synthesizes and deploys via CloudFormation.cdk bootstrap: provisions the CDK toolkit stack (S3 bucket, ECR repo, IAM roles) in a target account-region pair, required once per target.- CDK Pipelines: an L3 construct that creates a self-mutating CodePipeline definition entirely in CDK code.
- CDK Aspect: a class implementing
IAspect.visit(node)that walks the construct tree and applies cross-cutting modifications (e.g., enforcing tags on all resources). - AWS SAM: a CloudFormation transform (
Transform: AWS::Serverless-2016-10-31) and CLI tool that simplifies serverless application templates. - SAM template: a CloudFormation template using SAM's serverless resource types (
AWS::Serverless::Function,AWS::Serverless::Api,AWS::Serverless::HttpApi,AWS::Serverless::StateMachine). - SAM gradual deployment: a SAM feature that wires CodeDeploy traffic shifting onto Lambda function aliases via
AutoPublishAlias+DeploymentPreference. - SAM policy template: a named, parameterized IAM policy snippet (e.g.,
DynamoDBCrudPolicy) that SAM injects into Lambda execution roles. - Reference: https://docs.aws.amazon.com/cdk/v2/guide/home.html
Plain-Language Explanation: CDK and SAM
These tools collapse hundreds of lines of CloudFormation into dozens of lines of code. The analogies illuminate where the abstraction comes from and where it leaks.
Analogy 1: The IKEA Furniture Hierarchy
Picture buying furniture. Plain CloudFormation is buying raw lumber, screws, and instructions - you specify every plank, every nut, every washer. Reliable but verbose. L1 CDK constructs are the same lumber and screws, but ordered through a programmatic catalogue with a checkout API - same atoms, slightly faster ordering. L2 CDK constructs are the flat-pack IKEA boxes - you specify "Billy bookshelf, white, 80 cm" and the box contains the right planks, screws, and instructions automatically. You can still customize ("add a glass door"), but you no longer count individual screws. L3 CDK constructs are the fully assembled, delivered, and installed product - you say "office workstation for two people" and the whole desk-chair-monitor-cable bundle arrives configured.
SAM is the dedicated home-office furniture catalogue - narrowly focused on serverless desks and chairs, with built-in features like "ergonomic adjustment" (gradual deployment) and "voice-activated assembly" (sam local) that the general furniture catalogue doesn't bother optimizing.
Analogy 2: The Software Programming Language Stack
CloudFormation YAML is assembly language: every register, every memory address, explicit. CDK L1 is C with macros - same assembly under the hood but with named constants. CDK L2 is C++ with the standard library - you write Bucket(grantRead: role) instead of crafting bucket policies by hand. CDK L3 is a framework like Spring or Rails - one line gives you a fully configured ALB-ECS-CloudWatch trio.
SAM is a domain-specific language (DSL) for one workload type (serverless), like SQL is to databases. It is shorter than CloudFormation for serverless apps because it knows the patterns, but it cannot express non-serverless workloads.
cdk synth is the compiler that turns your high-level code back into the assembly language CloudFormation engine actually executes. CDK aspects are the linter or static analyzer that walks every line of the synthesized program and enforces "no unencrypted S3" rules.
Analogy 3: The Restaurant Kitchen Equipment Tiers
A restaurant chooses kitchen equipment at three tiers. Tier 1 (L1) is buying every component separately - a heating element, a thermostat, a control board, an enclosure - and assembling them. Total control, lots of work. Tier 2 (L2) is buying a commercial oven - the manufacturer combined the components with sensible defaults (temperature ranges, safety cutoffs); you tweak settings but do not rewire the thermostat. Tier 3 (L3) is buying a complete pizza station - oven, dough mixer, prep table, cooling rack, all integrated and pre-configured.
SAM is the pizza-station-only specialty supplier - it does not sell sushi stations or grill stations, but the pizza station ships faster and cheaper than ordering one from the general supplier.
CDK Pipelines is the self-installing kitchen contractor: you describe "I want this pizza station upgraded every Friday from the latest manufacturer firmware", and the contractor sets up its own scheduling, ordering, and installation system - including the contractor itself, who upgrades themselves first when their own definition changes (self-mutation).
For CDK construct levels, the IKEA hierarchy maps cleanest. For "why does CDK output CloudFormation", the programming language stack captures the synth/compile step. For SAM's narrow scope versus CDK's breadth, the kitchen equipment tiers make the trade-off vivid. Reference: https://docs.aws.amazon.com/cdk/v2/guide/constructs.html
CDK Construct Hierarchy
The L1/L2/L3 split is the most-tested CDK concept. Each level has specific use cases and failure modes.
L1 Constructs (CFN Resources)
L1 names start with Cfn (e.g., CfnBucket, CfnFunction). They are auto-generated from the CloudFormation resource specification - one CDK class per CloudFormation resource type. Properties map 1:1 to CloudFormation properties.
L1 constructs are the right choice when:
- You need a brand-new resource type that doesn't yet have an L2.
- You need a CloudFormation property that L2 has not exposed.
- You are migrating from existing CloudFormation and want a structural translation.
L2 Constructs (Curated)
L2 constructs are hand-crafted by AWS or the community. They wrap L1 with:
- Sensible defaults (e.g.,
Bucketenables versioning if you callversioned: trueand adds bucket policies via helper methods). - Helper methods like
grantRead(grantee)that compose IAM policies correctly. - Type-safe enums and validation.
L2 is the default for production CDK code. The naming convention drops the Cfn prefix (Bucket, Function, Vpc).
L3 Constructs (Patterns)
L3 constructs combine multiple resources into an architecture pattern. Examples:
ApplicationLoadBalancedFargateService: ALB + Fargate service + task definition + target group + log group.LambdaRestApi: API Gateway REST API + Lambda integration + deployment.BackgroundQueue(community): SQS + DLQ + Lambda consumer.
L3 constructs trade flexibility for brevity. Exam scenarios that mention "the team must comply with a security baseline that requires custom KMS keys, specific subnet placement, and tagged-but-not-modified ALB attributes" usually rule out L3 because the pattern's defaults conflict with the baseline. The answer is typically L2 with explicit configuration. Reference: https://docs.aws.amazon.com/cdk/v2/guide/constructs.html
CDK Bootstrap and Synth Lifecycle
CDK does not write directly to AWS APIs - it synthesizes CloudFormation. The bootstrap and synth steps are testable concepts.
cdk bootstrap
Before deploying CDK to a new account-region pair, you run cdk bootstrap aws://ACCOUNT/REGION. This provisions the CDK toolkit stack: an S3 bucket for asset uploads (Lambda zips, Docker contexts), an ECR repo for container images, and IAM roles (cdk-hnb659fds-cfn-exec-role, cdk-hnb659fds-deploy-role, etc.) that CDK uses to deploy from CodePipeline or local CLI.
For multi-account deployment, you bootstrap each target account with --trust ACCOUNT_ID to allow the source pipeline account to assume the deploy roles cross-account.
A common scenario: you set up CDK Pipelines in the tooling account targeting a production account; deploys fail with "cannot assume role". The fix is to re-bootstrap the production account with --trust TOOLING_ACCOUNT_ID --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess. The trust relationship is set at bootstrap time, not at pipeline-creation time. Reference: https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html
cdk synth
cdk synth runs your CDK app, walks the construct tree, and writes CloudFormation templates plus assets to cdk.out/. The output is called a cloud assembly. CodeBuild stages in CDK Pipelines invoke cdk synth and pass the assembly to the next stage.
Importantly, cdk synth is deterministic given the same context inputs. CDK uses context (a JSON cache in cdk.context.json) to pin lookups like Vpc.fromLookup to specific values - if you don't commit cdk.context.json, your CI builds may resolve different VPCs and produce different templates.
CDK Pipelines: Self-Mutating CD
CDK Pipelines is an L3 construct that creates a CodePipeline whose definition is itself in CDK code. The standout feature is self-mutation: when you change the pipeline definition (add a stage, change a deploy action), the pipeline updates itself in its first stage before continuing the deployment.
The standard CDK Pipelines flow:
- Source: pulls from CodeCommit, GitHub, or S3.
- Synth: CodeBuild runs
cdk synthto produce the cloud assembly. - UpdatePipeline (self-mutation): deploys any pipeline definition changes.
- Assets: uploads Lambda zips, Docker images to bootstrap S3/ECR.
- DeployStages: one per
Stageyou defined, with optionalpreandpoststeps.
Cross-account CDK Pipelines deploys work because each target account is bootstrapped with --trust SOURCE_ACCOUNT, granting the source account permission to assume the deploy roles. There is no separate cross-account role configuration in the pipeline definition itself - the trust is in the bootstrap stack. Reference: https://docs.aws.amazon.com/cdk/v2/guide/cdk_pipeline.html
CDK Aspects: Cross-Cutting Policy Injection
Aspects implement IAspect with a visit(node: IConstruct) method. The CDK app calls Aspects.of(scope).add(new MyAspect()) to register an aspect on a scope; the aspect is invoked once per construct in that scope's subtree before synthesis.
Common DOP-C02 aspect patterns:
- Tag enforcement: walk the tree, ensure every taggable resource has
Environment,CostCenter,Ownertags. - S3 encryption baseline: walk the tree, find every
CfnBucket, fail synthesis ifbucketEncryptionis unset or set toAES256instead of KMS-CMK. - Custom resource configuration validation: enforce that all RDS instances have
BackupRetentionPeriod >= 7.
Aspects synthesize errors before any CloudFormation runs - they are the IaC equivalent of pre-commit lint rules.
SAM: Serverless-Optimized CloudFormation
SAM is a CloudFormation transform. The Transform: AWS::Serverless-2016-10-31 line at the top tells CloudFormation to expand SAM resources into vanilla CloudFormation before deploy.
SAM Resource Types
AWS::Serverless::Function: expands toAWS::Lambda::Function+ IAM role + log group + optionally event source mappings.AWS::Serverless::Api: expands toAWS::ApiGateway::RestApi+ Deployment + Stage + permissions for Lambda integrations.AWS::Serverless::HttpApi: similar but for API Gateway HTTP API.AWS::Serverless::StateMachine: expands toAWS::StepFunctions::StateMachine+ role.AWS::Serverless::SimpleTable: a minimal DynamoDB shortcut.AWS::Serverless::Application: nests another SAM template (used heavily by Serverless Application Repository).
SAM CLI Commands
sam init: scaffolds a template from a runtime + use case.sam build: builds Lambda artifacts (compiled, dependency-resolved).sam local invoke/sam local start-api: emulates Lambda and API Gateway locally using Docker.sam package/sam deploy: uploads assets to S3 and runs CloudFormation.sam deploy --guided: walks you through deployment parameters and saves them tosamconfig.toml.
SAM Gradual Deployments (CodeDeploy Integration)
The most-tested SAM feature. Add to a AWS::Serverless::Function:
AutoPublishAlias: live
DeploymentPreference:
Type: Canary10Percent5Minutes
Alarms:
- !Ref ErrorRateAlarm
Hooks:
PreTraffic: !Ref PreTrafficLambdaFn
PostTraffic: !Ref PostTrafficLambdaFn
SAM provisions:
- A Lambda alias
livethat points to the latest version. - A CodeDeploy application + deployment group.
- Traffic shifting per the configuration:
Canary10Percent5Minutes,Canary10Percent10Minutes,Canary10Percent15Minutes,Canary10Percent30Minutes,Linear10PercentEvery1Minute,Linear10PercentEvery2Minutes,Linear10PercentEvery3Minutes,Linear10PercentEvery10Minutes,AllAtOnce. - Alarm-based auto-rollback if any listed alarm fires during the deployment window.
- Pre/post-traffic Lambda hooks for custom validation.
Without AutoPublishAlias, SAM does not create the alias and CodeDeploy has nothing to traffic-shift on. The deployment configuration on DeploymentPreference is ignored without AutoPublishAlias. Always include both for gradual deploys. Reference: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html
SAM Policy Templates
SAM ships ~70 named policy templates that inject scoped IAM into Lambda execution roles. Examples:
DynamoDBCrudPolicy: full CRUD on a named DynamoDB table.DynamoDBReadPolicy: read-only.S3ReadPolicy: GetObject + ListBucket on a named bucket.SQSPollerPolicy: ReceiveMessage + DeleteMessage on a named queue.KMSDecryptPolicy: Decrypt on a named key.
Usage:
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref OrdersTable
This expands to a properly scoped policy without you typing IAM JSON.
CDK vs SAM vs CloudFormation Selection
The DOP-C02 selection matrix:
| Need | Pick |
|---|---|
| Pure serverless app, want shortest template | SAM |
| Mixed serverless + container + RDS, prefer typed code | CDK |
| Existing team owns CloudFormation, no new tooling | CloudFormation |
| Need self-mutating CD pipeline | CDK Pipelines |
| Need org-wide cross-cutting policy enforcement | CDK Aspects |
| Need built-in canary for Lambda | SAM |
| Multi-account distribution of identical baseline | CloudFormation StackSets |
| Mix of all the above | CDK (synthesizes CFN, integrates with StackSets via CfnInclude) |
SAM has no equivalent of CloudFormation StackSets. To deploy a SAM app to many accounts you wrap sam deploy in CodePipeline cross-account actions or CDK Pipelines stages. Candidates often pick "use SAM" for a multi-account Lambda baseline question; the correct answer is "use CDK Pipelines with multiple Stage targets" or "use CloudFormation StackSets after sam package". Reference: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html
CDK Testing
CDK ships an assertions module for unit testing synthesized templates:
const template = Template.fromStack(stack);
template.hasResourceProperties('AWS::S3::Bucket', {
BucketEncryption: { ... }
});
template.resourceCountIs('AWS::Lambda::Function', 3);
This catches drift between intent and synthesized output before CI runs cdk deploy. Combined with aspect-based linting and cdk synth --strict, it forms a reliable IaC test pyramid.
Common Pitfalls (常考陷阱)
- Picking SAM for non-serverless workloads: SAM cannot model EKS, ALB+Fargate, or RDS gracefully. Pick CDK or plain CloudFormation.
- Forgetting
cdk bootstrapper account-region: every new target needs bootstrap, with--trustif cross-account. Without it,cdk deployfails on missing toolkit stack. - Treating L3 as a free upgrade: L3 constructs hide configuration that the security baseline may require. For prescriptive baselines, L2 is safer.
- Confusing
cdk diffwith CloudFormation change sets:cdk diffshows the textual diff of synthesized templates, not a CloudFormation API change set. The actual change set is created duringcdk deploy. - Missing
AutoPublishAliaswith SAM gradual deployment: deployment preference is silently ignored. - Assuming CDK Pipelines auto-bootstraps target accounts: it does not - bootstrap is a one-time manual step per account.
- Using L1 constructs unnecessarily: L1 sacrifices the IAM helper methods and validation that make L2 safe; only drop to L1 for properties L2 has not exposed.
FAQ
Q1: When should I prefer CDK over CloudFormation?
CDK wins when you have logic-heavy infrastructure (loops, conditionals based on environment), when you want type-checked auto-complete, when you want to share infrastructure libraries across teams via npm/pip packages, or when you want self-mutating pipelines via CDK Pipelines. Plain CloudFormation wins when the team is non-developer (operations or security teams) or when you need to deploy to environments without Node.js/Python toolchains.
Q2: When should I prefer SAM over CDK for serverless?
SAM is faster for pure serverless apps - shorter templates, built-in sam local, built-in gradual deploy via DeploymentPreference. CDK is better for mixed workloads or when you need TypeScript-level abstraction. Many teams write Lambda apps in SAM and orchestrate them inside CDK Stages.
Q3: Can I import an existing CloudFormation stack into CDK?
Yes, via CfnInclude from aws-cdk-lib/cloudformation-include. It loads a YAML/JSON template into a CDK stack and lets you add new constructs alongside. Useful for incremental migration.
Q4: How does CDK handle stateful resources during stack deletion?
By default, L2 stateful constructs (DynamoDB tables, RDS instances, S3 buckets with content) set removalPolicy: RETAIN - the resource survives stack deletion. You can override with removalPolicy: DESTROY. The L1 constructs use the CloudFormation default (Delete), so dropping to L1 reintroduces accidental-deletion risk.
Q5: What is the relationship between CDK Pipelines and CodePipeline?
CDK Pipelines synthesizes a CodePipeline definition. At runtime, CodePipeline is what executes. CDK Pipelines adds the self-mutation source-stage and bootstrap-aware deploy actions. You can still inspect, monitor, and troubleshoot the pipeline via the CodePipeline console.
Q6: Can SAM use Conditions and Mappings like vanilla CloudFormation?
Yes. SAM templates are CloudFormation templates with the SAM transform layered on top. Conditions, Mappings, Outputs, Parameters, Metadata - all work normally. SAM only adds resource type expansions; it does not replace the CFN language.
Q7: How do I test CDK stacks without deploying?
Use the aws-cdk-lib/assertions module to write unit tests against synthesized templates. Run cdk synth in CI and assert the output. For integration tests, deploy to an isolated dev account and run black-box tests, then cdk destroy. The synth-time assertions catch most regressions cheaply.
Wrap-Up
CDK and SAM compress hundreds of CloudFormation lines into dozens of code lines. CDK gives you typed languages, L1/L2/L3 hierarchy, aspects for org-wide policy, and self-mutating Pipelines. SAM gives you the shortest path for serverless apps with built-in canary deploys and policy templates. Pick CDK for heterogeneous platforms, SAM for serverless-only, plain CloudFormation when the team is non-developer. Bootstrap each target account once with --trust for cross-account work, never skip AutoPublishAlias with SAM gradual deploy, and prefer L2 constructs unless you have a specific L3 pattern that fits or a baseline gap that demands L1.