site logo

Marico's space

AWS CloudFormation — 基础设施即代码、模板、Stacks 与 Change Sets

服务器技术 2026-08-10 17:36:42 5

最近在系统性地把 AWS 基础打扎实,准备从传统运维往云/DevOps方向转。CloudFormation 是 AWS 基础设施自动化的地基——把"在控制台点点点"变成"可以 review、版本化、可复现的代码",这是云工程师的必备技能。

Topics Covered

# 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

What is IaC and Why It Exists

没有 IaC(基础设施即代码)之前,基础设施靠人工管理——有人 SSH 登录服务器、在控制台点点点,祈祷自己能记住改了什么。等这人离职了,知识也跟着走了。要创建三个一模一样的环境,就在控制台点三遍,祈祷别出错。

IaC(Infrastructure as Code,基础设施即代码) 是通过代码或配置文件来定义和管理基础设施的实践——让基础设施可以自动创建、更改、版本化和复现。

不再在控制台点击"创建 ECS 实例",而是写一个文件说"我要一个这个配置的 ECS 实例"。把这个文件交给工具,工具来创建实例。文件进 Git。下次需要同样的实例,跑同一个文件。完全相同的基础设施,零手动步骤。

应用代码的那些原则,现在同样适用于基础设施:

  • 版本控制——每次变更用 commit message 追踪
  • 代码审查——同事在变更应用之前先 review
  • CI/CD 流水线——基础设施变更自动测试和部署
  • 可复现性——同一个模板每次产生完全相同的基础设施
  • 文档化——模板本身就是文档

IaC Benefits

Benefit What it means in practice
Reproducibility 可复现性 同一模板在任何区域或账号 → 完全相同的基础设施
Version control 版本控制 每次变更都在 Git 里追踪——谁、什么、什么时候、为什么
Auditability 可审计性 完整的基础设施变更历史——合规必备
Automation 自动化 CI/CD 流水线无需人工干预自动部署基础设施变更
Drift detection 漂移检测 将实际基础设施与模板对比——发现未授权的手动变更
Disaster recovery 灾难恢复 几分钟内在新区域重建整个基础设施
Consistency 一致性 dev、staging、prod 都用同一模板构建——不再有"staging 没问题、prod 挂了"

What is CloudFormation

AWS CloudFormation 是 AWS 原生的 IaC 服务。写一个模板(YAML 或 JSON),上传,CloudFormation 会帮你创建、更新和管理所有资源。

把 CloudFormation 想象成一个按蓝图工作的施工经理。你的 YAML/JSON 模板就是蓝图——精确描述你的 AWS 基础设施应该长什么样。CloudFormation 读取蓝图、构建基础设施、记住建了什么。你改蓝图时,施工经理不会把整栋楼拆掉——他们找出哪些变了,只改受影响的部分。

为什么要用 CloudFormation 而不是控制台点点点:

  • 控制台点击无法复现——精确的操作顺序不会被记录
  • 模板可以被 review、审批、审计
  • 同一模板在不同环境创建完全相同的基础设施
  • 变更应用前可以通过 Change Sets 预览
  • 部署过程中出问题自动回滚

How CloudFormation Works in the Backend

搞清楚这个,排障会简单很多。以下是完整的内部流程,用编号序列表示:

完整的后端流程:

[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_COMPLETEUPDATE_COMPLETE
  • 失败 → Rollback 回滚: CloudFormation 尝试按反向 DAG 顺序撤销变更 → 使 stack 恢复到之前稳定状态

🎯 这对排障为什么重要: 当 stack 卡在 CREATE_IN_PROGRESS 时,说明它在 Stabilization Loop 里——等待某个资源就绪。看 Events 标签页——会显示在等哪个资源、为什么。失败时,错误来自 Resource Provider 步骤中底层 AWS API 调用。

CloudFormation Template Structure — All Sections with Limits

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 都是可选的。

Template Section Plain-English Explanations

最清晰的理解方式——每个 section 回答什么问题:

Section Plain-English Question It Answers
AWSTemplateFormatVersion 用什么模板格式?
Description 这个蓝图是干什么用的?
Metadata 蓝图的额外信息
Parameters 部署的人需要提供什么选项?
Rules 这些选项合法吗?
Mappings 预定义值的查找表
Conditions 这部分要构建吗?
Transform 展开/处理蓝图
Resources ⭐ 实际要构建什么?
Outputs 构建完成后应该返回什么有用信息?

Template vs Stack — The Distinction

Template 模板: 一个 YAML 或 JSON 文件——蓝图。一个文本文件,自身不创建任何东西。存在于 Git、OSS 或本地。

Stack 栈: CloudFormation 执行模板时创建的东西。一组 AWS 资源作为一个单元被管理。栈追踪它创建的所有资源、它们的状态、以及生成它们的模板。

同一模板创建多个栈:

webapp.yaml(Git 里一个文件)
→ 栈:webapp-dev → dev 账号的 dev 资源
→ 栈:webapp-staging → staging 账号的 staging 资源
→ 栈:webapp-prod → prod 账号的 prod 资源

三个环境,相同配置,零手动工作。

CloudFormation Building Blocks

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 行。

YAML Basics for CF Templates

# 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 用两个空格缩进表示嵌套。一个缩进错了,整个模板就废了。

CF Parameters

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 个参数。

CF Rules

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 才是正确的工具。

CF Mappings

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 个属性。

CF Outputs

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"

三种用途:

  • 控制台可见性 —— 栈创建后,Outputs 显示在 CloudFormation 控制台。不用在各个服务控制台里翻找,直接找到 ALB DNS 名称或 RDS 端点。
  • 跨栈引用 —— 一个栈导出 Output,另一个栈用 Fn::ImportValue 导入。网络栈导出 VPC ID → 所有应用栈导入它。
  • 自动化 —— 脚本查询 Outputs 动态获取资源标识符。

限制:每个模板最多 200 个 outputs。

CF Conditions

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。

CF Intrinsic Functions

内置函数,用于构建动态值、引用资源、操作字符串。

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"]]

CF Service Roles

默认情况下,CloudFormation 用你自己的 IAM 权限来创建资源。Service Role 是一个 CloudFormation 会 assumed 的 IAM Role——把人类能做什么和 CloudFormation 能做什么解耦。

场景: 一个开发者需要部署一个栈,创建 VPC、安全组和 ECS 实例。你可以直接给开发者这些 IAM 权限——但这样他也能在 CloudFormation 之外手动创建这些资源,绕过管控。

用 Service Role: 开发者只有 cloudformation:CreateStackiam:PassRole。Service Role 有 ec2:*vpc:*s3:*——栈需要什么就给什么。CloudFormation assumed 这个角色并创建资源。开发者获得基础设施,但没有直接访问底层服务的权限。

🎯 安全收益: 人类最小权限,自动化必要权限。人类无法绕过 CloudFormation 流程手动创建资源。

CF Stack Policies and Deletion Policies

Stack Policy 栈策略

附加到栈的 JSON 文档,控制栈更新期间哪些资源可以被更新或替换。

{ "Statement": [{ "Effect": "Deny", "Action": "Update:Replace", "Principal": "*", "Resource": "LogicalResourceId/ProductionDatabase" }]
}

这阻止了对 ProductionDatabase 资源的 Replace 操作——即使模板变更通常需要替换。CloudFormation 会让更新失败,而不是替换数据库。

Deletion Policy 删除策略

在模板内单个资源上设置——控制栈删除或资源从模板移除时会发生什么。

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 策略丢了生产数据库,是真实发生的、很痛的错误。

CF Change Sets

Change Set 变更集 是在实际执行之前预览 CloudFormation 要做什么——显示每个资源动作:新增、修改、删除、替换。

Replace 动作意味着资源被删除并重新创建——数据库、ECS 实例等会有停机。Change Set 让你在这种事发生之前就发现。

Change Set 工作流:

修改 YAML 模板 → 上传新模板 → 创建 Change Set → CloudFormation 计算差异 → review 每个提议的变更 → 如果正确:执行 → CloudFormation 应用 → 如果有风险或错误:删除 Change Set → 什么都没应用,栈原封不动。

什么时候一定要用 Change Sets: 更新任何生产栈之前——在 Replace 发生前发现,是计划内维护和计划外故障之间的区别。

CF Stack Sets

多个 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 栈实例 = 一个特定账号 + 区域组合中部署的一个栈。

Lab — Full Build-Up with Real Failures

这是课堂上的完整实验序列——用 Change Sets 一步一步构建完整网络 + ECS 栈。包含失败,因为这就是 CloudFormation 的真实情况——知道怎么修复才是技能。

Overall Lab Flow 整体实验流程

YAML 蓝图 → Parameters → VPC → 子网 → Internet Gateway → Gateway Attachment → 路由表 → 安全组 → 密钥对参数 → ECS → 通过 !Ref 建立依赖 → Change Sets → 执行 → 失败 → 修复模板/参数 → 新 Change Set → 执行 → 最终基础设施 ✅

Step-by-Step Lab — Exact Sequence 分步实验——精确序列

开始:创建 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 创建成功 ✅

What the Lab Teaches 实验教会什么

这些失败不是流程的 bug——它们就是 lesson:

Invalid AZ 失败: 模板包含硬编码的 AZ,在目标区域不存在。修复:用参数指定 AZ 名称,或用 !Select [0, !GetAZs !Ref AWS::Region] 动态选择第一个可用 AZ。

Invalid Key Pair