CloudFormation and CDK
CloudFormation is AWS's native IaC with managed state and automatic rollback; the CDK lets you write it in TypeScript or Python. Review a diff that would destroy data, watch a failed update roll back, and compare with Terraform.
🎯 What you'll learn
- Templates, intrinsic functions and stacks
- Change sets, rollback, stack policies, drift, StackSets
- SAM for serverless
- CDK constructs (L1/L2/L3), testing, and CFN/CDK vs Terraform
📚 Before you start
1CloudFormation: AWS's Native IaC
AWS CloudFormation provisions AWS resources from templates (YAML/JSON) as stacks. Unlike Terraform (devops-cloud/terraform/), state is managed by AWS: the stack is the state, and CloudFormation handles ordering, rollback and drift detection.
AWSTemplateFormatVersion: "2010-09-09"
Description: Uploads bucket and queue
Parameters:
Env:
Type: String
AllowedValues: [dev, staging, prod]
Conditions:
IsProd: !Equals [!Ref Env, prod]
Resources:
UploadsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain # keep data if the stack is deleted
UpdateReplacePolicy: Retain
Properties:
BucketName: !Sub "petpal-${Env}-uploads-${AWS::AccountId}"
VersioningConfiguration: { Status: !If [IsProd, Enabled, Suspended] }
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
JobsQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 120
Outputs:
BucketArn:
Value: !GetAtt UploadsBucket.Arn
Export: { Name: !Sub "${AWS::StackName}-BucketArn" } # importable by other stacksTemplate sections: Parameters, Mappings, Conditions, Resources (the only required one), Outputs. Intrinsic functions: !Ref, !GetAtt, !Sub, !Join, !If, !ImportValue, !FindInMap. Dynamic references pull secrets at deploy time without putting them in templates: {{resolve:secretsmanager:prod/db:SecretString:password}}.
2Stack Lifecycle and Safety
- Change sets: preview exactly what an update will do (add/modify/replace/remove) — the equivalent of
terraform plan. Review them forReplacement: True. - Automatic rollback on failure: if any resource fails to create/update, the stack rolls back to the last good state (optionally triggered by CloudWatch alarms during deployment: rollback triggers).
- Stack policies forbid updates to critical resources; termination protection prevents stack deletion;
DeletionPolicy/UpdateReplacePolicy: Retain|Snapshotprotect data. - Drift detection compares resources with the template.
- Nested stacks (reuse templates within a stack) and StackSets (deploy the same stack to many accounts/regions — guardrails, baseline roles;
23-multi_account_and_organizations/). - Import existing resources into a stack; custom resources (Lambda-backed) for things CloudFormation doesn't support; the CloudFormation Registry for third-party resource types and Hooks for pre-deployment policy checks.
3AWS SAM
The Serverless Application Model is a CloudFormation transform with shorthand for serverless resources (AWS::Serverless::Function, ::Api, ::HttpApi, ::StateMachine) plus a CLI for local invocation (sam local invoke, sam local start-api), building and deploying Lambda apps (12-lambda_and_serverless/).
4AWS CDK: Infrastructure in a Programming Language
The Cloud Development Kit lets you define infrastructure in TypeScript, Python, Java, C# or Go; cdk synth synthesizes a CloudFormation template, and cdk deploy deploys it as stacks.
import { App, Stack, StackProps, Duration, RemovalPolicy } from "aws-cdk-lib";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as sqs from "aws-cdk-lib/aws-sqs";
import * as lambda from "aws-cdk-lib/aws-lambda-nodejs";
import { S3EventSource } from "aws-cdk-lib/aws-lambda-event-sources";
import { Construct } from "constructs";
export class UploadsStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, "Uploads", {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
versioned: true,
removalPolicy: RemovalPolicy.RETAIN,
});
const dlq = new sqs.Queue(this, "ThumbDlq", { retentionPeriod: Duration.days(14) });
const fn = new lambda.NodejsFunction(this, "Thumbnailer", {
entry: "src/thumbnailer.ts", // bundled with esbuild automatically
memorySize: 1024,
timeout: Duration.seconds(30),
deadLetterQueue: dlq,
});
bucket.grantReadWrite(fn); // generates a least-privilege IAM policy
fn.addEventSource(new S3EventSource(bucket, { events: [s3.EventType.OBJECT_CREATED], filters: [{ prefix: "incoming/" }] }));
}
}
const app = new App();
new UploadsStack(app, "petpal-prod-uploads", { env: { account: "111111111111", region: "eu-west-1" } });Constructs come in levels: L1 (CfnBucket, 1:1 with CloudFormation), L2 (Bucket — sensible defaults, helper methods like grantRead), L3 patterns (ApplicationLoadBalancedFargateService builds a whole service). Teams publish their own constructs as libraries ("a standard API with alarms and dashboards") — reuse via package managers, with unit tests:
const template = Template.fromStack(new UploadsStack(new App(), "test"));
template.hasResourceProperties("AWS::S3::Bucket", { VersioningConfiguration: { Status: "Enabled" } });
template.resourceCountIs("AWS::Lambda::Function", 1);Aspects apply checks or modifications across all constructs (e.g. cdk-nag security rules, enforcing tags). cdk diff shows changes; cdk bootstrap prepares an account/region (asset bucket, ECR repo, deploy roles).
5CloudFormation/CDK vs Terraform
| CloudFormation / CDK | Terraform / OpenTofu | |
|---|---|---|
| scope | AWS (plus registry types) | multi-cloud + SaaS providers |
| state | managed by AWS (stacks) | you manage (backends, locking) |
| new AWS features | usually day-one support | provider updates (usually fast) |
| rollback | automatic stack rollback | no automatic rollback (fix forward) |
| language | YAML/JSON or TS/Python/… (CDK) | HCL (or CDKTF) |
| limits | 500 resources per stack, slower deploys | plan/apply speed depends on API calls |
Under the HoodWhat cdk deploy Does▾
cdk synth runs your program: constructing the construct tree, then each construct emits CloudFormation resources into a cloud assembly (cdk.out/): one template per stack plus assets (Lambda bundles, Docker images) with content hashes. cdk deploy uploads assets to the bootstrap bucket/ECR repository, then calls CloudFormation CreateChangeSet/ExecuteChangeSet using the bootstrap deploy roles. CloudFormation builds a dependency graph from Ref/GetAtt/DependsOn, creates resources in parallel where possible, and on any failure reverses completed operations in reverse order. Logical IDs (derived from construct paths) identify resources across updates — renaming a construct changes its logical ID, which CloudFormation sees as "delete old, create new" (data loss for stateful resources) unless you override the logical ID or use RemovalPolicy.RETAIN.
⚠️ Common Pitfalls
- Deploying without reviewing change sets /
cdk diff— replacements of stateful resources. - Renaming constructs holding data (logical ID change).
- Missing
DeletionPolicy/RemovalPolicy: RETAINon buckets and databases. - Cross-stack exports that can't be changed while imported (deadlocked stacks).
- Hard-coded account/region in reusable constructs.
- Secrets in parameters or context instead of dynamic references.
📋 Quick Reference
template: Parameters · Conditions · Resources · Outputs (Export) · intrinsics (!Ref !GetAtt !Sub !If)
safety: change sets · rollback (+ alarm triggers) · DeletionPolicy Retain/Snapshot · termination protection · drift
scale: nested stacks · StackSets · SAM for serverless
CDK: L1/L2/L3 constructs · grant* helpers · assertions tests · aspects (cdk-nag) · synth → diff → deploy · bootstrap🧠 Key takeaways
- CloudFormation stacks are the state; updates go through change sets with automatic rollback.
- Protect stateful resources: DeletionPolicy/RemovalPolicy Retain, deletion protection, stack policies; watch logical-ID changes.
- CDK synthesizes CloudFormation from code: L2/L3 constructs,
grant*helpers, assertion tests,cdk diffbefore deploy. - Choose CFN/CDK for AWS-only with managed state; Terraform for multi-provider ecosystems.
🎤 Interview Q&A
Q1What is CloudFormation?core▾
AWS's native infrastructure as code: declarative YAML/JSON templates describing resources; stacks are created, updated and deleted as units, with change sets to preview changes and automatic rollback on failure.
- What's drift detection? Finding resources changed outside CloudFormation.
Q2What does CDK add?core▾
Define infrastructure in TypeScript, Python, C# or Java with constructs (reusable components with sensible defaults, e.g. L2/L3 constructs), synthesized into CloudFormation templates. You get loops, types, testing and abstraction.
- Downside? Abstractions can hide what's created; review the synthesized template.
Q3CloudFormation/CDK vs Terraform?senior▾
CloudFormation/CDK are AWS-native with managed state and same-day support for many features. Terraform is multi-cloud with a huge provider ecosystem and explicit state you manage. Choose by cloud strategy and team skills.
- Who manages CloudFormation state? AWS, inside the stack.
Q4How do you structure stacks?core▾
Split by lifecycle and ownership (network, data, application), pass values via exports/parameters or SSM, keep stacks small enough to update quickly, and protect stateful resources with deletion policies and termination protection.
- Why DeletionPolicy: Retain? To avoid deleting databases or buckets when a stack is removed.
Q5How do you test CDK code?core▾
Snapshot tests of synthesized templates, fine-grained assertions (Template.fromStack(...).hasResourceProperties), cdk-nag for security/compliance rules, and integration tests deploying to a sandbox account.
- What does cdk diff show? Changes between deployed and synthesized stacks.
🛠️ Practice exercises
Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.
✅ Check yourself
Answer the questions — feedback is instant.