Terraform-命令行2
1.7.16.1. refresh
terraform refresh
命令将实际存在的基础设施对象的状态同步到状态文件中记录的对象状态。它可以用来检测真实状态与记录状态之间的漂移并更新状态文件。
警告!!!该命令已在最新版本 Terraform 中被废弃,因为该命令的默认行为在当前用户错误配置了使用的云平台令牌时会引发对状态文件错误的变更。
该命令并不会修改基础设施对象,只修改状态文件。
我们一般不需要使用该命令,因为 Terraform 会自动执行相同的刷新操作,作为在 terraform plan
和 terraform apply
命令中创建计划的一部分。本命令在这里主要是为了向后兼容,但我们不建议使用它,因为它没有提供在更新状态之前检查操作效果的机会。
1.7.16.1.1. 用法
terraform refresh [options]
该命令本质上是以下命令的别名,具有完全相同的效果:
1 | terraform apply -refresh-only -auto-approve |
因此,该命令支持所有 terraform apply
所支持的参数,除了它不接受一个现存的变更计划文件,不允许选择 “refresh only” 之外的模式,并且始终应用 -auto-approve
选项。
自动执行 refresh
是很危险的,因为如果当前用户错误配置了使用的 Provider 的令牌,那么 Terraform 会错误地以为当前状态文件中记录的所有资源都被删除了,随即从状态文件中无预警地删除所有相关记录。
我们推荐运行如下命令来取得相同的效果,同时可以在修改状态文件之前预览即将对其作出的修改:
1 | terraform apply -refresh-only |
该命令将会在交互界面中提示用户检测到的变更,并提示用户确认执行。
terraform apply
和 terraform plan
命令的 -refresh-only
选项是从 Terraform v0.15.4 版本开始被引入的。对更早的版本,用户只能直接使用 terraform refresh
命令,同时要小心本篇警告过的危险副作用。尽可能避免显式使用 terraform refresh
命令,Terraform 在执行 terraform plan
和 terraform apply
命令时都会自动执行刷新状态的操作以生成变更计划,尽可能依赖该机制来维持状态文件的同步。
show
1.7.17.1. show
terraform show
命令从状态文件或是变更计划文件中打印人类可读的输出信息。这可以用来检查变更计划以确定所有操作都是符合预期的,或是审查当前的状态文件。
可以通过添加 -json
参数输出机器可读的 JSON 格式输出。
需要注意的是,使用 -json
输出时所有标记为 sensitive
的敏感数据都会以明文形式被输出。
1.7.17.1.1. JSON 输出
可以使用 terraform show -json
命令打印 JSON 格式的状态信息。
如果指定了一个变更计划文件,terraform show -json
会以 JSON 格式记录变更计划、配置以及当前状态。
如果在写入状态文件后更新了包含新架构版本的 Provider 程序,则需要先升级状态,然后才能使用 show -json
显示状态。如果要查看计划,必须先在不使用 -refresh=false
的情况下创建计划文件。如果要查看当前状态,请先运行 terraform refresh
。
1.7.17.1.2. 用法
terraform show [options] [file]
您可以将为 file
指定状态文件或计划文件的路径。如果不指定文件路径,Terraform 将显示最新的状态快照。
该命令支持以下参数:
-json
:以 JSON 格式输出-no-color
:与apply
类似,不再赘述
state
1.7.18.1. state
terraform state
命令可以用来进行复杂的状态管理操作。随着你对 Terraform 的使用越来越深入,有时候你需要对状态文件进行一些修改。由于我们在状态管理章节中提到过的,状态文件的格式属于 HashiCorp 未公开的私有格式,所以直接修改状态文件是不适合的,我们可以使用 terraform state
命令来执行修改。
该命令含有数个子命令,我们会一一介绍。
1.7.18.1.1. 用法
terraform state <subcommand> [options] [args]
1.7.18.1.2. 远程状态
所有的 state
子命令都可以搭配本地状态文件以及远程状态使用。使用远程状态时读写操作可能用时稍长,因为读写都要通过网络完成。备份文件仍然会被写入本地磁盘。
1.7.18.1.3. 备份
所有会修改状态文件的 terraform state
子命令都会生成备份文件。可以通过 -backup
参数指定备份文件的位置。
只读子命令(例如 list
)由于不会修改状态,所以不会生成备份文件。
注意修改状态的 state
子命令无法禁用备份。由于状态文件的敏感性,Terraform 强制所有修改状态的子命令都必须生成备份文件。如果你不想保存备份,可以手动删除。
1.7.18.1.4. 命令行友好
state
子命令的输出以及命令结构都被设计得易于同 Unix 下其他命令行工具搭配使用,例如 grep、awk 等等。同样的,输出结果也可以在 Windows 上轻松使用 PowerShell 处理。
对于复杂场景,我们建议使用管道组合 state
子命令与其他命令行工具一同使用。
1.7.18.1.5. 资源地址
state
子命令中大量使用了资源地址,我们在资源地址章节中做了相关的介绍。
list
1.7.18.1.1. list
terraform state list
命令可以列出状态文件中记录的资源对象。
1.7.18.1.1.1. 用法
terraform state list [options] [address...]
该命令会根据 address 列出状态文件中相关资源的信息(如果给定了 address 的话)。如果没有给定 address,那么所有资源都会被列出。
列出的资源根据模块深度以及字典序进行排序,这意味着根模块的资源在前,越深的子模块定义的资源越在后。
对于复杂的基础设施,状态文件可能包含成千上万到的资源对象。可以指定一个或多个资源地址来进行过滤。
可以使用的可选参数有:
-state=path
:指定使用的状态文件地址。默认为terraform.tfstate
。使用远程 Backend 时该参数设置无效-id=id
:要显示的资源 ID
1.7.18.1.1.2. 例子:列出所有资源
1 | $ terraform state list |
1.7.18.1.1.3. 例子:根据资源地址过滤
1 | $ terraform state list aws_instance.bar |
1.7.18.1.1.4. 例子:根据模块过滤
该例子列出给定模块及其子模块的所有资源:
1 | $ terraform state list module.elb |
1.7.18.1.1.5. 例子:根据ID过滤
此示例将仅列出在命令行中指定 ID 的资源,查找特定资源在代码中的位置时非常有用:
1 | $ terraform state list -id=sg-1234abcd |
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. 例子:重命名一个资源
1 | $ terraform state mv 'packet_device.worker' 'packet_device.helper' |
1.7.18.2.1.3. 例子:将一个资源移动进一个模块
如果我们最初在根模块中编写了资源,但现在希望将其重构进子模块,则可以将 resource
块移动到子模块代码中,删除根模块中的原始资源,然后运行以下命令告诉 Terraform 将其视为一次移动:
1 | $ terraform state mv 'packet_device.worker' 'module.app.packet_device.worker' |
在上面的示例中,新资源具有相同的名称,但模块地址不同。如果新的模块组织建议不同的命名方案,您还可以同时更改资源名称:
1 | $ terraform state mv packet_device.worker module.worker.packet_device.main |
1.7.18.2.1.4. 例子:移动一个模块进入另一个模块
我们还可以将整个模块重构为子模块。在配置中,将代表模块的 module
块移动到不同的模块中,然后使用如下命令将更改配对:
1 | $ terraform state mv 'module.app' 'module.parent.module.app' |
1.7.18.2.1.5. 例子:移动一个模块到另一个状态文件
1 | $ terraform state mv -state-out=other.tfstate 'module.app' 'module.app' |
1.7.18.2.1.6. 移动一个带有 count 参数的资源
使用 count
元参数定义的资源具有多个实例,每个实例都由一个整数标识。我们可以通过在给定地址中包含显式索引来选择特定实例:
1 | $ terraform state mv 'packet_device.worker[0]' 'packet_device.helper[0]' |
不使用 count
或 for_each
的资源只有一个资源实例,其地址与资源本身相同,因此我们可以从不包含索引的地址移动到包含索引的地址,或相反:
1 | $ 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:
1 | $ terraform state mv 'packet_device.worker["example123"]' 'packet_device.helper["example456"]' |
PowerShell:
1 | $ terraform state mv 'packet_device.worker[\"example123\"]' 'packet_device.helper[\"example456\"]' |
Windows 命令行(cmd.exe
):
1 | $ terraform state mv packet_device.worker[\"example123\"] packet_device.helper[\"example456\"] |
除了使用字符串而不是整数作为实例键之外,for_each
资源的处理与 count
资源类似,因此具有和不具有索引组件的相同地址组合都是有效的,如上一节所述。
1.7.18.3.1. pull
terraform state pull
命令可以从远程 Backend 中人工下载状态并输出。该命令也可搭配本地状态文件使用。
1.7.18.3.1.1. 用法
terraform state pull
该命令下载当前位置对应的状态文件,并以原始格式打印到标准输出流。
由于状态文件使用 JSON 格式,该功能可以搭配例如 jq 这样的命令行工具使用,也可以用来人工修改状态文件。
注意:Terraform 状态文件必须采用 UTF-8 格式,不带字节顺序标记 (BOM)。对于 Windows 上的 PowerShell,使用 Set-Content 自动以 UTF-8 格式对文件进行编码。例如,运行 terraform state pull
| sc terraform.tfstate
push
1.7.18.4.1. push
terraform push
命令被用来手动上传本地状态文件到远程 Backend。该命令也可以被用在当前使用的本地状态文件上。
该命令应该很少使用。它时一种需要对远程状态进行手动干预的情况下使用的工具。
1.7.18.4.1.1. 用法
terraform state push [options] PATH
该命令会把 PATH 位置的状态文件推送到当前使用的 Backend 上(可以是当前使用的 terraform.tfstate 文件)。
如果 PATH 为 -
,则从标准输入流读取要推送的状态数据。该数据在写入目标状态之前被完全加载到内存中并进行验证。
注意:Terraform 状态文件必须采用 UTF-8 格式,不带字节顺序标记 (BOM)。对于 Windows 上的 PowerShell,使用 Set-Content 自动以 UTF-8 格式对文件进行编码。例如,运行 terraform state push | sc terraform.tfstate
。
Terraform 会进行一系列检查以防止你进行一些不安全的变更:
- 检查 lineage:如果两个状态文件的 lineage 值不同,Terraform 会禁止推送。一个不同的 lineage 说明两个状态文件描述的是完全不同的基础设而你可能会因此丢失重要数据
- 序列号检查:如果目标状态文件的 serial 值大于你要推送的状态的 serial 值,Terraform 会禁止推送。一个更高的 serial 值说明目标状态文件已经无法与要推送的状态文件对应上了
这两种检查都可以通过添加 -force
参数禁用,但不推荐这样做。如果禁用安全检查直接推送,那么目标状态文件将被覆盖。
replace-provider
1.7.18.5.1. replace-provider
terraform state replace-provider
命令可以替换状态文件中资源对象所使用的 Provider.
1.7.18.5.1.1. 用法
terraform state replace-provider [options] FROM_PROVIDER_FQN TO_PROVIDER_FQN
该命令会更新所有使用 from 的 Provider 的资源,将它们使用的 Provider 更新为 to Provider。这让我们可以更新状态文件中资源所使用的 Provider 的源。
该命令在进行任意修改之前会先生成一个备份文件。备份机制不可关闭。
支持以下可选参数:
-auto-approve
:跳过交互式提示确认环节-lock=false
:执行时是否先锁定状态文件。如果其他人可能同时对同一工作区运行命令,则这是危险的。-lock-timeout=0s
:除非使用-lock=false
禁用锁定,否则命令 Terraform 为上锁操作设置一个超时时长。持续时间语法是一个数字后跟一个时间单位字母,例如“3s”表示三秒。
以下是使用 local
Backend 时可用的遗留参数:
-backup=FILENAME
:指定源状态文件的备份地址,默认为源状态文件加上".backup"后缀-bakcup-out=FILENAME
:指定目标状态文件的备份地址,默认为目标状态文件加上".backup"后缀-state=FILENAME
:源状态文件地址,默认为当前 Backend 或是"terraform.tfstate"-state-out=FILENAME
:目标状态文件地址。如果不指定则使用源状态文件。可以是一个已经存在的文件或新建一个文件
1.7.18.5.1.2. 样例
下面的示例将 hashicorp/aws
Provider 程序替换为 acme
的复刻版本,该 Provider 托管在 registry.acme.corp
的私有注册表中:
1 | $ terraform state replace-provider hashicorp/aws registry.acme.corp/acme/aws |
1.7.18.6.1. rm
Terraform 状态的主要功能是记录下代码中的资源实例地址与其代表的远程对象之间的绑定。通常,Terraform 会自动更新状态以响应应用计划时采取的操作,例如删除已被删除的远程对象的绑定。
terraform state rm
命令可以用来从状态文件中删除对象和实际远程对象的绑定,该命令只是删除绑定,不会删除实际存在的远程对象,删除后 Terraform 会“忘记”这个对象的存在。
注意:从 Terraform v1.7.0 开始支持 removed
块。与 terraform state rm
命令不同,您可以使用 removed
块一次删除多个资源,并且您可以将删除操作作为正常计划和执行工作流程的一部分进行审查。了解有关将 removed
块与资源一起使用以及将 removed
块与模块一起使用的更多信息。
1.7.18.6.1.1. 用法
terraform state rm [options] ADDRESS...
Terraform 将在状态中搜索与给定资源地址匹配的任何实例,并删除所有实例对应的记录,以便 Terraform 将不再跟踪相应的远程对象。
这意味着尽管这些对象仍将继续存在于远程系统中,但后续的 terraform plan
会尝试新建这些被“遗忘”的实例。根据远程系统施加的约束,如果这些对象的名称或其他标识符与仍然存在的旧对象发生冲突,创建这些对象可能会失败。
可以使用如下可选参数:
-dry-run
:报告与给定地址匹配的所有资源实例(由于此时并未执行删除,所以 Terraform 这时还不会“遗忘”任何资源)。-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.6.1.1.1. 删除一个资源
下面的例子演示了如何让 Terraform “遗忘”所有类型为 packet_device
,并且名为 worker
的资源实例:
1 | $ terraform state rm 'packet_device.worker' |
不使用 count
或 for_each
的资源只有一个实例,因此该示例也是选择该单个实例的正确语法。
1.7.18.6.1.1.2. 删除一个模块
1 | $ terraform state rm 'module.foo' |
1.7.18.6.1.1.3. 删除一个模块内资源
要选择在子模块中定义的资源,我们必须指定该模块的路径作为资源地址的一部分:
1 | $ terraform state rm 'module.foo.packet_device.worker' |
1.7.18.6.1.1.4. 删除一个声明count的资源
使用 count
元参数定义的资源具有多个实例,每个实例都由一个整数标识。我们可以通过在给定地址中包含显式索引来选择特定实例:
1 | $ terraform state rm 'packet_device.worker[0]' |
方括号 ([
, ]
) 在某些 shell 中具有特殊含义,因此我们可能需要引用或转义地址,以便将其逐字传递给 Terraform。上面的例子使用了 Unix 风格 shell 的典型引用语法。
1.7.18.6.1.1.5. 删除一个声明for_each的资源
使用 for_each
元参数定义的资源具有多个实例,每个实例都由一个字符串标识。我们可以通过在给定地址中包含显式密钥来选择特定实例。
但是,字符串的语法包含引号,并且引号符号通常在命令 shell 中具有特殊含义,因此我们需要为我们正在使用的 shell 使用适当的引用和/或转义语法。例如:
Linux, MacOS, and Unix:
1 | $ terraform state rm 'packet_device.worker["example"]' |
PowerShell:
1 | $ terraform state rm 'packet_device.worker[\"example\"]' |
Windows命令行(cmd.exe
):
1 | $ terraform state rm packet_device.worker[\"example\"] |
show
1.7.18.7.1. show
terraform state show
命令可以展示状态文件中单个资源的属性。
1.7.18.7.1.1.1. 用法
terraform state show [options] ADDRESS
该命令需要指定一个资源地址。资源地址需要遵循资源地址格式。
该命令支持以下可选参数:
-state=path
:指向状态文件的路径。默认情况下是terraform.tfstate
。如果启用了远程 Backend 则该参数设置无效
terraform state show
的输出被设计成人类可读而非机器可读。如果想要从输出中提取数据,请使用 terraform show -json
。
1.7.18.7.1.1.2. 展示单个资源
1 | $ terraform state show 'packet_device.worker' |
1.7.18.7.1.1.3. 展示单个模块资源
1 | $ terraform state show 'module.foo.packet_device.worker' |
1.7.18.7.1.1.4. 展示声明count资源中特定实例
1 | $ terraform state show 'packet_device.worker[0]' |
1.7.18.7.1.1.5. 展示声明for_each资源中特定实例
Linux, MacOS, and Unix:
1 | $ terraform state show 'packet_device.worker["example"]' |
PowerShell:
1 | $ terraform state show 'packet_device.worker[\"example\"]' |
Windows命令行:
1 | $ terraform state show packet_device.worker[\"example\"] |
1.7.19.1. taint
terrform taint
命令可以手动标记某个Terraform管理的资源有"污点",强迫在下一次执行apply时删除并重建之。
该命令并不会修改基础设施,而是在状态文件中的某个资源对象上标记污点。当一个资源对象被标记了污点,在下一次 plan
操作时会计划将之删除并且重建,apply
操作会执行这个变更。
强迫重建某个资源可以使你能够触发某种副作用。举例来说,你想重新执行某个预置器操作,或是某些人绕过 Terraform 修改了虚拟机状态,而你想将虚拟机重置。
注意为某个资源标记污点并重建之会影响到所有依赖该资源的对象。举例来说,一条 DNS 记录使用了服务器的 IP 地址,我们在服务器上标记污点会导致 IP 发生变化从而影响到 DNS 记录。这种情况下可以使用 plan
命令查看变更计划。
警告:此命令已被弃用。从 Terraform v0.15.2 开始,我们建议使用 -replace
选项和 terraform apply
代替(详细信息如下)。
1.7.19.1.1. 推荐的替代方法
从 Terraform v0.15.2 开始,我们建议使用 terraform apply
的 -replace
选项来强制 Terraform 替换对象,即使没有发生需要变更的配置更改。
1 | terraform apply -replace="aws_instance.example[0]" |
我们推荐使用 -replace
参数,因为这可以在 Terraform 计划中显示将要发生的变更,让我们在采取任何会影响系统的操作之前了解计划将如何影响我们的基础设施。当我们使用 terraform taint
时,其他用户有可能可以在我们审查变更之前针对标记的对象创建新的变更计划。
1.7.19.1.2. 用法
1 | terraform taint [options] <address> |
address
参数是要标记污点的资源地址。该地址格式遵循资源地址语法,例如:
aws_instance.foo
aws_instance.bar[1]
aws_instance.baz[\"key\"]
(资源地址中的引号必须在命令行中转义,这样它们就不会被 shell 解释)module.foo.module.bar.aws_instance.qux
该命令可以使用如下可选参数:
-allow-missing
:如果声明该参数,那么即使资源不存在,命令也会返回成功(状态码0)。对于其他异常情况,该命令可能仍会返回错误,例如读取或写入状态时出现问题。-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.19.1.3. 标记单个资源
1 | $ terraform taint aws_security_group.allow_all |
1.7.19.1.4. 标记使用for_each创建的资源的特定实例
1 | $ terraform taint "module.route_tables.azurerm_route_table.rt[\"DefaultSubnet\"]" |
1.7.19.1.5. 标记模块中的资源
1 | $ terraform taint "module.couchbase.aws_instance.cb_node[9]" |
虽然我们推荐模块深度不要超过1,但是我们仍然可以标记多层模块中的资源:
1 | $ terraform taint "module.child.module.grandchild.aws_instance.example[2]" |
1.7.20.1. validate
terraform validate
命令可以检查目录下 Terraform 代码,只检查语法文件,不会访问诸如远程 Backend、Provider 的 API 等远程资源。
validate
检查代码的语法是否合法以及一致,不管输入变量以及现存状态。
自动运行此命令是安全的,例如作为文本编辑器中的保存后检查或作为 CI 系统中可复用的测试步骤。
validate
命令需要已初始化的工作目录,所有引用的插件与模块都被安装完毕。如果只想检查语法而不想与 Backend 交互,可以这样初始化工作目录:
1 | $ terraform init -backend=false |
要验证特定运行上下文中的配置(特定目标工作空间、输入变量值等),请改用 terraform plan
命令,其中包括隐式验证检查。
1.7.20.1.1. 用法
terraform validate [options]
默认情况下 validate
命令不需要任何参数就可以在当前工作目录下进行检查。
可以使用如下可选参数:
- -json:使用 JSON 格式输出机器可读的结果
- -no-color:禁止使用彩色输出
1.7.20.1.2. JSON 输出格式
当您使用 -json
选项时,Terraform 将生成 JSON 格式的验证结果,使得我们可以将之与验证结果的工具进行集成,例如在文本编辑器中突出显示错误。
与所有 JSON 输出选项一样,Terraform 在开始验证任务之前就可能会遇到错误,因此输出的错误可能不会是 JSON 格式的。因此,使用 Terraform 输出的外部软件应该准备好在 stdout 上读取到非有效 JSON 的数据,然后将其视为一般错误情况。
输出包含一个 format_version
键,从 Terraform 1.1.0 开始,其值为“1.0”。该版本的语义是:
- 对于向后兼容的变更或新增字段,我们将增加 minor 版本号,例如
"1.1"
。这种变更会忽略所有不认识的对象属性,以保持与未来其他 minor 版本的前向兼容。 - 对于不向后兼容的变更,我们将增加 major 版本,例如
"2.0"
。不同的 major 版本之间的数据无法直接传递。
我们只会在 Terraform 1.0 兼容性承诺的范围内更新 major 版本。
在正常情况下,Terraform 会将 JSON 对象打印到标准输出流。顶级 JSON 对象将具有以下属性:
valid
(bool):总体验证结果结论,如果 Terraform 认为当前配置有效,则为true
;如果检测到任何错误,则为false
。error_count
(number):零或正整数,给出 Terraform 检测到的错误计数。如果valid
为true
,则error_count
将始终为零,因为错误的存在表明配置无效。warning_count
(number):零或正整数,给出 Terraform 检测到的警告计数。警告不会导致 Terraform 认为配置无效,但用户应考虑并尝试解决它们。diagnostics
(对象数组):嵌套对象的 JSON 数组,每个对象描述来自 Terraform 的错误或警告。
diagnostics
中的对象拥有如下属性:
severity
(string):字符串关键字,可以是"error"
或"warning"
,指示诊断严重性。error
的存在会导致 Terraform 认为配置无效,而warning
只是对用户的建议或警告,不会阻止代码运行。Terraform 的后续版本可能会引入新的严重性等级,因此解析错误信息时应该准备好接受并忽略他们不了解的severity
值。summary
(string):诊断报告的问题性质的简短描述。
在 Terraform 易于阅读的的诊断消息中,summary
充当诊断的一种“标题”,打印在 “Error:” 或 “Warning:” 指示符之后。
摘要通常是简短的单个句子,但如果返回错误的子系统并没有设计成返回全面的诊断信息时,就只能把整个错误信息作为摘要返回,导致较长的摘要。这种情况下,摘要可能包含换行符,渲染摘要信息时需要注意。
detail
(string):可选的附加消息,提供有关问题的更多详细信息。
在 Terraform 易于阅读的的诊断消息中,详细信息提供了标题和源位置引用之后出现的文本段落。
详细消息通常是多个段落,并且可能散布有非段落行,因此旨在向用户呈现详细消息的工具应该区分没有前导空格的行,将它们视为段落,以及有前导空格的行,将它们视为预格式化文本。然后,渲染器应该对段落进行软换行以适合渲染容器的宽度,但保留预格式化的行不换行。
一些 Terraform 详细消息包含使用 ASCII 字符来标记项目符号的近似项目符号列表。这不是官方承诺,因此渲染器应避免依赖它,而应将这些行视为段落或预格式化文本。此格式的未来版本可能会为其他文本约定定义附加规则,但将保持向后兼容性。
range
(对象):引用与诊断消息相关的配置源代码的一部分的可选对象。对于错误,这通常指示被检测为无效的特定块头、属性或表达式的边界。
源范围是一个具有 filename
属性的对象,该 filename
为当前工作目录的相对路径,然后两个属性 start
和 end
本身都是描述源位置的对象,如下所述。
并非所有诊断消息都与配置的特定部分相关,因此对于不相关的诊断消息,range
将被省略或为 null
。
snippet
(对象):可选对象,包括与诊断消息相关的配置源代码的摘录。
snippet
信息包括了:
context
(string):诊断的根上下文的可选摘要。例如,这可能是包含触发诊断的表达式的resource
块。对于某些诊断,此信息不可用,并且此属性将为空。code
(string):Terraform 配置的片段,包括诊断源。可能包含多行,并且可能包括触发诊断的表达式周围的附加配置源代码。start_line
(number):从一开始的行计数,表示源文件中代码摘录开始的位置。该值不一定与range.start.line
相同,因为code
可能在诊断源之前包含一行或多行上下文。highlight_start_offset
(number):代码字符串中从零开始的字符偏移量,指向触发诊断的表达式的开头。highlight_end_offset
(number):代码字符串中从零开始的字符偏移量,指向触发诊断的表达式的末尾。values
(对象数组):包含零个或多个表达式值,帮助我们理解复杂表达式中的诊断来源。这些表达式值对象如下所述。
1.7.20.1.2.1. 源位置(Source Position)
在诊断对象的 range
属性中源位置对象具有以下属性:
byte
(number):指定文件中从零开始的字节偏移量。line
(number):从一开始的行计数,指向文件中相关位置的行。column
(number):从一开始的列计数,指向line
对应的行开头开始的 Unicode 字符计数位置。start
位置是包含的(数学的[]
),而end
位置是不包含的(数学的()
)。用于特定错误消息的确切位置仅供人类解读。
1.7.20.1.2.2. 表达式值
表达式值对象提供有关触发诊断的表达式一部分的值的附加信息。当使用 for_each
或类似结构时,这特别有用,以便准确识别哪些值导致错误。该对象有两个属性:
-
traversal
(string):类似 HCL 的可遍历表达式字符串,例如var.instance_count
。复杂的索引键值可能会被省略,因此该属性并非总是合法、可解析的 HCL。该字符串的内容旨在便于人类阅读。 -
statement
(string):一个简短的英语片段,描述触发诊断时表达式的值。该字符串的内容旨在便于人类阅读,并且在 Terraform 的未来版本中可能会发生变化。
1.7.21.1. untaint
Terraform 有一个名为“tainted”的标记,用于跟踪可能损坏的对象,该命令已被废弃,应使用 terraform apply -replace
代替。
如果创建一个资源的操作由多个步骤组成,操作期间其中之一的操作发生错误,Terraform 会自动将对象标记为“受污染”,因为 Terraform 无法确定该对象是否处于完整功能状态。
terraform untaint
命令可以手动清除一个 Terraform 管理的资源对象上的污点,恢复它在状态文件中的状态。它是 terraform taint
的逆向操作。
该命令不会修改实际的基础设施资源,只会在资源文件中清除资源对象上的污点标记。
如果我们从对象中删除污点标记,但后来发现它还是损坏了,则可以使用如下命令创建并应用一个计划来替换受损的资源对象,而无需首先重新在该对象上标记污点:
1 | terraform apply -replace="aws_instance.example[0]" |
1.7.21.1.1. 用法
terraform untaint [options] address
name参数是要清除污点的资源的资源名称。该参数的格式为TYPE.NAME,比如aws_instance.foo。
可以使用如下可选参数:
-allow-missing
:如果声明该参数,那么即使资源不存在,命令也会返回成功(状态码0)。对于其他异常情况,该命令可能仍会返回错误,例如读取或写入状态时出现问题。-lock=false
:执行时是否先锁定状态文件。如果其他人可能同时对同一工作区运行命令,则这是危险的。-lock-timeout=DURATION
:除非使用-lock=false
禁用锁定,否则命令 Terraform 为上锁操作设置一个超时时长。持续时间语法是一个数字后跟一个时间单位字母,例如“3s”表示三秒。-no-color
:关闭彩色输出。在无法解释输出色彩的终端中运行 Terraform 时请使用此参数。
以下是使用 local
Backend 时可用的遗留参数:
-
-backup=FILENAME
:指定源状态文件的备份地址,默认为源状态文件加上".backup"后缀 -
-bakcup-out=FILENAME
:指定目标状态文件的备份地址,默认为目标状态文件加上".backup"后缀 -
-state=FILENAME
:源状态文件地址,默认为当前 Backend 或是"terraform.tfstate" -
-state-out=FILENAME
:目标状态文件地址。如果不指定则使用源状态文件。可以是一个已经存在的文件或新建一个文件
1.7.22.1. workspace
terraform workspace
命令可以用来管理当前使用的工作区。我们在状态管理章节中介绍过工作区的概念。
该命令包含一系列子命令,我们将会一一介绍。
1.7.22.1.1. 用法
terraform workspace <subcommand> [options] [args]
1.7.22.1.1. list
terraform workspace list
命令列出当前存在的工作区。
1.7.22.1.1.1. 用法
terraform workspace list [DIR]
该命令会打印出存在的工作区。当前工作会使用 *
号标记:
1 | $ terraform workspace list |
1.7.22.2.1. select
terraform workspace select
命令用来选择使用的工作区。
1.7.22.2.1.1. 用法
terraform workspace select NAME [DIR]
NAME 指定的工作区必须已经存在:
该命令支持以下参数
-or-create
:如果指定的工作区不存在,则创建之。默认为false
。
1 | $ terraform workspace list |
new
1.7.22.3.1. new
terraform workspace new
命令用来创建新的工作区。
1.7.22.3.1.1. 用法
terraform workspace new [OPTIONS] NAME [DIR]
该命令使用给定名字创建一个新的工作区。不可存在同名工作区。
如果使用了 -state
参数,那么给定路径的状态文件会被拷贝到新工作区。
该命令支持以下可选参数:
-lock=false
:执行时是否先锁定状态文件。如果其他人可能同时对同一工作区运行命令,则这是危险的。-lock-timeout=DURATION
:除非使用-lock=false
禁用锁定,否则命令 Terraform 为上锁操作设置一个超时时长。持续时间语法是一个数字后跟一个时间单位字母,例如“3s”表示三秒。默认为0s
。-state=path
:用来初始化新环境所使用的状态文件路径
创建新工作区:
1 | $ terraform workspace new example |
使用状态文件创建新工作区:
1 | $ terraform workspace new -state=old.terraform.tfstate example |
delete
1.7.22.4.1. delete
terraform workspace delete
命令被用以删除已经存在的工作区。
1.7.22.4.1.1. 用法
terraform workspace delete [OPTIONS] NAME [DIR]
该命令被用以删除已经存在的工作区。
被删除的工作区必须已经存在,并且不可以删除当前正在使用的工作区。如果工作区状态不是空的(存在跟踪中的远程对象),Terraform 会禁止删除,除非声明 -force
参数。
另外,不同的 Backend 在没有 -force
参数时可能会有不同的限制,以实现对工作区的安全删除,例如检查工作区是否已上锁。
如果使用 -force
删除非空工作区,那么原本跟踪的资源的状态就将处于"dangling",也就是实际基础设施资源仍然存在,但脱离了 Terraform的 管理。有时我们希望这样,只是希望当前 Terraform 项目不再管理这些资源,交由其他项目管理。但大多数情况下并非这样,所以 Terraform 默认会禁止删除非空工作区。
该命令可以使用如下可选参数:
-force
:删除含有非空状态文件的工作区。默认为false
。-lock=false
:执行时是否先锁定状态文件。如果其他人可能同时对同一工作区运行命令,则这是危险的。-lock-timeout=DURATION
:除非使用-lock=false
禁用锁定,否则命令 Terraform 为上锁操作设置一个超时时长。持续时间语法是一个数字后跟一个时间单位字母,例如“3s”表示三秒。默认为0s
。
例子:
1 | $ terraform workspace delete example |
1.7.22.5.1. show
terraform workspace show
命令被用以输出当前使用的工作区。
1.7.22.5.1.1. 用法
terraform workspace show
例子:
1 | $ terraform workspace show |
1.7.23.1. test
terraform test
命令读取 Terraform 测试文件并执行其中的测试。
test
命令和测试文件对于想要验证和测试其旨在被复用的模块的作者特别有用。我们也可以使用 test
命令来验证根模块。
1.7.23.1.1. 用法
terraform test [options]
该命令在当前目录和指定的测试目录(默认情况下是 test
目录)中搜索所有 Terraform 测试文件,并执行指定的测试。有关测试文件的更多详细信息,请参阅测试。
Terraform 然后会根据测试文件的规范执行一系列 Terraform 的 plan
或 apply
命令,并根据测试文件的规范验证相关计划和状态文件。
警告:Terraform 测试命令可以创建真正的基础设施,但可能会产生成本。请参阅 Terraform 测试清理部分,了解确保创建的基础设施被清理的最佳实践。
1.7.23.1.2. 一般参数
Terraform test
命令支持以下参数:
-cloud-run=<module source>
- 通过 HCP Terraform 远程运行针对指定的 Terraform 私有注册表模块的测试。-filter=testfile
- 将terraform test
操作限制为指定的测试文件。-json
- 显示测试结果的机器可读 JSON 输出。-test-directory=<relative directory>
- 指定 Terraform 查找测试文件的目录。请注意,Terraform 始终在主代码目录中加载测试文件。默认的测试目录是tests
。-verbose
- 根据每个运行块的command
属性打印出测试文件中每个run
块的计划或状态。
1.7.23.1.3. 状态管理
每个 Terraform 测试文件在执行时都会在内存中从无到有地维护所需的所有 Terraform 状态。该状态完全独立于被测代码的任何现有状态,因此您可以安全地执行 Terraform 测试命令,而不会影响任何已存在的基础设施。
1.7.23.1.3.1. Terraform 测试清理
Terraform test
命令可以创建真实的基础设施。一旦 Terraform 完全执行了所有测试文件,Terraform 就会尝试销毁所有遗留的基础设施。如果无法销毁,Terraform 会报告由它创建但无法销毁的资源列表。
我们应该密切监视测试命令的输出,以确保 Terraform 清理了它创建的基础设施,否则需要执行手动清理。我们建议为目标 Provider 创建专用的测试帐户,这样可以定期安全地清除该帐户内的资源,确保不会意外地留下昂贵的资源。
Terraform 还提供诊断,解释为什么它无法自动清理。我们应该检查这些诊断,以确保未来的清理操作成功。
1.7.23.1.4. 在 HCP Terraform 上运行测试
我们可以使用 -cloud-run
参数在 HCP Terraform 上远程执行测试。
-cloud-run
参数接受私有注册表模块地址。此参数针对 HCP Terraform 用户界面中指定的私有模块运行测试。
我们必须提供来自私有注册表的模块,而不是公共 Terraform 注册表。
在使用该参数之前,您必须执行 terraform login
,并确保您的 host
参数与目标模块的私有注册表主机名匹配。
1.7.23.1.5. 例子:测试的目录结构与命令
以下目录结构表示包含测试和配置(setup)模块的 Terraform 模块的示例目录树:
1 | project/ |
在项目的根目录下,有一些典型的 Terraform 配置文件:main.tf
、outputs.tf
、terraform.tf
和 variables.tf
。测试文件 validations.tftest.hcl
和 outputs.tftest.hcl
位于默认测试目录 tests
中。
另外 testing
目录下有一个为测试而存在的设置(setup)模块
要执行测试,我们应该从代码根目录运行 terraform test
,如同运行 terraform plan
或 terraform apply
一样。尽管实际的测试文件位于内嵌的 tests
目录中,但 Terraform 仍从主代码目录执行。
可以使用 -filter
参数指定执行特定的测试文件。
Linux、Mac 操作系统和 UNIX 下:
1 | terraform test -filter=tests/validations.tftest.hcl |
PowerShell:
1 | terraform test -filter='tests\validations.tftest.hcl' |
Windows cmd.exe
:
1 | terraform test -filter=tests\validations.tftest.hcl |
1.7.23.1.5.1. 另一种测试目录结构
在上面的示例中,测试文件位于默认的 tests
目录中。测试文件也可以直接包含在主代码目录中:
1 | project/ |
测试文件的位置不会影响 terraform test
的运行。测试文件的所有引用以及其中的绝对文件路径都应相对于主代码目录。
我们还可以使用 -test-directory
参数来更改测试文件的位置。例如, terraform test -test-directory=testing
将命令 Terraform 从 testing
目录加载测试,而不是 tests
。
测试目录必须位于主代码目录下,但可以多层嵌套。
注意:无论
-test-directory
的值为何,根代码目录中的测试文件始终会被加载。
我们不建议更改默认测试目录。这些自定义选项是为那些在 terraform test
功能发布之前可能已在其代码中包含了 tests
子模块的代码作者准备的。一般来说,应始终使用默认的 tests
目录。