1. 概述

Terraform 是一个广泛使用的 IaC(Infrastructure as Code,基础设施即代码)工具,通过声明式语言 HCL(HashiCorp Configuration Language) 来构建和管理资源集合。这些资源可以是云上的服务器、数据库、网络设备,也可以是本地文件系统中的文件。

通常在执行 terraform apply 时,我们会一次性创建计划中所有的资源。但在某些场景下,我们可能只需要应用部分资源,比如排查某个资源的问题,或者因为某个 provider 存在 bug 需要单独调试。这时我们不希望一次性执行整个计划。

本文将介绍如何通过指定特定配置文件,只应用 Terraform 计划中的一部分资源。我们使用的 Terraform 版本为 1.8.2。

2. 示例配置

本节将演示一个创建本地文件的 Terraform 配置,这些文件中包含不同语言的问候语。

配置目录下有如下文件:

$ ls
hello_en.tf  hello_it.tf  hello_tr.tf  locals.tf

其中 hello_en.tfhello_it.tfhello_tr.tf 分别用于创建英文、意大利文和土耳其文的问候文件,而 locals.tf 定义了这些资源中使用的本地变量。

2.1. 本地变量

我们先看 locals.tf 的内容:

locals {
    output_dir = "/tmp/hellos_output_dir"
    file_perm = "0744"
    files = {
        English = "hello_en.txt"
        Italian = "hello_it.txt"
        Turkish = "hello_tr.txt"
    }
    hello_messages = {
        English = "Hello Baeldung!\n"
        Italian = "Ciao Baeldung!\n"
        Turkish = "Merhaba Baeldung!\n"
    }
}

我们使用 locals 块定义了几个本地变量:

  • output_dir:文件输出目录
  • file_perm:文件权限
  • files:各语言对应的文件名
  • hello_messages:各语言对应的问候语内容

这些变量在资源中被引用,实现统一配置。

2.2. 资源定义

hello_en.tf 为例,其内容如下:

resource "local_file" "hello_en" {
    content = local.hello_messages["English"]
    filename = "${local.output_dir}/${local.files["English"]}"
    file_permission = local.file_perm
}

说明:

  • 使用 resource 声明资源
  • 资源类型为 local_file,用于创建本地文件
  • content 指定文件内容,这里引用了 locals.hello_messages["English"]
  • filename 是文件路径,由 output_dirfiles["English"] 拼接而成
  • file_permission 设置文件权限

另外两个文件 hello_it.tfhello_tr.tf 结构类似,只是引用了不同的语言键值。

2.3. 构建基础设施

初始化 Terraform:

$ terraform init
...
Terraform has been successfully initialized!

直接执行 apply(跳过 plan):

$ terraform apply -auto-approve
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

查看生成的文件:

$ ls /tmp/hellos_output_dir
hello_en.txt  hello_it.txt  hello_tr.txt
$ cat /tmp/hellos_output_dir/hello_en.txt
Hello Baeldung!

成功创建了三个语言文件,内容正确。

3. 使用 -target 参数

默认情况下,terraform apply 会创建所有资源。如果我们只想创建某一个资源,可以使用 -target 参数。

3.1. 清理资源

先清理已有资源:

$ terraform destroy -auto-approve
Destroy complete! Resources: 3 destroyed.

此时 /tmp/hellos_output_dir 目录下的文件也被删除。

3.2. 单独应用某个资源

使用 -target 只创建 hello_tr 资源:

$ terraform apply -auto-approve -target=local_file.hello_tr
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

验证文件是否生成:

$ ls /tmp/hellos_output_dir/
hello_tr.txt
$ cat /tmp/hellos_output_dir/hello_tr.txt
Merhaba Baeldung!

✅ 成功只创建了 hello_tr.txt 文件。

3.3. 多个 -target

你也可以多次使用 -target 指定多个资源:

$ terraform apply -auto-approve -target=local_file.hello_tr -target=local_file.hello_en

⚠️ 注意:虽然 -target 很方便,但在正常流程中不推荐使用。因为它会绕过依赖关系,可能导致状态不一致。

4. 使用模块(Module)组织配置

模块是 Terraform 中组织配置文件的基本单位。任何包含配置文件的目录都可以视为一个模块。例如,我们的示例就是一个模块。

4.1. 重构目录结构

我们将配置文件整理如下:

$ tree
. 
├── hello_messages
│   ├── hello_en.tf
│   ├── hello_it.tf
│   ├── hello_tr.tf
│   ├── locals.tf
│   └── outputs.tf
└── hello_zipper.tf
  • hello_messages 是一个子模块,包含创建文件的逻辑
  • hello_zipper.tf 是根模块,调用子模块并打包生成的文件

4.2. 输出变量

子模块中新增 outputs.tf

output "output_dir" {
    value = local.output_dir
}

用于将 output_dir 输出给根模块使用。

4.3. 根模块定义

hello_zipper.tf 内容如下:

module "hellos" {
    source = "./hello_messages"
} 

resource "archive_file" "hellos_zipped" {
    type = "zip"
    output_path = "./hellos.zip"
    source_dir = module.hellos.output_dir
}

说明:

  • 使用 module 块调用子模块 hello_messages
  • 使用 archive_file 资源将子模块生成的文件打包为 zip
  • source_dir 引用了子模块的输出变量 output_dir

4.4. 构建模块

在根模块目录下执行 apply:

$ terraform apply -auto-approve

查看生成的压缩包:

$ ls hellos.zip
hellos.zip
$ unzip hellos.zip
Archive:  hellos.zip
  inflating: hello_en.txt
  inflating: hello_it.txt
  inflating: hello_tr.txt

内容正确,说明模块构建成功。

使用模块可以更清晰地组织配置文件,并支持独立构建。例如,如果只修改了 hello_zipper.tf,我们只需在根模块目录下执行 apply,无需使用 -target

5. 总结

本文介绍了如何通过指定特定配置文件来执行 terraform apply,实现只创建部分资源:

  • 使用 -target 参数可以指定具体资源(如 local_file.hello_tr
  • 该参数也适用于 terraform planterraform destroy
  • 不建议在正常流程中频繁使用 -target,因为它可能破坏依赖关系
  • 使用模块可以更好地组织配置文件,实现模块化构建,减少对 -target 的依赖

合理使用模块结构和 -target 参数,能帮助我们在复杂项目中更灵活地控制资源的创建与更新。


原始标题:Passing Specific Configuration Files to terraform apply