Using Claude Code with AWS CloudFormation and CDK: A Practical IaC Review Guide
Use Claude Code to review AWS CDK and CloudFormation safely: IAM, diffs, drift, rollback, and guardrails.
Clicking through the AWS Console feels fast until you need to explain why production differs from staging. CloudFormation and AWS CDK solve that by putting infrastructure into code, but code can still be risky: a broad IAM policy, a replacement of a stateful resource, or an unnoticed NAT Gateway can turn a clean deployment into a security or cost problem.
Claude Code is useful here, but I do not treat it as an autonomous deployment operator. I use it as a drafting and review partner: generate the first CloudFormation or CDK shape, explain cdk diff, flag privilege expansion, and turn stack events into a rollback checklist. The human still owns approval.
For beginners: CloudFormation is the AWS-native service that creates and updates resources from templates. CDK is a development kit that lets you write TypeScript, Python, and other languages, then synthesizes CloudFormation templates. A stack is the deployable unit. Drift means the real AWS resource no longer matches the template.
This article follows the official AWS docs current on June 3, 2026:
- AWS CDK Developer Guide
- AWS CDK CLI deploy command
- AWS CDK CLI diff command
- CloudFormation change sets
- CloudFormation drift detection
- CloudFormation stack failure options
- IAM security best practices
- AWS CDK security best practices
Set the Boundary First
The safest workflow is to ask Claude Code to review, not just generate.
| Task | Claude Code helps with | Human must approve |
|---|---|---|
| Requirements | Resource list, environments, dependencies | Whether the resource is needed |
| Template draft | CloudFormation/CDK first pass | Naming, deletion policy, cost |
| IAM | Candidate actions and resources | Least privilege and conditions |
| Diff review | Plain-language summary of cdk diff or a change set | Deletes, replacements, privilege expansion |
| Failure handling | Event analysis and likely fixes | Rollback plan and production action |
flowchart LR
A["Write requirements"] --> B["Draft IaC with Claude Code"]
B --> C["Human checks cost and naming"]
C --> D["Run cdk diff or change set"]
D --> E["Ask Claude Code for risk review"]
E --> F["Approve and deploy"]
F --> G["Record drift and rollback checks"]
Use Case 1: Convert a Small Existing AWS Setup to CloudFormation
Start with a low-risk slice instead of importing a whole production estate. This copy-pasteable CloudFormation template creates a private S3 bucket and a Lambda-assumable IAM role that can read only that bucket. It uses DeletionPolicy: Retain so accidental stack deletion does not remove the bucket.
mkdir cfn-iac-review-demo
cd cfn-iac-review-demo
# Save the next YAML as cfn-safe-iac-demo.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: "ClaudeCodeLab safe IaC review demo: private S3 bucket and least-privilege Lambda role"
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- prod
ProjectName:
Type: String
Default: cclab-iac-demo
AllowedPattern: "^[a-z0-9][a-z0-9-]{2,24}$"
Resources:
DemoBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketName: !Sub "${ProjectName}-${Environment}-${AWS::AccountId}-${AWS::Region}"
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
Tags:
- Key: Project
Value: !Ref ProjectName
- Key: Environment
Value: !Ref Environment
DemoReaderRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${ProjectName}-${Environment}-reader-${AWS::Region}"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ReadOnlyDemoBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ListOnlyThisBucket
Effect: Allow
Action:
- s3:ListBucket
Resource: !GetAtt DemoBucket.Arn
- Sid: ReadObjectsOnly
Effect: Allow
Action:
- s3:GetObject
Resource: !Sub "${DemoBucket.Arn}/*"
Outputs:
BucketName:
Value: !Ref DemoBucket
ReaderRoleArn:
Value: !GetAtt DemoReaderRole.Arn
Ask Claude Code for a strict review before creating anything:
claude -p "
Review cfn-safe-iac-demo.yaml as a CloudFormation template.
Return a table covering only:
1. accidental public access
2. IAM Resource scope
3. delete or replacement risk
4. possible monthly cost
5. what to inspect in a production change set
"
Then create and inspect a change set:
aws cloudformation create-change-set \
--stack-name cclab-iac-demo-dev \
--change-set-name review-20260603-001 \
--template-body file://cfn-safe-iac-demo.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=Environment,ParameterValue=dev ParameterKey=ProjectName,ParameterValue=cclab-iac-demo
aws cloudformation describe-change-set \
--stack-name cclab-iac-demo-dev \
--change-set-name review-20260603-001
Look for Add, Modify, Remove, and especially Replacement. Replacement is harmless for a demo role, but dangerous for S3, RDS, DynamoDB, keys, and networking resources.
Use Case 2: Build a Minimal CDK TypeScript Stack
For new projects, CDK TypeScript is easier to maintain because Claude Code can work with types and file structure. This example creates DynamoDB, S3, and Lambda without an API Gateway, so it stays small and cheap for review practice.
mkdir cdk-iac-review-demo
cd cdk-iac-review-demo
npm init -y
npm install aws-cdk-lib constructs
npm install --save-dev aws-cdk typescript ts-node @types/node
mkdir bin lib
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts"
}
// bin/app.ts
import * as cdk from "aws-cdk-lib";
import { IacReviewDemoStack } from "../lib/iac-review-demo-stack";
const app = new cdk.App();
new IacReviewDemoStack(app, "IacReviewDemoStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION ?? "us-east-1",
},
});
// lib/iac-review-demo-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as logs from "aws-cdk-lib/aws-logs";
import * as s3 from "aws-cdk-lib/aws-s3";
export class IacReviewDemoStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const stage = this.node.tryGetContext("stage") ?? "dev";
const isProd = stage === "prod";
const table = new dynamodb.Table(this, "AppTable", {
partitionKey: { name: "pk", type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: isProd,
deletionProtection: isProd,
removalPolicy: isProd ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
});
const bucket = new s3.Bucket(this, "PrivateAssetsBucket", {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
enforceSSL: true,
encryption: s3.BucketEncryption.S3_MANAGED,
versioned: true,
removalPolicy: isProd ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: !isProd,
});
const fn = new lambda.Function(this, "ApiHandler", {
runtime: lambda.Runtime.NODEJS_20_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async () => ({
statusCode: 200,
body: JSON.stringify({
tableName: process.env.TABLE_NAME,
bucketName: process.env.BUCKET_NAME
})
});
`),
environment: {
TABLE_NAME: table.tableName,
BUCKET_NAME: bucket.bucketName,
STAGE: stage,
},
timeout: cdk.Duration.seconds(10),
memorySize: 256,
logRetention: logs.RetentionDays.ONE_WEEK,
});
table.grantReadWriteData(fn);
bucket.grantPut(fn);
fn.addToRolePolicy(
new iam.PolicyStatement({
sid: "DenyInsecureS3Transport",
effect: iam.Effect.DENY,
actions: ["s3:*"],
resources: [bucket.bucketArn, bucket.arnForObjects("*")],
conditions: { Bool: { "aws:SecureTransport": "false" } },
}),
);
cdk.Tags.of(this).add("Project", "ClaudeCodeLab");
cdk.Tags.of(this).add("Stage", stage);
new cdk.CfnOutput(this, "FunctionName", { value: fn.functionName });
new cdk.CfnOutput(this, "BucketName", { value: bucket.bucketName });
new cdk.CfnOutput(this, "TableName", { value: table.tableName });
}
}
Review before deploying:
npx cdk synth -c stage=dev
npx cdk diff -c stage=dev
claude -p "
Review this cdk diff.
Only flag deletes, replacements, IAM privilege expansion, S3 exposure, DynamoDB cost, and Lambda environment-variable leakage.
$(npx cdk diff -c stage=dev 2>&1)
"
autoDeleteObjects: !isProd is convenient for a development sandbox, but it would be a serious production mistake. That is the kind of environment-specific guardrail Claude Code should call out.
Use Case 3: Explain Production Diffs
Small TypeScript edits can become large CloudFormation changes. Ask Claude Code to translate the diff for approvers:
claude -p "
Summarize this cdk diff for a product owner who is not an AWS specialist.
Use columns:
- change type
- target resource
- user impact
- security impact
- cost impact
- question before approval
$(npx cdk diff -c stage=prod 2>&1)
"
The recurring high-risk items are IAM Action and Resource, security groups open to 0.0.0.0/0, missing deletion protection, S3 public access, CloudFront or NAT Gateway additions, and stateful replacements.
Use Case 4: Standardize Rollback and Drift Checks
When deployment fails, console fixes can create drift. Use CloudFormation events for diagnosis, then put the permanent fix back into IaC.
aws cloudformation describe-stacks --stack-name cclab-iac-demo-dev
aws cloudformation detect-stack-drift --stack-name cclab-iac-demo-dev
aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id YOUR_DETECTION_ID
claude -p "
This stack is in UPDATE_ROLLBACK_FAILED.
Read the events and separate likely causes, resources safe to touch, resources not safe to touch, and AWS documentation steps to verify.
$(aws cloudformation describe-stack-events --stack-name cclab-iac-demo-dev 2>&1)
"
Common Pitfalls
Over-broad IAM. Do not accept Action: "*" or Resource: "*" because it works. Scope actions and ARNs to the actual use case, and use conditions when they reduce risk.
Deploying without reading the diff. cdk deploy should not be the first interesting command. Use synth, diff, and change sets before production.
Ignoring cost guardrails. ALB, NAT Gateway, RDS, OpenSearch, and always-on ECS can create costs even when traffic is low. Ask Claude Code to list resources with fixed monthly cost.
Treating manual console changes as permanent fixes. Manual changes may be necessary during an incident, but they must be reconciled into CloudFormation or CDK afterward.
Hardcoding secrets. Keep passwords and API keys out of templates and Git history. Use Secrets Manager or SSM Parameter Store patterns and tell Claude Code not to print secret values.
Review Prompt
You are a strict AWS CloudFormation/CDK reviewer.
Review this IaC diff before production.
Check:
- least-privilege IAM Action/Resource/Condition
- accidental public access through security groups or S3
- delete or replacement risk for stateful resources
- added fixed monthly cost such as ALB, NAT Gateway, RDS, or OpenSearch
- likely CloudFormation drift
- resources requiring manual rollback steps
Output:
1. issues to fix now
2. questions for the approver
3. commands to run before deploy
4. commands to run after deploy
Learn More
For deeper IAM review, read Claude Code and AWS IAM. For the compute side, pair this with Claude Code and AWS Lambda, Claude Code and AWS API Gateway, and Claude Code and AWS DynamoDB.
ClaudeCodeLab also offers practical products and team training for turning these prompts into pull request templates, release checklists, and review workflows.
Summary
Claude Code works best with CloudFormation and CDK when it acts as a strict second reviewer. Let it draft, but require it to explain IAM, cost, replacements, rollback, and drift before anyone deploys. The hands-on result from this workflow is simple: teams catch more production risks when Claude Code reviews cdk diff and change sets than when it only generates templates.
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Obsidian to CLAUDE.md Workflow: Stop Re-explaining Context
Turn Obsidian working notes into concise CLAUDE.md operating notes that make Claude Code sessions easier to resume.
Claude Code Revenue CTA Routing: Send Articles to PDF, Gumroad, and Consultation
A Claude Code workflow for routing article readers to the free PDF, Gumroad products, or consultation by intent.
Claude Code Team Handoff Rules: Review Evidence, Permissions, Rollback, and Revenue Paths
A practical Claude Code handoff format for team review, proof, permission rules, rollback, free PDF, Gumroad, and consultation paths.
Related Products
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.