Terraform 是一个声明式工具。然而,’何时 apply’ 是一个比想象中复杂得多的问题。
🎯 本文涵盖内容
- terraform apply 的确切工作原理
- 在迁移场景下 apply 的使用方法
- 漂移(Drift)发生时 apply 的作用和注意事项
- 两种情况的区别和共同原则
- 容易出错的模式和推荐的工作流程
📌 引言: “不就是直接 apply 吗?”
初学 Terraform 时,每个人都会这么想。编写代码,查看 plan,然后 apply — 简单。但在实际运营环境中工作时,就会产生疑问。
“我在控制台直接添加了一条安全组规则… 还能 apply 吗?”
“我想用 Terraform 管理现有基础设施,该从何开始?”
这两个问题正是我们今天要讨论的核心。迁移和漂移修正乍一看相似,但性质完全不同,各自有适合的 apply 策略。

🔍 首先需要了解:Terraform 的状态(State)
Terraform 将基础设施的当前状态记录在 .tfstate 文件中。执行 apply 时,它会比较以下三项来决定操作:
| 比较 | 对象说明 |
| 代码(HCL) | 我期望的状态 (desired state) |
| State 文件 | Terraform 已知的状态 (known state) |
| 实际基础设施 | 云中实际存在的状态 (actual state) |
如果这三者一致,apply 将不执行任何操作。当出现不一致时,就需要 apply。
🧳 案例 1: 迁移 — 将现有基础设施导入 Terraform
何时属于此案例?
- 已存在通过控制台或 CLI 手动创建的基础设施
- 现在想用 Terraform 将其代码化(IaC化)
- State 文件中没有该资源
错误方法:编写代码后立即 apply
# ❌ 危险方法
terraform apply
如果为已存在的 S3 存储桶、EC2 实例编写代码后直接 apply,Terraform 会尝试创建新的。如果已存在同名资源,会报错;如果运气不好,现有资源可能会被删除后重新创建,导致事故发生。
正确方法:先 terraform import
# ✅ 正确方法: 先用 import 注册到 State
terraform import aws_s3_bucket.my_bucket my-existing-bucket-name
import 命令将实际基础设施资源注册到 State 文件中。之后运行 plan 检查代码和 State 是否一致,只有在没有差异时才能安全地进行管理。
Terraform 1.5+ : 使用 import 块进行声明式迁移
从 Terraform 1.5 开始,可以直接在 HCL 代码中编写 import 块。
# import.tf
import {
to = aws_s3_bucket.my_bucket
id = "my-existing-bucket-name"
}
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-existing-bucket-name"
# ... 其他属性
}
# 用 plan 提前确认 import 结果
terraform plan
# 如果没有问题则 apply
terraform apply
在这种方式下,apply 会一次性处理导入 + 代码同步。迁移完成后,import 块可以删除。
迁移中 apply 的推荐时机总结
- ✅ terraform import 完成 State 注册后
- ✅ terraform plan 显示 No changes 或仅显示预期变更时
- ✅ 使用 import 块方式,审查 plan 结果后
🔄 案例 2: 漂移(Drift) — State 与实际基础设施不一致时
什么是漂移?
配置漂移(Configuration Drift)是指 Terraform 管理的资源在代码或 State 之外被更改时发生的情况。
常见原因:
- 👤 有人直接在控制台修改了安全组规则
- 🔧 其他团队成员通过 AWS CLI 更改了标签
- ☁️ 云服务自动更新属性 (例如:EKS 节点组)
현재 상태:
코드(HCL) → 포트 443만 허용
State 파일 → 포트 443만 허용 (apply 시점 기록)
실제 AWS → 포트 443 + 8080 허용 ← 누군가 콘솔에서 추가!
在这种状态下运行 terraform plan 会怎样?
terraform plan
# ~ aws_security_group_rule.allow_https will be updated in-place
# - from_port = 8080 (待删除)
Terraform 将根据代码(HCL)尝试删除 8080 规则。
apply 前必须做的事:检测漂移
# 将 State 与实际基础设施同步以检测漂移
terraform refresh
# 或与 plan 一起
terraform plan -refresh-only
-refresh-only 标志只将实际基础设施状态反映到 State 中,而不更改基础设施。这对于了解漂移发生在哪里以及如何发生非常有帮助。
漂移处理策略:两种选择
策略 A: 更新代码以符合实际 (接受漂移)
如果控制台中添加的更改是故意的,则更新代码并 apply。
# 在代码中添加 8080 规则
resource "aws_security_group_rule" "allow_8080" {
type = "ingress"
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
security_group_id = aws_security_group.main.id
}
terraform plan # 确认
terraform apply # 使代码与实际状态一致
策略 B: 将基础设施恢复到代码基准 (消除漂移)
如果是未经授权的更改,则通过 apply 恢复到基于代码的状态。
# 不修改代码直接 apply
terraform apply
# → 8080 规则被移除并恢复到代码状态
漂移场景中 apply 的推荐时机总结
- ✅ 使用 terraform plan -refresh-only 了解漂移范围后
- ✅ 判断漂移是故意的还是未经授权的更改后
- ✅ 决定采用策略 A (更新代码) 或策略 B (恢复原状) 后
- ❌ 不了解漂移原因就盲目 apply 是不可取的
⚠️ 注意事项: apply 前务必检查的事项
1. 没有 plan 的 apply 是事故的捷径
# ❌ 绝对禁止模式
terraform apply -auto-approve
即使在 CI/CD 自动化中,也必须包含人工审查 plan 结果的步骤。
2. 绝不能手动编辑 State 文件
如果发生漂移,直接修改 .tfstate 文件会导致更大的问题。务必使用 terraform state 命令。
# 从 State 中移除特定资源 (保留基础设施)
terraform state rm aws_s3_bucket.old_bucket
# 在 State 中查看资源列表
terraform state list
3. 使用远程 State 是必需而非可选
在团队环境中,使用本地 State 会使漂移和冲突成为常态。
# 使用 S3 + DynamoDB 配置远程 State
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
dynamodb_table = "terraform-lock"
encrypt = true
}
}
4. 在生产环境中尽量减少使用 target 标志
# ⚠️ 除非紧急情况,否则避免使用
terraform apply -target=aws_instance.web
只选择特定资源进行 apply 可能会导致依赖关系混乱。
✅ 总结: 迁移 vs 漂移,有何不同?
区分 迁移 漂移修正
| 区分 | 迁移 | 漂移修正 |
| 情况 | 将现有基础设施纳入 Terraform 管理 | 管理中的基础设施在外部被更改 |
| State 状态 | 该资源没有 State | State 与实际基础设施不一致 |
| apply 前操作 | terraform import 或 import 块 | terraform plan -refresh-only |
| apply 目的 | 同步代码和 State | 恢复到代码基准或更新代码后同步 |
| 风险等级 | 高 (可能误删/重建) | 中 (可能应用非预期更改) |
结论:两者都使用 apply。但流程不同。
terraform apply 只是一个工具。无论是迁移还是漂移修正,核心都是在 apply 之前查看 plan,并在理解将发生何种更改的情况下执行。Terraform 的真正威力并非来自 apply 命令的一行,而是来自之前执行的 plan 和 refresh。

发表回复