1.7.14.1. plan
terraform plan
命令被用来创建变更计划。Terraform 会先运行一次 refresh
(我们后面的章节会介绍,该行为也可以被显式关闭),然后决定要执行哪些变更使得现有状态迁移到代码描述的期待状态。
该命令可以方便地审查状态迁移的所有细节而不会实际更改现有资源以及状态文件。例如,在将代码提交到版本控制系统前可以先执行 terraform plan
,确认变更行为如同预期一般。
如果您直接在交互式终端中使用 Terraform 并且希望执行 Terraform 所提示的变更,您也可以直接运行 terraform apply
。默认情况下,apply
命令会自动生成新计划并提示您批准它。
可选参数 -out
可以将变更计划保存在一个文件中,以便日后使用 terraform apply
命令来执行该计划。
在使用版本控制和代码审查工作流程对实际基础架构进行更改的团队中,开发人员可以使用保存下来的的计划文件来验证更改的效果,然后再对提交的变更进行代码审查。但是,要慎重考虑考虑对目标系统同时进行的其他更改可能会导致配置更改的最终效果与早期保存的计划所指示的不同,因此您应该始终重新检查最终的实际执行的计划,在执行之前确保它仍然符合您的意图。
如果 Terraform 检测不到任何变更,那么 terraform plan
会提示没有任何需要执行的变更。
1.7.14.1.1. 用法
terraform plan [options]
plan
命令在当前工作目录中查找根模块配置。
由于 plan
命令是 Terraform 的主要命令之一,因此它有多种不同的选项,如下部分所述。但是,大多数时候我们不需要设置这些选项,因为 Terraform 配置通常应设计为无需特殊的附加选项即可进行日常工作。
plan
命令的参数选项可以分为以下三大类
- Plan 模式:当我们的目标不仅仅是更改远程系统以匹配代码配置时,我们可以在某些特殊情况下使用一些特殊的替代规划模式。
- Plan 选项:除了特殊的 Plan 模式之外,我们还可以设置一些选项,以便根据特殊的需求来定制计划流程。
- 其他选项:这些选项改变了规划命令本身的行为,而不是定制生成的计划的内容。
1.7.14.1.2. Plan 模式
上一节描述了 Terraform 的默认规划变更计划行为,该行为会变更远程系统以匹配我们对配置代码所做的更改。 Terraform 还有两种不同的规划模式,每种模式都会创建具有不同预期结果的计划。这些选项可用于 terraform plan
和 terraform apply
。
Destroy 模式:创建一个计划,其目的是销毁当前存在的所有远程对象,留下空的 Terraform 状态。这与运行
terraform destroy
相同。销毁模式对于临时的开发环境等情况非常有用,在这种情况下,一旦开发任务完成,托管对象就不再需要保留。通过
-destroy
命令行选项启用销毁模式。Refresh-Only 模式:创建一个计划,其目标仅是更新 Terraform 状态和所有根模块的输出值,以匹配对 Terraform 外部远程对象所做的更改。如果您使用 Terraform 之外的工具更改了一个或多个远程对象(例如,在响应事件时),并且您现在需要使 Terraform 的记录与这些更改保持一致,那么该命令会很有帮助。
使用
-refresh-only
命令行选项启用 Refresh-Only 模式。
相对而言,我们把 Terraform 在未选择任何替代模式时使用的默认规划模式的情况下的行为称为“正常模式”。由于上述的替代模式仅适用于特殊情况,因此其他一些 Terraform 文档仅讨论正常规划模式。
Plan 模式都是互斥的,因此启用任何非默认 Plan 模式时都会禁用“正常”计划模式,并且我们不能同时使用多种替代模式。
注意:在 Terraform v0.15 及更早版本中,只有 terraform plan
命令支持 -destroy
选项,terraform apply
命令是不支持的。要在早期版本中以 Destroy 模式创建并应用计划,我们必须运行 terraform destroy
。另外,-refresh-only
选项仅在 Terraform v0.15.4 及之后的版本中可用。
1.7.14.1.3. Plan 选项
相较于 Plan 模式,还有一些可以用来更改规划行为的参数选项。
-refresh=false
:在检查配置更改之前跳过同步 Terraform 状态与远程对象的默认行为。这可以减少远程 API 请求的数量,加快规划操作的速度。但是,设置refresh=false
会导致 Terraform 忽略外部更改,这可能会导致计划不完整或不正确。您不能在 Refresh Only 计划模式中使用refresh=false
,因为这将导致什么都不做。-replace=ADDRESS
- 命令 Terraform 在计划中替换给定地址的资源实例。当一个或多个远程对象降级时,这非常有用,并且我们可以替换成具有相同配置的对象来与不可变的基础架构模式保持一致。如果指定的资源在计划中存在“更新”操作或没有变化,则 Terraform 将使用“替换”操作。多次包含此选项可一次替换多个对象。您不能将-replace
与-destroy
选项一起使用,并且该功能仅从 Terraform v0.15.2 开始可用。对于早期版本,使用terraform taint
来实现类似的结果。-target=ADDRESS
- 命令 Terraform 只计算给定地址匹配的资源实例以及这些实例所依赖的任何对象的变更。 注意:应该仅在特殊情况下使用-target=ADDRESS
,例如从错误中恢复或绕过 Terraform 限制。有关更多详细信息,请参阅资源定位。-var 'NAME=VALUE'
- 设置在配置的根模块中声明的单个输入变量的值。多次设置该选项可设置多个变量。有关详细信息,请参阅在命令行中输入变量。-var-file=FILENAME
- 使用tfvars
文件中的定义为配置的根模块中声明的潜在多个输入变量设置值。多次设置该选项可包含多个文件中的值。
除了 -var
和 -var-file
选项之外,还有其他几种方法可以在根模块中设置输入变量的值。有关详细信息,请参阅为根模块输入变量赋值。
1.7.14.1.4. 在命令行中输入变量
我们可以使用 -var
命令行选项来指定根模块中声明的输入变量的值。
然而,要做到这一点,需要编写一个可由您选择的 shell 和 Terraform 解析的命令,对于涉及大量引号和转义序列的表达式来说这可能会很复杂。在大多数情况下,我们建议改用 -var-file
选项,并将实际值写入单独的文件中,以便 Terraform 可以直接解析它们,而不是解释 shell 解析后的结果。
警告:如果在等号之前或之后包含空格(例如 -var "length = 2"),Terraform 将报错。
要在 Linux 或 macOS 等系统上的 Unix 风格 shell 上使用 -var
,我们建议将选项参数写在单引号 '
中,以确保 shell 按字面解释该值:
terraform plan -var 'name=value'
如果我们的预期值还包含单引号,那么我们仍然需要对其进行转义,以便 shell 进行正确解释,这还需要暂时终止引号序列,以便反斜杠转义字符合法:
terraform plan -var 'name=va'\''lue'
在 Windows 上使用 Terraform 时,我们建议使用 Windows 命令提示符 (cmd.exe)。当您从 Windows 命令提示符将变量值传递给 Terraform 时,请在参数周围使用双引号 "
:
terraform plan -var "name=value"
如果我们的预期值还包含双引号,那么您需要用反斜杠转义它们:
terraform plan -var "name=va\"lue"
Windows 上的 PowerShell 无法正确地将文字引号传递给外部程序,因此我们不建议您在 Windows 上时将 Terraform 与 PowerShell 结合使用。请改用 Windows 命令提示符。
根据变量的类型约束,声明变量值的语法有所不同。原始类型 string
、number
和 bool
对应一个直接的字符串值,除非您的 shell 如上面的示例所示需要,否则不需要添加特殊的标点符号。对于所有其他类型约束,包括 list
、map
和 set
类型以及特殊的 any
关键字,您必须编写一个表示该值的有效 Terraform 语言表达式,并附带必要的引用或转义字符以确保它将通过您的 shell 逐字传递到 Terraform。例如,对于 list(string)
类型约束:
# Unix-style shell
terraform plan -var 'name=["a", "b", "c"]'
# Windows Command Prompt (do not use PowerShell on Windows)
terraform plan -var "name=[\"a\", \"b\", \"c\"]"
使用环境变量设置输入变量时也适用类似的约束。有关设置根模块输入变量的各种方法的更多信息,请参阅为根模块变量赋值。
1.7.14.1.5. 资源定位
我们可以使用 -target
选项将 Terraform 的计算范围仅集中在少数资源上。我们可以使用资源地址语法来指定约束。Terraform 对代码中的资源地址的解释行为如下:
- 如果给定地址定位了一个特定资源实例,Terraform 将单独选择该实例。对于设置了
count
或for_each
的资源,资源实例地址必须包含实例索引部分,例如azurerm_resource_group.example[0]
。 - 如果给定的地址对应到一个资源整体(即表达式中不含索引部分),Terraform 将选择该资源的所有实例。对于设置了
count
或for_each
的资源,这意味着选择当前与该资源关联的所有实例索引。对于单实例资源(没有count
或for_each
),资源地址和资源实例地址相同,因此这种可能性不适用。 - 如果给定的地址标识整个 Module 实例,Terraform 将选择属于该 Module 实例及其所有子 Module 实例的所有资源的所有实例。
一旦 Terraform 选择了我们直接定位的一个或多个资源实例,它还会扩展选择范围以包括这些选择直接或间接依赖的所有其他对象。
这种资源定位功能是为某些特殊情况设计的,例如从故障中恢复或绕过 Terraform 的某些限制。不建议将 -target
用于常规操作,因为这可能会导致未检测到的配置漂移以及对资源真实状态与配置的关系的混淆。
与其使用 -target
作为对非常庞大的配置的一小部分子集进行操作的方法,不如将大型配置分解为多个较小的配置,每个配置都可以独立应用。数据源可用于访问有关在其他配置中创建的资源的信息,从而允许将复杂的系统架构分解为更易于管理且可以独立更新的部分。
1.7.14.1.6. 其他选项
terraform plan
命令还有一些与规划命令的输入和输出相关的其他选项,这些配置不会影响 Terraform 将创建哪种类型的计划。这些命令不一定在 terraform apply
上也可用,除非该命令的文档中另有说明。
-compact-warnings
:如果 Terraform 生成了一些告警信息而没有伴随的错误信息,那么以只显示消息总结的精简形式展示告警-detailed-exitcode
:当命令退出时返回一个详细的返回码。如果有该参数,那么返回码将会包含更详细的含义:0 = 成功的空计划(没有变更)
- 1 = 错误
2 = 成功的非空计划(有变更)
-generate-config-out=PATH
-(实验功能)如果配置中存在import
块,则命令 Terraform 为尚未存在的任何导入资源生成 HCL。配置将写入PATH
位置的新文件,该文件不可以存在,否则 Terraform 将报错。如果plan
命令因为其他原因失败,Terraform 仍可能尝试写入配置。-input=false
:在取不到值的情况下是否提示用户给定输入变量值。此参数在非交互式自动化系统中运行 Terraform 时特别有用。-lock=false
:操作过程中不对状态文件上锁。如果其他人可能同一时间对同一工作区运行命令可能引发事故。-lock-timeout=DURATION
:除非使用-lock=false
禁用锁定,否则指示 Terraform 在返回错误之前重试获取锁定一段时间。持续时间语法是一个数字后跟一个时间单位字母,例如"3s"
表示三秒。-no-color
:关闭彩色输出。在无法解释输出色彩的终端中运行 Terraform 时请使用此参数。-out=FILENAME
:将变更计划保存到指定路径下的文件中,随后我们可以使用terraform apply执行该计划Terraform 将允许计划文件使用任何文件名,但典型的约定是将其命名为
tfplan
。请不要使用 Terraform 支持的后缀名来命名文件;如果您使用.tf
后缀,那么 Terraform 将尝试将该文件解释为配置源文件,这将导致后续命令出现语法错误。-parallelism-n
:限制Terraform遍历图的最大并行度,默认值为10
。
1.7.14.1.7. 指定其他配置文件目录
Terraform v0.13 及更早版本接受提供目录路径的附加位置参数,在这种情况下,Terraform 将使用该目录作为根模块而不是当前工作目录。
该用法在 Terraform v0.14 中已弃用,并在 Terraform v0.15 中删除。如果您的工作流程需要修改根模块目录,请改用 -chdir
全局选项,该选项适用于所有命令,并使 Terraform 始终在给定目录中查找它通常在当前工作目录中读取或写入的所有文件。
如果我们之前使用此遗留模式时同时需要 Terraform 将 .terraform
子目录写入当前工作目录,即使根模块目录已被覆盖,请使用 TF_DATA_DIR
环境变量命令 Terraform 将 .terraform
目录写入其他位置,而不是当前工作目录。
1.7.14.1.8. 安全警告
被保存的变更计划文件(使用 -out
参数)内部可能含有敏感信息,Terraform 本身并不会加密计划文件。如果你要移动或是保存该文件一段时间,强烈建议你自行加密该文件。
Terraform 未来打算增强计划文件的安全性。