4. 手动导入单个资源:通过 terraform import 导入 AWS EC2 实例

在 Terraform 工作流中,资源导入(Import) 是将已有云上资源纳入 Terraform 状态管理的核心操作。本章以 AWS EC2 实例为例,演示手动导入单个资源的完整流程:从执行导入命令、验证状态文件,到反向生成配置文件,最终实现基础设施即代码(IaC)的闭环管理。

4.1 导入前置条件与准备

在执行导入操作前,需确保以下条件已满足:

前置条件 说明
Terraform 已安装 版本 ≥ 1.5(推荐使用 1.5+ 以支持 import block 和 plan -generate-config-out)
AWS Provider 已配置 在 provider.tf 中声明 AWS Provider,并配置凭证(环境变量或共享凭证文件)
目标 EC2 实例 ID 从 AWS 控制台或 CLI 获取,格式如 i-0abcd1234efgh5678
空资源块占位 在 .tf 文件中预先定义一个空的 aws_instance 资源块,用于接收导入的状态
最佳实践: 建议在导入前先执行 terraform init 确保 Provider 插件已下载,并执行 terraform plan 确认当前工作目录无未预期的变更。

4.2 定义空资源占位块

在 ec2.tf 文件中创建一个空的 aws_instance 资源块,仅指定资源类型和本地名称,不填写任何配置参数:

# ec2.tf - 空资源占位块
resource "aws_instance" "imported_web" {
  # 此块仅用于接收导入的状态
  # 配置将在后续步骤中生成
}

此时执行 terraform plan 会提示该资源未配置任何参数,但不会报错——Terraform 允许空资源块存在,仅会在 apply 时因缺少必要参数而失败。

4.3 执行 terraform import 命令

使用 terraform import 命令将目标 EC2 实例的状态导入到本地状态文件中。命令格式为:

terraform import [options] <ADDRESS> <ID>

其中:

  • ADDRESS:资源在 Terraform 配置中的地址,格式为 资源类型.资源名称,如 aws_instance.imported_web
  • ID:云服务商侧的资源标识符,对于 EC2 实例即为实例 ID

实际执行命令:

terraform import aws_instance.imported_web i-0abcd1234efgh5678

执行成功后,Terraform 会输出类似以下信息:

aws_instance.imported_web: Importing from ID "i-0abcd1234efgh5678"...
aws_instance.imported_web: Import prepared!
  Prepared aws_instance for import
aws_instance.imported_web: Refreshing state... [id=i-0abcd1234efgh5678]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be modified by Terraform.

4.4 验证导入结果

导入成功后,通过以下两种方式验证状态是否正确写入:

4.4.1 查看状态文件

terraform state list

输出应包含刚导入的资源地址:

aws_instance.imported_web

4.4.2 查看资源详情

terraform state show aws_instance.imported_web

输出将显示该 EC2 实例的完整属性列表,包括实例类型、AMI ID、子网 ID、安全组、标签等所有由 AWS 返回的字段。示例输出片段:

# aws_instance.imported_web:
resource "aws_instance" "imported_web" {
    ami                          = "ami-0c55b159cbfafe1f0"
    arn                          = "arn:aws:ec2:us-east-1:123456789012:instance/i-0abcd1234efgh5678"
    associate_public_ip_address  = true
    availability_zone            = "us-east-1a"
    cpu_core_count               = 1
    cpu_threads_per_core         = 1
    disable_api_termination      = false
    ebs_optimized                = false
    get_password_data            = false
    hibernation                  = false
    id                           = "i-0abcd1234efgh5678"
    instance_state               = "running"
    instance_type                = "t2.micro"
    private_ip                   = "10.0.1.10"
    public_ip                    = "54.123.45.67"
    subnet_id                    = "subnet-0a1b2c3d4e5f67890"
    tags                         = {
        "Name" = "web-server"
    }
    tenancy                      = "default"
    vpc_security_group_ids       = [
        "sg-0a1b2c3d4e5f67890",
    ]
    # ... 更多属性省略
}
关键验证点: 确认 id 字段与导入时使用的实例 ID 一致,且 instance_state 为 running,表明状态已成功关联。

4.5 更新配置文件:从状态生成配置

导入状态后,.tf 文件中的资源块仍然是空的。Terraform 1.5+ 提供了 plan -generate-config-out 功能,可以从状态中反向生成配置文件内容。

4.5.1 生成配置文件

terraform plan -generate-config-out=generated.tf

执行后,Terraform 会分析当前状态,并在 generated.tf 文件中生成 aws_instance.imported_web 的完整配置。生成的配置文件示例:

# generated.tf
# __generated__ by Terraform
# Please review and update as needed.

resource "aws_instance" "imported_web" {
  ami                         = "ami-0c55b159cbfafe1f0"
  associate_public_ip_address = true
  availability_zone           = "us-east-1a"
  instance_type               = "t2.micro"
  subnet_id                   = "subnet-0a1b2c3d4e5f67890"
  tags = {
    Name = "web-server"
  }
  vpc_security_group_ids = [
    "sg-0a1b2c3d4e5f67890",
  ]

  # 以下为可选或计算属性,可根据需要保留或删除
  # cpu_core_count       = 1
  # cpu_threads_per_core = 1
  # ebs_optimized        = false
  # ... 更多注释掉的属性
}

4.5.2 合并与清理

将生成的配置内容复制到原始的 ec2.tf 文件中,替换空资源块。建议按以下步骤清理:

  1. 删除注释掉的属性:保留必要参数,删除 # __generated__ 标记和注释掉的计算属性。
  2. 调整参数值:将硬编码的值(如 subnet_id)改为变量引用,提高配置的可复用性。
  3. 执行 terraform plan:验证配置与状态是否一致,预期输出应为 No changes. Your infrastructure matches the configuration.
注意: 生成的配置可能包含 availability_zone 等与 Provider 默认行为冲突的参数。如果计划使用 subnet_id 隐式推导可用区,建议删除 availability_zone 参数以避免冲突。

4.6 导入流程总览

terraform import 手动导入 EC2 实例流程 1. 准备空资源块 2. terraform import 3. 验证状态文件 4. 生成配置文件 5. terraform plan 确认 关键命令 terraform import terraform state list terraform state show 生成配置命令 terraform plan -generate-config-out=gen.tf

4.7 常见问题与避坑指南

问题场景 原因 解决方案
导入后 plan 显示要删除资源 空资源块缺少必要参数,Terraform 认为需要重建 使用 -generate-config-out 生成完整配置,或手动补充 ami、instance_type 等必要参数
导入时报错 "Resource already managed by Terraform" 该实例 ID 已存在于当前状态文件中 执行 terraform state rm aws_instance.imported_web 移除旧状态后重新导入
生成的配置包含过多计算属性 Terraform 默认导出所有可读属性 删除注释掉的属性,仅保留必要参数;使用 ignore_changes 忽略非托管属性
导入后 plan 显示属性漂移 云上资源与生成的配置存在细微差异(如时间戳、默认值) 执行 terraform apply -refresh-only 同步状态,或手动调整配置匹配实际值
生产建议: 对于批量导入场景(如迁移整个 VPC 环境),建议使用 import block(Terraform 1.5+)配合 for_each 实现声明式导入,避免手动逐条执行 terraform import 命令。

4.8 本章小结

手动导入单个 EC2 实例是 Terraform 资源导入的基础操作,核心步骤可概括为:

  • 占位:在配置文件中创建空资源块
  • 导入:执行 terraform import 将云上状态写入本地
  • 验证:通过 state list 和 state show 确认状态完整性
  • 生成:使用 plan -generate-config-out 反向生成配置文件
  • 确认:执行 terraform plan 确保配置与状态一致

掌握这一流程后,即可将任意 AWS 资源纳入 Terraform 管理,为后续的批量导入和状态修复奠定基础。