跳到主要内容

导入与漂移

本章目标:把已经在云上存在、但没被 Terraform 管理的资源「收编」进 state(import);并处理「有人手动改了云上资源」造成的漂移(drift)。这两件事是真实运维里绕不开的。

完成后你应能安全地把存量资源纳入管理,并决定「改回去」还是「接受现状」。


1. 为什么需要 import​

现实里大量资源是「先有云、后补 IaC」的:

  • 早期手动建的资源,现在想用 Terraform 统一管理。
  • 别的团队/工具建的,你想接管。
  • 灾难恢复后,云上资源还在,但 state 丢了,需要重新建立映射。

import 的本质:告诉 Terraform「这个资源地址,对应云上那个真实对象」。传统 terraform import 会立即把映射写进 state;声明式 import 块则在 apply 时写入。两种方式都不会自动产出完整的生产配置,配置仍要你审阅并维护。


2. 方式一:terraform import(传统)​

先写好资源配置,再执行 import 把云上对象绑定到该地址:

# 先写配置
resource "aws_instance" "web" {
# 必填字段仍需满足 provider schema;值应尽量与云上当前配置一致
ami = "ami-0abcd1234"
instance_type = "t3.small"
}
terraform import aws_instance.web i-0abc123def456
# 成功后再 terraform plan,会提示配置与真实状态的差异,逐步补齐

注意点:

  • import 只写 state,不写代码。import 后必须跑 plan,根据差异把配置补全到和真实一致,否则下次 apply 会去「修正」它。
  • 需要目标资源的 ID(不同资源 ID 形式不同:i-xxx、ARN、名称等),从云控制台或 CLI 查。
  • 一批资源要逐个 import,繁琐。

3. 方式二:import 块(Terraform 1.5+,推荐)​

声明式、可进版本控制、支持 for_each 批量:

import {
to = aws_instance.web
id = "i-0abc123def456"
}

resource "aws_instance" "web" {
# 正常写完整配置
ami = "ami-0abcd1234"
instance_type = "t3.small"
tags = {
Name = "web-01"
}
}

流程:

terraform plan # 检测到 import 块,读取云上对象并预览导入与配置差异;不写 state
# 根据 plan 差异补全 resource 配置
terraform apply # 写入 state,完成导入

若还没有资源配置,可先让 Terraform 生成初稿,再人工收敛:

# Terraform 1.5+:基于 import 块生成资源配置初稿
terraform plan -generate-config-out=generated.tf
# 逐项审阅 generated.tf,整理到模块文件后再提交和 apply

生成的配置只用于加速盘点,不能跳过 plan 评审直接进入生产。

导入后删掉 import 块即可(它只在导入时起作用,留着无害但冗余)。

import 块更适合存量纳管

把 import 块和 resource 放一起提交,相当于「声明我要接管这个已有资源」,可读、可评审、可重复,比一堆 terraform import 命令强得多。


4. 漂移(drift)是什么​

漂移 = 云上真实状态 与 state 记录不一致。常见原因:

  • 有人直接在控制台手改了配置。
  • 别的脚本/工具越权修改。
  • 云厂商侧自动行为(如某些托管服务的后台调整)。

Terraform 默认在 plan 时先 refresh,所以漂移会在 plan 里暴露出来——但那是「准备改之前才发现」,偏被动。


5. 主动检测漂移:-refresh-only​

terraform plan -refresh-only

它只刷新 state、不改云,输出「真实 vs state 的差异」:

# aws_security_group.web has been changed
~ resource "aws_security_group" "web" {
~ ingress = [ # 有人手动加了一条规则
+ { ... } # 控制台手加的
]
}

适合定时任务(每日)跑,发现漂移就告警/派单。

terraform refresh 已不推荐

旧版 terraform refresh 直接改 state,容易误覆盖。1.5+ 统一用 plan -refresh-only,更安全、可审阅。


6. 漂移怎么处理:改回去 or 接受​

选择做法适用
改回代码口径在 plan 里 apply,Terraform 把云上拉回代码定义手动改动是「误改/越权」,应以代码为准
接受现状把真实值同步进配置,使其与代码一致手动改是「合理临时调整」,应固化进 IaC

6.1 用 lifecycle 接受某些字段的偏离​

有些字段天然会被外部改动(如 ASG 当前实例数、某些托管服务的后台标签),不想每次 plan 都报噪,可显式忽略:

resource "aws_security_group" "web" {
# ...
lifecycle {
ignore_changes = [
ingress, # 接受入站规则被外部调整
tags["LastModified"],
]
}
}
ignore_changes 是把双刃剑

它能降噪,但也会掩盖真实漂移。只忽略「明确由外部系统负责」的字段,别用它掩盖自己代码没覆盖的变更。

6.2 停止管理但保留资源:terraform state rm​

terraform state rm aws_instance.legacy

把资源从 state 移除——云上资源不会被删除,只是 Terraform 不再管它。用于「这个资源交给别的团队/工具了」的场景。之后再导入或手工处置都可。


7. 与手工变更共存的整体策略​

  1. 新资源一律走 Terraform,杜绝「手动建完再 import」成为常态。
  2. 关键资源加 prevent_destroy(见第 9 章),防止 apply 误删。
  3. 定时 plan -refresh-only 做漂移巡检,把「代码 vs 真实」的偏差变成可观察、可评审的事件。
  4. import 块纳管存量,别用一堆临时命令。
  5. 漂移处置要留痕:无论改回还是接受,都在 PR/工单里说明,保持代码与真实的因果可追溯。

8. 练习与验收​

练习 A

  1. 手动在控制台建一个安全组(不入 Terraform)
  2. 写 import 块把它导入,跑 plan 观察差异,apply 完成接管
  3. 导入成功后删掉 import 块,再 plan 应无变更

练习 B

  1. 导入后,去控制台手动加一条入站规则
  2. 跑 terraform plan -refresh-only,确认 drift 被检出
  3. 用 lifecycle { ignore_changes = [ingress] } 接受该字段,再 plan 应不再报

验收清单

  • 能用 terraform import 与 import 块两种方式纳管存量资源
  • 理解 import 只写 state、不写代码,需补配置
  • 会用 plan -refresh-only 主动检测漂移
  • 会用 ignore_changes 与 state rm 处理「接受/退出管理」
  • 建立了「新资源走 IaC、漂移可巡检」的整体意识

下一章:生产协作 —— 收口权限、密钥、破坏性变更的护栏,把 Terraform 真正放进团队协作的生产环境。