解决terraform与远程云服务器资源状态不一致的问题

解决terraform与远程云服务器资源状态不一致的问题

July 22, 2020

概述

在运维过程中遇到了下面的情况:长期使用terraform管理资源,但是通过terraform以外的工具对云资源做过属性变更(比如通过网页上的控制台对云资源做属性变更)。此时,云资源的属性与terraform state的信息已经不一致,跑terraform plan就会发现,它会删除现有的,然后重建一个。但是这个是我们不希望看到的,我们希望可以保留原有实例并且把本地的terraform state更新成最新的属性。

解决方案

要想解决这个问题,可以利用 terraform import 命令。也就是重新导入云资源的实例,然后同步成最新的信息。

下面以阿里云ECS为例子。

(1)删除 state 中的现有资源

如果该资源受 terraform 管理,则需要先把它删除。执行下面的命令

# 基于当前的tf文件,初始化 state 文件
terraform init

# 列出当前的state文件所有管理的项目清单
terraform state list

# 根据上面的输出,找到对应的项目的名称。通过rm删除, 下面假设项目名称是 alicloud_instance.testecs
terraform state rm alicloud_instance.testecs

(2)在阿里云控制台获取实例ID

登录阿里云网页上的控制台,获取所要导入的实例 ID

(3)tf 文件中添加实例的定义

保证本地的tf文件中有该实例资源的相关定义。如果没有,则可以用下面的方式在文件中新增内容。

如果是以resource模式定义,则添加下面内容,直接留空即可,其余的数据在后续步骤中可以补全

resource "alicloud_instance" "testecs" {}

如果是以模块方式来定义,则添加下面内容,其余的数据在后续步骤中可以补全

module "testmodule" {
  source           = "../modules/instance"
  environment      = "xxx"
  vswitch_info     = "xxxx"
  vpc_id           = "xxxx"
  instance_type    = "xxxx"
  security_groups  = "xxxx"
  service          = "xxxx"
  instance_count   = "xxxxx"
  system_disk_size = "xxxxx"
  system_disk_category = "xxxxx"
  alicloud_availability_zones = "xxxxx"
}

(4)导入实例

如果是以resource模式定义,则直接用实例的资源路径

terraform import alicloud_instance.testecs i-xxxxxxxxxxxxxx

​ 如果是以模块方式来定义, 则使用包含模块的资源路径

terraform import module.testmodule.alicloud_instance.instance i-xxxxxxxxxxxxxx

​如果该服务有多个实例,则要指定下标。注意:下标部分要用引号,否则报错

terraform import module.testlistmodule.alicloud_instance."instance[0]" i-xxxxxxxxxxxxxx

(5)补齐资源模板定义

在确定清楚参数属性的具体值之后,如果以模板参数值为准,那么只需要运行apply命令再变更回来即可;如果以控制台的值为准,那么只需要补充或修改模板参数值即可。

由于模板中没有完成对所导入资源的详细定义,因此,资源导入成功后,模板内容与State存储的内容存在差异,此时如果直接运行plan命令,将会看到一个update。为了保持资源模板与资源状态的一致,需要在模板中手动补齐缺失的参数定义,直到运行plan不会再有变更信息为止

后记

阿里云的文档中提到了另外三种场景,应该也可以用类似的方式来处理:

  • 长期使用控制台、阿里云CLI、资源编排服务或者直接调用API创建和管理资源,初次使用Terraform的场景。
  • 所有资源都定义在一个模板中,想要对原有模板进行重构拆分,以降低随着资源不断增多而带来的模板和state的管理复杂度的场景。
  • 想要将新版Provider中新增的参数同步到原文档中的场景。

参考链接:

最后更新于