4. Terraform plan深度解读
在 Terraform 大规模变更风险控制中,terraform plan 是唯一能在真正执行前把“将要发生什么”完整暴露出来的环节。但很多人只看了最后一行 Plan: 3 to add, 1 to change, 0 to destroy 就点确认,这在大规模环境下极其危险。本节把 plan 拆成四个可独立判读的层次:文件结构、动作符号、known after apply、敏感值与风险信号。
一、plan 文件的结构:从二进制到可读 JSON
执行 terraform plan -out=tfplan 生成的是二进制 plan 文件,它包含三部分:配置快照(当前 .tf 与变量解析结果)、状态快照(state 中已有资源)、变更计划(planned values 与 actions)。二进制文件不可直接阅读,但可以用 terraform show -json tfplan 转成结构化 JSON,这是做自动化风险扫描的基础。
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
jq '.resource_changes[] | {addr: .address, actions: .change.actions}' plan.json
JSON 中最关键的是 resource_changes 数组,每个元素包含 address(资源地址)、change.actions(动作数组)、change.before(当前值)、change.after(计划值)、change.after_unknown(执行后才知的值)。理解这五个字段,就掌握了 plan 的全部信息源。
二、资源动作符号:六种组合的真实含义
Terraform 用符号表示动作,但符号是数组而非单字符,必须按组合理解:
| 符号 | 含义 | 风险等级 |
|---|---|---|
+ | create,新建资源 | 低(除非配额/成本敏感) |
- | destroy,删除资源 | 高 |
~ | update in-place,原地更新 | 中,取决于字段 |
-/+ | replace,先删后建 | 极高,服务会中断 |
+/− | create before destroy,先建后删 | 中高,需双倍配额 |
−/+ 带 # forces replacement | 强制替换 | 极高,需重点审查 |
特别注意 -/+ 与 +/- 的区别:前者是先销毁再创建,对数据库、负载均衡等有状态资源意味着数据丢失和停机;后者依赖 lifecycle { create_before_destroy = true },虽然不中断但会短暂出现两份实例。在 plan 输出中,替换动作会附带 # forces replacement 注释,指明是哪个字段触发的,这是定位风险根因的关键线索。
三、known after apply:为什么计划值不完整
当某个属性的值依赖尚未创建的资源(如自动生成的 ID、ARN、IP),Terraform 无法在 plan 阶段确定它,会标记为 (known after apply)。这在 JSON 中体现为 after_unknown 字段为 true。
它的风险在于:依赖这些未知值的下游资源,其变更范围在 plan 阶段是不完整的。例如安全组规则引用新创建的安全组 ID,plan 只能显示“将新增一条规则”,但无法确认规则内容是否正确。大规模场景下,如果大量资源出现 known after apply,说明模块间耦合过深,plan 的确定性下降,应通过拆分 apply 批次或使用 terraform plan -refresh-only 先固化状态来降低不确定性。
四、敏感值处理与风险信号提取
标记为 sensitive = true 的变量和输出,在 plan 中显示为 (sensitive value),JSON 中对应字段被替换为占位符。这带来一个矛盾:安全上必须脱敏,但风险审查又需要看到真实值。工程做法是:在 CI 中把 plan JSON 上传到受控的 artifact 存储,由具备权限的审查者用 terraform show -json 配合内部工具解密查看,而不是在日志里明文打印。
风险信号提取应聚焦以下模式,可直接用 jq 或 OPA 策略实现:
- destroy 类动作:任何
-或-/+,尤其是aws_db_instance、aws_s3_bucket、aws_iam_role等有状态或权限资源。 - 替换动作:
actions包含delete且create,检查是否配置了create_before_destroy。 - 安全组/防火墙规则放宽:
after中出现0.0.0.0/0或端口范围扩大。 - IAM 策略变更:
Action: "*"或Resource: "*"的新增。 - known after apply 比例:统计
after_unknown为 true 的资源数,超过阈值则提示分批 apply。
grep 直接匹配 plan 文本输出做风险判断,文本格式会随 Terraform 版本变化;应基于 terraform show -json 的结构化字段编写策略。五、把 plan 审查变成可执行的检查清单
结合上述分析,可以在 CI 流水线中固化一份 plan 审查清单,作为 apply 前的强制门禁:
- 执行
terraform plan -out=tfplan并转 JSON,归档为构建产物。 - 统计
resource_changes中 actions 含delete的资源,若涉及数据库、存储桶、IAM 角色,标记为阻断项,需人工审批。 - 检查所有 replace 动作是否配置了
create_before_destroy,未配置的标记为高风险。 - 扫描
after中新增的0.0.0.0/0、"*"权限,标记为安全审查项。 - 统计
after_unknown为 true 的资源比例,超过 30% 时建议拆分 apply 批次。 - 敏感值不落日志,plan.json 上传至受控存储,审查者按需解密查看。
这套流程的价值在于:把原本依赖个人经验的“看 plan”变成可重复、可审计、可自动阻断的工程动作。在大规模变更中,plan 不是一次性的输出,而是风险控制的核心数据源。