
最近在系统性地把 AWS 基础打扎实,准备从传统运维往云/DevOps方向转。CloudFormation 是 AWS 基础设施自动化的地基——把"在控制台点点点"变成"可以 review、版本化、可复现的代码",这是云工程师的必备技能。
| # | Topic | Type |
|---|---|---|
| 1 | What is IaC and Why It Exists | Concept + Interview |
| 2 | IaC Benefits | Concept |
| 3 | What is CloudFormation | Concept + Interview |
| 4 | How CloudFormation Works in the Backend | Concept + Interview |
| 5 | CloudFormation Template Structure — All Sections with Limits | Concept + Lab |
| 6 | Template Section Plain-English Explanations | Concept |
| 7 | Template vs Stack — The Distinction | Concept + Interview |
| 8 | CloudFormation Building Blocks | Concept + Cert |
| 9 | YAML Basics for CF Templates | Concept + Lab |
| 10 | CF Parameters | Concept + Lab |
| 11 | CF Rules | Concept + Cert |
| 12 | CF Mappings | Concept + Cert |
| 13 | CF Outputs | Concept + Lab |
| 14 | CF Conditions | Concept + Cert |
| 15 | CF Intrinsic Functions | Concept + Cert |
| 16 | CF Service Roles | Concept + Interview |
| 17 | CF Stack Policies and Deletion Policies | Concept + Interview |
| 18 | CF Change Sets | Concept + Lab |
| 19 | CF Stack Sets | Concept + DevOps |
| 20 | Lab — Full Build-Up with Real Failures | Lab |
| 21 | Interview Questions | Interview |
| 22 | Assignment | Practice |
没有 IaC(基础设施即代码)之前,基础设施靠人工管理——有人 SSH 登录服务器、在控制台点点点,祈祷自己能记住改了什么。等这人离职了,知识也跟着走了。要创建三个一模一样的环境,就在控制台点三遍,祈祷别出错。
IaC(Infrastructure as Code,基础设施即代码) 是通过代码或配置文件来定义和管理基础设施的实践——让基础设施可以自动创建、更改、版本化和复现。
不再在控制台点击"创建 ECS 实例",而是写一个文件说"我要一个这个配置的 ECS 实例"。把这个文件交给工具,工具来创建实例。文件进 Git。下次需要同样的实例,跑同一个文件。完全相同的基础设施,零手动步骤。
应用代码的那些原则,现在同样适用于基础设施:
| Benefit | What it means in practice |
|---|---|
| Reproducibility 可复现性 | 同一模板在任何区域或账号 → 完全相同的基础设施 |
| Version control 版本控制 | 每次变更都在 Git 里追踪——谁、什么、什么时候、为什么 |
| Auditability 可审计性 | 完整的基础设施变更历史——合规必备 |
| Automation 自动化 | CI/CD 流水线无需人工干预自动部署基础设施变更 |
| Drift detection 漂移检测 | 将实际基础设施与模板对比——发现未授权的手动变更 |
| Disaster recovery 灾难恢复 | 几分钟内在新区域重建整个基础设施 |
| Consistency 一致性 | dev、staging、prod 都用同一模板构建——不再有"staging 没问题、prod 挂了" |
AWS CloudFormation 是 AWS 原生的 IaC 服务。写一个模板(YAML 或 JSON),上传,CloudFormation 会帮你创建、更新和管理所有资源。
把 CloudFormation 想象成一个按蓝图工作的施工经理。你的 YAML/JSON 模板就是蓝图——精确描述你的 AWS 基础设施应该长什么样。CloudFormation 读取蓝图、构建基础设施、记住建了什么。你改蓝图时,施工经理不会把整栋楼拆掉——他们找出哪些变了,只改受影响的部分。
为什么要用 CloudFormation 而不是控制台点点点:
搞清楚这个,排障会简单很多。以下是完整的内部流程,用编号序列表示:
完整的后端流程:
[1] Parse + Validate 解析验证: 读取你的 YAML/JSON,检查语法错误,验证资源类型和属性是否正确。
[2] Transform Expansion 转换扩展: 展开任何转换——SAM 简写语法、Includes、Language Extensions、Modules——转换成完整的 CloudFormation 资源定义。
[3] Pre-deployment Validation 部署前验证: 在部署开始前检查请求的资源与配置是否合理。验证 IAM 权限和配额限制。
[4] Build Dependency Graph (DAG) 构建依赖图: 确定资源依赖关系——谁依赖谁、谁先谁后。没有依赖的资源标记为可并行执行。有依赖的资源按正确顺序排列。
[5] Compute Change Set 计算变更集: 将当前 AWS 状态(存在 CloudFormation 的事务状态存储中)与新模板对比,确定具体要变更什么——新增、修改、删除、替换。
[6] Evaluate Hooks / Guardrails 评估钩子/防护规则: 在执行变更前检查预定义的组织/安全规则。比如:验证每个 OSS bucket 在创建前都已启用加密。
[7] Parallel Execution by DAG topological order 按 DAG 拓扑序并行执行: 没有依赖的资源同时创建/更新。有依赖的资源按顺序执行。每个资源:Resource Provider 处理实际的 Create/Update/Delete API 调用。然后 → Stabilization Loop 稳定化循环 —— CloudFormation 持续轮询直到资源达到所需就绪状态。API 调用成功不代表资源就绪。有些资源的稳定化可能需要 20-90 分钟(RDS、Elasticsearch 等)。
[8] Success or Failure 成功或失败:
- 成功 → State Committed 状态提交: 所有资源都达到目标状态 → stack 变为
CREATE_COMPLETE或UPDATE_COMPLETE- 失败 → Rollback 回滚: CloudFormation 尝试按反向 DAG 顺序撤销变更 → 使 stack 恢复到之前稳定状态
🎯 这对排障为什么重要: 当 stack 卡在
CREATE_IN_PROGRESS时,说明它在 Stabilization Loop 里——等待某个资源就绪。看 Events 标签页——会显示在等哪个资源、为什么。失败时,错误来自 Resource Provider 步骤中底层 AWS API 调用。
CloudFormation 模板是一个 YAML 或 JSON 文件,有以下结构。每个 section 都有限制,生产和 SAA-C03 考试都需要知道。
AWSTemplateFormatVersion: "2010-09-09" # optional, but always write it
Description: "..." # max 1024 bytes
Metadata: {} # console UI hints, cfn-init config
Parameters: {} # max 200
Rules: {} # UNDER-USED — param validation logic
Mappings: {} # max 200 mappings, 200 attrs each
Conditions: {} # max 512
Transform: [] # macro/SAM expansion
Resources: {} # REQUIRED — max 500
Outputs: {} # max 200
只有 Resources 是必需的。 其他 section 都是可选的。
最清晰的理解方式——每个 section 回答什么问题:
| Section | Plain-English Question It Answers |
|---|---|
AWSTemplateFormatVersion |
用什么模板格式? |
Description |
这个蓝图是干什么用的? |
Metadata |
蓝图的额外信息 |
Parameters |
部署的人需要提供什么选项? |
Rules |
这些选项合法吗? |
Mappings |
预定义值的查找表 |
Conditions |
这部分要构建吗? |
Transform |
展开/处理蓝图 |
Resources |
⭐ 实际要构建什么? |
Outputs |
构建完成后应该返回什么有用信息? |
Template 模板: 一个 YAML 或 JSON 文件——蓝图。一个文本文件,自身不创建任何东西。存在于 Git、OSS 或本地。
Stack 栈: CloudFormation 执行模板时创建的东西。一组 AWS 资源作为一个单元被管理。栈追踪它创建的所有资源、它们的状态、以及生成它们的模板。
同一模板创建多个栈:
webapp.yaml(Git 里一个文件)
→ 栈:webapp-dev→ dev 账号的 dev 资源
→ 栈:webapp-staging→ staging 账号的 staging 资源
→ 栈:webapp-prod→ prod 账号的 prod 资源
三个环境,相同配置,零手动工作。
SAM(Serverless Application Model,无服务器应用模型)
无服务器场景(Lambda、API Gateway、DynamoDB)的简写语法,用简化形式编写。
Transform: AWS::Serverless-2016-10-31宏在部署时将 SAM 展开为完整 CloudFormation。原本要 50 行定义一个 Lambda 及其角色和日志组,SAM 让你用 10 行搞定。
Include
将另一个模板文件的内容拉到当前模板——把大模板拆分成专注、易维护的文件。
Language Extensions
添加额外的语法特性——原生字符串操作、length 函数、
ToJsonString——减少重复代码,让模板更灵活。
Modules
可复用的基础设施组件,跨多个模板使用。把"标准 VPC 设置"定义为模块一次——在每个团队的模板里引用,不用重复写 200 行。
# Comment in YAML key: value # String
number: 42 # Integer
boolean: true # Boolean
list: # List/Array - item1 - item2
nested: # Nested object/map key1: value1 key2: value2
multiline: | # Multi-line string (preserves newlines) line one line two
YAML 里缩进就是一切。 不像 JSON(花括号),YAML 用两个空格缩进表示嵌套。一个缩进错了,整个模板就废了。
Parameters 让你避免硬编码值——部署时由部署者提供。
同一模板在 dev 创建
t3.micro、在 prod 创建c5.2xlarge——只要传不同的值就行。无需修改模板。
Parameters: InstanceType: Type: String Default: t3.micro AllowedValues: - t3.micro - t3.small - t3.medium - t3.large Description: "EC2 instance type for the web server" Environment: Type: String AllowedValues: - dev - staging - prod Description: "Deployment environment"
Parameter types 参数类型:
| Type | Use for |
|---|---|
String |
文本值、名称、标识符 |
Number |
数值 |
CommaDelimitedList |
多个字符串 |
AWS::EC2::KeyPair::KeyName |
部署前验证密钥对是否存在 |
AWS::EC2::VPC::Id |
部署前验证 VPC ID 是否存在 |
AWS::EC2::Subnet::Id |
部署前验证子网 ID 是否存在 |
AWS::SSM::Parameter::Value<String> |
直接从 SSM Parameter Store 拉取值 |
🎯 面试技巧: AWS 特定参数类型(如
AWS::EC2::VPC::Id)在栈开始部署前就验证——无效的 VPC ID 会立即被拒绝,不会等到一半才报错。
限制:每个模板最多 200 个参数。
Rules 是 Parameters 的验证层——确保参数组合合法,不只是 AllowedValues 能做到的。
AllowedValues可以限制单个参数为固定列表。但如果需要:"当 Environment 是prod时,InstanceType 不能是t3.micro"?这种跨参数的逻辑就是 Rules 要处理的。
Rules: ProdRequiresLargeInstance: Assertions: - Assert: !Or - !Not [!Equals [!Ref Environment, prod]] - !Not [!Equals [!Ref InstanceType, t3.micro]] AssertDescription: "Production environment cannot use t3.micro"
🎯 注意: Rules 在实践中被标记为"UNDER-USED"——大多数团队用带
AllowedValues的 Parameters 做简单验证,用 Conditions 做条件资源创建。但当你需要验证多个参数值放在一起是否合理时,Rules 才是正确的工具。
Mappings 是模板内置的查找表。经典用法——按区域存储 AMI ID,这样部署者不需要知道这些 ID。
Mappings: RegionAMIMap: ap-south-1: AMI: ami-0abcdef1234567890 us-east-1: AMI: ami-0987654321fedcba eu-west-1: AMI: ami-0a1b2c3d4e5f67890 Resources: MyEC2: Type: AWS::EC2::Instance Properties: ImageId: !FindInMap [RegionAMIMap, !Ref AWS::Region, AMI]
部署到 Mumbai → 自动使用 Mumbai 的 AMI。部署者不用碰 AMI ID。
其他用法: 环境特定的实例规格(prod 用 t3.large,dev 用 t3.micro)、区域特定的端点 URL。
限制:最多 200 个 mappings,每个 200 个属性。
Outputs 是栈创建后 CloudFormation 暴露的值——重要的资源标识符,方便查找和复用。
Outputs: BucketName: Description: "Name of the created S3 bucket" Value: !Ref MyBucket BucketARN: Description: "ARN of the S3 bucket" Value: !GetAtt MyBucket.Arn Export: Name: !Sub "${AWS::StackName}-BucketARN"
三种用途:
Fn::ImportValue 导入。网络栈导出 VPC ID → 所有应用栈导入它。限制:每个模板最多 200 个 outputs。
Conditions 添加布尔逻辑——只在特定条件为真时创建资源或设置属性值。
Parameters: Environment: Type: String AllowedValues: [dev, prod] Conditions: IsProduction: !Equals [!Ref Environment, prod] Resources: MyBucket: Type: AWS::S3::Bucket Properties: VersioningConfiguration: Status: !If [IsProduction, Enabled, Suspended] ReadReplica: Type: AWS::RDS::DBInstance Condition: IsProduction # Only created when IsProduction is true Properties: # read replica config...
Condition functions 条件函数:
| Function | What it does |
|---|---|
!Equals [a, b] |
如果 a 等于 b 则为真 |
!Not [condition] |
反转条件 |
!And [c1, c2] |
两个都为真才为真 |
!Or [c1, c2] |
任一为真即为真 |
!If [condition, true_value, false_value] |
条件值 |
限制:每个模板最多 512 个 conditions。
内置函数,用于构建动态值、引用资源、操作字符串。
| Function | Short form | What it does |
|---|---|---|
Ref |
!Ref |
资源的默认标识符或参数值 |
Fn::GetAtt |
!GetAtt |
资源的特定属性(ARN、DNS 名称、IP) |
Fn::Sub |
!Sub |
字符串替换——插入变量值 |
Fn::Join |
!Join |
用分隔符连接列表 |
Fn::Select |
!Select |
按索引从列表返回一个项 |
Fn::FindInMap |
!FindInMap |
在 Mappings 表中查找值 |
Fn::If |
!If |
根据 Condition 返回两个值之一 |
Fn::ImportValue |
— | 导入另一个栈导出的 Output |
Fn::Base64 |
!Base64 |
将字符串编码为 Base64(用于 ECS User Data) |
Fn::Split |
!Split |
将字符串拆分为列表 |
!Ref vs !GetAtt——关键区别:
!Ref MyBucket→ bucket 名称(S3 的默认标识符)
!GetAtt MyBucket.Arn→ bucket ARN(特定属性)
!GetAtt MyInstance.PublicIp→ ECS 公网 IP(特定属性)
实用示例:
Resources: MyInstance: Type: AWS::EC2::Instance Properties: InstanceType: !Ref InstanceTypeParam # value from Parameter ImageId: !FindInMap [AMIMap, !Ref AWS::Region, AMI] # from Mappings Tags: - Key: Name Value: !Sub "${AWS::StackName}-webserver" # string substitution UserData: !Base64 | #!/bin/bash yum update -y yum install -y httpd systemctl start httpd Outputs: PublicIP: Value: !GetAtt MyInstance.PublicIp FullDNS: Value: !Join [".", ["api", !Ref Environment, "tejascloud.com"]]
默认情况下,CloudFormation 用你自己的 IAM 权限来创建资源。Service Role 是一个 CloudFormation 会 assumed 的 IAM Role——把人类能做什么和 CloudFormation 能做什么解耦。
场景: 一个开发者需要部署一个栈,创建 VPC、安全组和 ECS 实例。你可以直接给开发者这些 IAM 权限——但这样他也能在 CloudFormation 之外手动创建这些资源,绕过管控。
用 Service Role: 开发者只有
cloudformation:CreateStack和iam:PassRole。Service Role 有ec2:*、vpc:*、s3:*——栈需要什么就给什么。CloudFormation assumed 这个角色并创建资源。开发者获得基础设施,但没有直接访问底层服务的权限。🎯 安全收益: 人类最小权限,自动化必要权限。人类无法绕过 CloudFormation 流程手动创建资源。
附加到栈的 JSON 文档,控制栈更新期间哪些资源可以被更新或替换。
{ "Statement": [{ "Effect": "Deny", "Action": "Update:Replace", "Principal": "*", "Resource": "LogicalResourceId/ProductionDatabase" }]
}
这阻止了对 ProductionDatabase 资源的 Replace 操作——即使模板变更通常需要替换。CloudFormation 会让更新失败,而不是替换数据库。
在模板内单个资源上设置——控制栈删除或资源从模板移除时会发生什么。
| Deletion Policy | What happens when stack is deleted |
|---|---|
Delete (default) |
资源被删除 |
Retain |
资源保留,CloudFormation 停止管理 |
Snapshot |
删除前 AWS 做备份(RDS、EBS、Redshift) |
Resources: MyDatabase: Type: AWS::RDS::DBInstance DeletionPolicy: Snapshot # backup before delete Properties: {} MyBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain # keep the bucket, just stop managing it Properties: {}
🎯 生产规则: RDS 永远
DeletionPolicy: Snapshot。存有重要数据的 OSS bucket 永远DeletionPolicy: Retain。因为默认Delete策略丢了生产数据库,是真实发生的、很痛的错误。
Change Set 变更集 是在实际执行之前预览 CloudFormation 要做什么——显示每个资源动作:新增、修改、删除、替换。
Replace动作意味着资源被删除并重新创建——数据库、ECS 实例等会有停机。Change Set 让你在这种事发生之前就发现。
Change Set 工作流:
修改 YAML 模板 → 上传新模板 → 创建 Change Set → CloudFormation 计算差异 → review 每个提议的变更 → 如果正确:执行 → CloudFormation 应用 → 如果有风险或错误:删除 Change Set → 什么都没应用,栈原封不动。
什么时候一定要用 Change Sets: 更新任何生产栈之前——在 Replace 发生前发现,是计划内维护和计划外故障之间的区别。
在多个 AWS 账号和/或区域中部署同一 CloudFormation 栈,单次操作完成。
没有 Stack Sets:登录 10 个账号,在每个账号切 3 个区域,手动部署 30 次。
有 Stack Sets:定义一次目标账号和区域 → CloudFormation 并行部署到全部 30 个。
常见用途: 安全基线(CloudTrail、Config、GuardDuty)到每个账号、每个区域的集中日志、所有账号的标准网络。
两种模式:
| Mode | How targets are defined |
|---|---|
| Self-managed 自管理 | 显式列出账号 ID 和区域 |
| Service-managed (AWS Organizations) 服务托管 | 自动部署到 OU 中的所有账号——新账号加入 OU 后自动获得该栈 |
Stack Instance 栈实例 = 一个特定账号 + 区域组合中部署的一个栈。
这是课堂上的完整实验序列——用 Change Sets 一步一步构建完整网络 + ECS 栈。包含失败,因为这就是 CloudFormation 的真实情况——知道怎么修复才是技能。
YAML 蓝图 → Parameters → VPC → 子网 → Internet Gateway → Gateway Attachment → 路由表 → 安全组 → 密钥对参数 → ECS → 通过
!Ref建立依赖 → Change Sets → 执行 → 失败 → 修复模板/参数 → 新 Change Set → 执行 → 最终基础设施 ✅
开始:创建 VPC + CIDR
Create Stack → 初始模板只有 VPC 和 CIDR →
CREATE_COMPLETE✅
通过 Change Set 添加子网
修改模板——添加子网资源,用
!Ref引用 VPC → Create Change Set → Execute Change Set
❌ FAILED: Invalid Availability Zone —— 模板中的 AZ 名称错误
修复 AZ → 创建新 Change Set → Execute Change Set
子网创建成功 ✅
添加 Internet Gateway
修改模板——添加
AWS::EC2::InternetGateway,添加AWS::EC2::VPCGatewayAttachment用!Ref VPC和!Ref IGW→ Create Change Set → Execute
MyInternetGateway 创建成功 ✅
AttachGateway 创建成功 ✅
Internet Gateway 已附加到 VPC ✅
添加路由表
修改模板——添加
AWS::EC2::RouteTable,添加路由目标0.0.0.0/0→ IGW,添加子网路由表关联 → Create Change Set → Execute
路由表创建 + 关联成功 ✅
添加安全组
修改模板——添加
AWS::EC2::SecurityGroup带入站规则(SSH 22、HTTP 80)→ Create Change Set → Execute
安全组创建成功 ✅
添加密钥对作为参数 + 创建 ECS
修改模板——添加 KeyPair 参数(
Type: AWS::EC2::KeyPair::KeyName),添加 ECS 实例引用 Mappings 里的 AMI、用!Ref引用 SG 和子网、用!Ref KeyPairParam引用密钥对 → Create Change Set → Execute
❌ FAILED: Invalid/Non-existent Key Pair —— 输入的密钥对名称错误
更正 Key Pair 参数 → 创建新 Change Set → Execute Change Set
ECS 创建成功 ✅
这些失败不是流程的 bug——它们就是 lesson:
Invalid AZ 失败: 模板包含硬编码的 AZ,在目标区域不存在。修复:用参数指定 AZ 名称,或用
!Select [0, !GetAZs !Ref AWS::Region]动态选择第一个可用 AZ。Invalid Key Pair