用 Claude Code 审查 AWS CloudFormation 和 CDK:实战 IaC 指南
用Claude Code安全审查AWS CDK与CloudFormation,覆盖IAM、diff、漂移、回滚与护栏。
在 AWS Console 里点几下很快,但几个月后要解释“为什么生产环境和 staging 不一样”就会很痛苦。CloudFormation 和 AWS CDK 可以把基础设施变成代码,不过 IaC 并不自动等于安全。一个过宽的 IAM policy、一次无意的资源替换、一个忘记关掉的 NAT Gateway,都可能带来安全或成本事故。
我更推荐把 Claude Code 当作 IaC 的起草和审查伙伴,而不是自动部署机器人。它适合生成 CloudFormation/CDK 初稿、解释 cdk diff、检查 IAM 权限扩大、把 CloudFormation 事件整理成回滚清单。最终是否部署,仍然要由人来确认。
给初学者的解释:CloudFormation 是 AWS 官方的模板执行服务;CDK 是用 TypeScript 等语言描述基础设施,再合成为 CloudFormation 模板的开发工具;stack 是一组一起创建、更新、删除的资源;drift(漂移)是实际 AWS 资源和模板不一致。
本文基于 2026 年 6 月 3 日的 AWS 官方文档:
- 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
先划清 Claude Code 的职责
安全的做法不是直接说“帮我部署”,而是让 Claude Code 做严格审查。
| 工作 | Claude Code 适合做 | 人必须确认 |
|---|---|---|
| 需求整理 | 资源、环境、依赖关系 | 是否真的需要这些资源 |
| 模板初稿 | CloudFormation/CDK 代码 | 命名、删除策略、成本 |
| IAM | 候选 Action、Resource、Condition | 是否最小权限 |
| 差异审查 | 解释 cdk diff 或 change set | 删除、替换、权限扩大 |
| 故障处理 | 读取事件、提出修复候选 | 回滚方案和生产操作 |
flowchart LR
A["写清需求"] --> B["Claude Code 起草 IaC"]
B --> C["人工检查命名和成本"]
C --> D["运行 cdk diff 或 change set"]
D --> E["Claude Code 做风险审查"]
E --> F["批准后部署"]
F --> G["记录 drift 和 rollback 检查"]
用例1:把小范围现有资源写成 CloudFormation
不要一开始就把整个生产环境导入。先选一个风险低的切片。下面的模板创建一个私有 S3 bucket,以及一个只读该 bucket 的 Lambda IAM role。因为会创建命名 IAM role,所以需要 CAPABILITY_NAMED_IAM。
mkdir cfn-iac-review-demo
cd cfn-iac-review-demo
# 将下面的 YAML 保存为 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
部署前先让 Claude Code 严格审查:
claude -p "
请把 cfn-safe-iac-demo.yaml 当作 CloudFormation 模板审查。
只用表格指出:
1. 是否可能公开访问
2. IAM Resource 是否过宽
3. 删除或替换时是否会丢数据
4. 是否会产生月度费用
5. 生产 change set 中必须确认什么
"
再创建并查看 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
重点看 Add、Modify、Remove 和 Replacement。对无状态 demo role 来说替换问题不大,但对 S3、RDS、DynamoDB、KMS key、VPC 子网就是高风险。
用例2:用 CDK TypeScript 写最小安全栈
新项目更适合用 CDK TypeScript。类型和文件结构清楚,Claude Code 生成后也更容易审查。下面示例创建 DynamoDB、S3 和 Lambda,不包含 API Gateway,成本和范围都更小。
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 ?? "ap-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,再 deploy:
npx cdk synth -c stage=dev
npx cdk diff -c stage=dev
claude -p "
审查这个 cdk diff。
只指出删除、替换、IAM 权限扩大、S3 暴露、DynamoDB 成本、Lambda 环境变量泄露。
$(npx cdk diff -c stage=dev 2>&1)
"
autoDeleteObjects: !isProd 在开发环境很方便,但如果进入生产就是严重问题。Claude Code 应该帮助你发现这类环境护栏是否失效。
用例3:把生产 diff 翻译给审批人
CDK 的小改动可能在 CloudFormation 中变成资源替换。让 Claude Code 用审批人能理解的语言总结:
claude -p "
请把下面的 cdk diff 总结给不熟悉 AWS 的产品负责人。
列包含:
- 变更类型
- 目标资源
- 用户影响
- 安全影响
- 成本影响
- 批准前要问的问题
$(npx cdk diff -c stage=prod 2>&1)
"
高风险项通常是 IAM Action 和 Resource、对 0.0.0.0/0 开放的 security group、缺少删除保护、S3 public access、CloudFront/NAT Gateway 增加,以及有状态资源替换。
用例4:标准化回滚和 drift 检查
部署失败后直接在 Console 修,很容易制造 drift。先用事件诊断,再把永久修复回写到 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 "
这个 stack 处于 UPDATE_ROLLBACK_FAILED。
阅读 events,并分开说明可能原因、可以触碰的资源、不能触碰的资源、需要按 AWS 官方文档确认的步骤。
$(aws cloudformation describe-stack-events --stack-name cclab-iac-demo-dev 2>&1)
"
常见坑
IAM 太宽。 不要因为能运行就接受 Action: "*" 或 Resource: "*"。按真实用例收窄 action、ARN 和 condition。
不读 diff 就 deploy。 生产前至少要有 synth、diff、change set。看到 Replacement 时停下来确认。
忽视固定成本。 ALB、NAT Gateway、RDS、OpenSearch、常驻 ECS 在低流量时也会收费。让 Claude Code 列出固定月费资源。
把 Console 手动修改当作永久修复。 事故期间可以手动救火,但之后必须回到 CloudFormation 或 CDK。
明文写 secret。 密码和 API key 不应进入模板和 Git 历史。使用 Secrets Manager 或 SSM Parameter Store,并要求 Claude Code 不输出秘密值。
审查提示词
你是严格的 AWS CloudFormation/CDK 审查者。
请在生产发布前审查这个 IaC diff。
检查:
- IAM Action/Resource/Condition 是否最小权限
- security group 或 S3 是否意外公开
- 有状态资源是否会删除或替换
- 是否新增 ALB、NAT Gateway、RDS、OpenSearch 等固定月费
- 是否可能存在 CloudFormation drift
- 回滚时是否需要手动步骤
输出:
1. 现在必须修的问题
2. 给审批人的问题
3. deploy 前要运行的命令
4. deploy 后要运行的命令
延伸阅读
IAM 审查可继续阅读 Claude Code 与 AWS IAM。计算和 API 层建议搭配 AWS Lambda、AWS API Gateway 和 AWS DynamoDB。
如果你想把 IaC 审查变成团队流程,ClaudeCodeLab 的实践教材和团队培训可以帮助你把这些提示词嵌入 Pull Request 模板和发布检查清单。
总结
Claude Code 与 CloudFormation/CDK 配合得最好时,它不是自动部署者,而是严格的第二审查者。让它起草代码,但在部署前必须解释 IAM、成本、替换、回滚和 drift。实际操作后的结论很明确:让 Claude Code 审查 cdk diff 和 change set,比只让它生成模板更能发现生产风险。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。