3. 漂移检测的核心命令与输出解读
在 Terraform 的日常运维中,配置漂移(Configuration Drift)指的是真实基础设施的状态与 Terraform 状态文件(terraform.tfstate)中记录的状态不一致。这种不一致可能来自控制台手工修改、其他自动化工具的操作、云厂商侧的自动变更,或者资源被意外删除。要发现漂移,核心手段就是围绕 terraform plan、terraform refresh、terraform show 这三条命令构建检测链路。理解它们各自读取什么、输出什么、如何配合,是漂移治理的基本功。
三条命令的定位与数据流
先厘清三者的职责边界。terraform refresh 的职责是"对齐状态":它读取配置(.tf 文件)和当前状态文件,调用云 API 查询每个资源的真实属性,然后把真实值写回状态文件。它不修改真实资源,只修改状态记录。terraform plan 的职责是"计算差异":它先隐式执行一次刷新(除非加 -refresh=false),再把刷新后的状态与配置期望值对比,输出一份变更计划。terraform show 的职责是"呈现内容":它把状态文件或计划文件以人类可读的形式打印出来,本身不做任何计算。
三者的数据流关系可以用下图表示:配置与真实资源是两端,状态文件是中间记录,refresh 负责把真实值同步进状态,plan 负责把状态与配置对比出差异,show 负责把状态或计划渲染成可读文本。
terraform plan:漂移检测的主入口
日常检测漂移,最常用的就是 terraform plan。它默认会先刷新状态,再计算差异。如果真实资源被手工改过,刷新后的状态就会带上真实值,plan 对比配置后就会显示出"要把资源改回配置声明值"的计划。这正是漂移的典型信号。
执行时建议加上 -out 参数把计划保存下来,便于后续用 show 查看和审阅:
terraform plan -out=drift.tfplan
# 输出片段示例(假设某安全组被手工放开了 8080 端口)
Terraform will perform the following actions:
# aws_security_group.web will be updated in-place
~ resource "aws_security_group" "web" {
id = "sg-0a1b2c3d4e5f67890"
name = "web-sg"
~ ingress = [
- {
- from_port = 8080
- to_port = 8080
- protocol = "tcp"
- cidr_blocks = ["0.0.0.0/0"]
},
# (原有 80/443 规则保持不变)
]
}
Plan: 0 to add, 1 to change, 0 to destroy.
解读这份输出,关键看三点。第一,看动作符号:~ 表示原地修改(in-place update),- 表示删除,+ 表示新建,-/+ 表示先删后建(replace)。第二,看资源地址:aws_security_group.web 告诉你漂移发生在哪个资源。第三,看属性差异:- 开头的行是"当前真实状态里有、但配置里没有"的内容,也就是被手工加进去的 8080 规则。plan 的意图是把它移除,使资源回到配置声明的样子。
如果计划里出现大量 -/+ 替换,且涉及有状态资源(如数据库、磁盘),要格外警惕——这可能是漂移触发了不可逆的重建。此时不要直接 apply,应先确认漂移来源。
terraform refresh:只对齐状态,不动资源
terraform refresh 是一个容易被误解的命令。它不会把真实资源改成配置的样子,它只是把真实资源的当前属性读回来,覆盖状态文件里的旧记录。换句话说,它让"状态文件"追上"真实世界",而不是让"真实世界"追上"配置"。
它的典型用途有两个。一是单独查看真实状态:先 refresh,再 show,就能看到资源当前的真实属性,而不触发任何变更计划。二是在 plan 之前手动对齐状态,避免 plan 基于过期状态做出误判。需要注意的是,在较新的 Terraform 版本中,terraform refresh 已被标记为不推荐单独使用,官方建议用 terraform plan -refresh-only 或 terraform apply -refresh-only 来替代,因为后者会明确告诉你"状态将被更新成什么",更安全可控。
# 传统方式:直接刷新状态
terraform refresh
# 推荐方式:先看刷新会带来什么变化,再决定是否应用
terraform plan -refresh-only
terraform apply -refresh-only
-refresh-only 模式的输出只包含状态更新,不会包含任何资源创建、修改或删除。如果输出里出现了属性变化,说明真实资源确实与状态记录不一致,这就是漂移的直接证据。
terraform show:把状态和计划读出来
terraform show 本身不检测漂移,但它是解读漂移的放大镜。它有两种主要用法:读取状态文件,或读取 plan 生成的计划文件。
# 查看当前状态中所有资源的属性
terraform show
# 查看之前保存的计划文件内容
terraform show drift.tfplan
# 以 JSON 格式输出,便于脚本解析和比对
terraform show -json drift.tfplan
当你怀疑漂移但 plan 输出太长时,可以用 terraform show -json 把计划导出为 JSON,再用 jq 之类的工具筛选出 resource_changes 中 actions 不为 ["no-op"] 的条目。这样能快速定位到底哪些资源发生了变更,而不必在成百上千行文本里翻找。
terraform show -json drift.tfplan | \
jq '.resource_changes[] | select(.change.actions != ["no-op"]) | {address, actions: .change.actions}'
输出中的变更信号速查
把 plan 输出里的符号和含义对应清楚,是快速判断漂移性质的关键。下表整理了常见信号及其解读方向。
| 符号 | 含义 | 漂移解读 |
|---|---|---|
~ |
原地修改 | 属性被手工改动,plan 要改回配置值 |
- |
删除 | 配置中已移除但真实资源仍存在,或资源被标记为待销毁 |
+ |
新建 | 配置中声明了但状态里没有,可能是资源被手工删除 |
-/+ |
先删后建 | 不可变属性被改动,触发替换,需重点确认 |
<= |
仅读取数据源 | 数据源刷新,通常不涉及资源漂移 |
terraform plan -detailed-exitcode。该参数会让 plan 在"有变更"时返回退出码 2,"无变更"时返回 0,"出错"时返回 1。这样就能用退出码自动判断是否存在漂移,无需人工阅读输出。
terraform apply 来"顺便"检测漂移。apply 会真实修改基础设施,如果漂移判断有误,可能造成生产事故。检测阶段应始终停留在 plan / refresh / show,确认清楚后再决定是否 apply。
总结来说,terraform plan 是发现漂移的主入口,terraform refresh(或 -refresh-only)负责把真实值同步进状态,terraform show 负责把状态和计划渲染成可读、可解析的形式。三者配合,就能形成一条从"发现差异"到"确认差异"再到"定位差异"的完整检测链路。真正重要的是养成先看 plan、再动手的习惯,让每一次变更都有据可依。