Use Cases (Updated: 6/3/2026)

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.

Using Claude Code with AWS CloudFormation and CDK: A Practical IaC Review Guide

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:

Set the Boundary First

The safest workflow is to ask Claude Code to review, not just generate.

TaskClaude Code helps withHuman must approve
RequirementsResource list, environments, dependenciesWhether the resource is needed
Template draftCloudFormation/CDK first passNaming, deletion policy, cost
IAMCandidate actions and resourcesLeast privilege and conditions
Diff reviewPlain-language summary of cdk diff or a change setDeletes, replacements, privilege expansion
Failure handlingEvent analysis and likely fixesRollback 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.

#claude-code #aws #cloudformation #cdk #iac #typescript
Free

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.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.