1.7.18.2.1. mv
Terraform 状态的主要功能是记录下代码中的资源实例地址与其代表的远程对象之间的绑定。通常,Terraform 会自动更新状态以响应应用计划时采取的操作,例如删除已被删除的远程对象的绑定。
在修改了 resource
块名称,或是将资源移动到代码中的不同模块时,如果想保留现有的远程对象,可以使用 terraform state mv
命令。
1.7.18.2.1.1. 用法
terraform state mv [options] SOURCE DESTINATION
Terraform 将在当前状态中查找与给定地址匹配的资源实例、资源或模块,如果找到,则将原本由源地址跟踪的远程对象移动到目标地址下。
源地址和目标地址都必须使用资源地址语法,并且它们引用对象的类型必须相同:我们只能将一个资源实例移动到另一个资源实例,将整个模块实例移动到另一个整个模块实例,等等。此外,如果我们要移动资源或资源实例,则只能将其移动到具有相同资源类型的新地址。
terraform state mv
最常见的用途是当我们在代码中重命名 resource
块,或是将 resource
块移动到子模块中时,这两种情况都是为了保留现有对象但以新地址跟踪它。默认情况下,Terraform 会将移动或重命名资源配置理解为删除旧对象并在新地址创建新对象的请求,因此 terraform state mv
允许我们已经存在的对象附加到Terraform 中的新地址上。
警告:如果我们在多人协作环境中使用 Terraform,则必须确保当我们使用 terraform state mv
进行代码重构时,我们与同事进行了仔细沟通,以确保没有人在我们的配置更改和 terraform 状态之间进行任何其他更改mv 命令,因为否则他们可能会无意中创建一个计划,该计划将销毁旧对象并在新地址创建新对象。
该命令提供以下可选参数:
-dry-run
:报告与给定地址匹配的所有资源实例。-lock=false
:执行时是否先锁定状态文件。如果其他人可能同时对同一工作区运行命令,则这是危险的。-lock-timeout=DURATION
:除非使用-lock=false
禁用锁定,否则命令 Terraform 为上锁操作设置一个超时时长。持续时间语法是一个数字后跟一个时间单位字母,例如“3s”表示三秒。
以下是使用 local
Backend 时可用的遗留参数:
-backup=FILENAME
:指定源状态文件的备份地址,默认为源状态文件加上".backup"后缀-bakcup-out=FILENAME
:指定目标状态文件的备份地址,默认为目标状态文件加上".backup"后缀-state=FILENAME
:源状态文件地址,默认为当前 Backend 或是"terraform.tfstate"-state-out=FILENAME
:目标状态文件地址。如果不指定则使用源状态文件。可以是一个已经存在的文件或新建一个文件
1.7.18.2.1.2. 例子:重命名一个资源
$ terraform state mv 'packet_device.worker' 'packet_device.helper'
...
-resource "packet_device" "worker" {
+resource "packet_device" "helper" {
# ...
}
1.7.18.2.1.3. 例子:将一个资源移动进一个模块
如果我们最初在根模块中编写了资源,但现在希望将其重构进子模块,则可以将 resource
块移动到子模块代码中,删除根模块中的原始资源,然后运行以下命令告诉 Terraform 将其视为一次移动:
$ terraform state mv 'packet_device.worker' 'module.app.packet_device.worker'
在上面的示例中,新资源具有相同的名称,但模块地址不同。如果新的模块组织建议不同的命名方案,您还可以同时更改资源名称:
$ terraform state mv packet_device.worker module.worker.packet_device.main
1.7.18.2.1.4. 例子:移动一个模块进入另一个模块
我们还可以将整个模块重构为子模块。在配置中,将代表模块的 module
块移动到不同的模块中,然后使用如下命令将更改配对:
$ terraform state mv 'module.app' 'module.parent.module.app'
1.7.18.2.1.5. 例子:移动一个模块到另一个状态文件
$ terraform state mv -state-out=other.tfstate 'module.app' 'module.app'
1.7.18.2.1.6. 移动一个带有 count 参数的资源
使用 count
元参数定义的资源具有多个实例,每个实例都由一个整数标识。我们可以通过在给定地址中包含显式索引来选择特定实例:
$ terraform state mv 'packet_device.worker[0]' 'packet_device.helper[0]'
不使用 count
或 for_each
的资源只有一个资源实例,其地址与资源本身相同,因此我们可以从不包含索引的地址移动到包含索引的地址,或相反:
$ terraform state mv 'packet_device.main' 'packet_device.all[0]'
方括号 ([
, ]
) 在某些 shell 中具有特殊含义,因此您可能需要引用或转义地址,以便将其逐字传递给 Terraform。上面的示例显示了 Unix 风格 shell 的典型引用语法。
1.7.18.2.1.7. 移动一个带有 for_each 参数的资源
使用 for_each
元参数定义的资源具有多个实例,每个实例都由一个字符串标识。我们可以通过在给定地址中包含显式的键来选择特定实例。
但是,字符串的语法包含引号,并且引号符号通常在命令 shell 中具有特殊含义,因此我们需要为正在使用的 shell 使用适当的引用和/或转义语法。例如:
Linux、MacOS 以及 Unix:
$ terraform state mv 'packet_device.worker["example123"]' 'packet_device.helper["example456"]'
PowerShell:
$ terraform state mv 'packet_device.worker[\"example123\"]' 'packet_device.helper[\"example456\"]'
Windows 命令行(cmd.exe
):
$ terraform state mv packet_device.worker[\"example123\"] packet_device.helper[\"example456\"]
除了使用字符串而不是整数作为实例键之外,for_each
资源的处理与 count
资源类似,因此具有和不具有索引组件的相同地址组合都是有效的,如上一节所述。