如何使用Terraform变量、依赖项和条件来提高灵活性

来自菜鸟教程
跳转至:导航、​搜索

介绍

Terraform 使用的 Hashicorp 配置语言 (HCL) 提供了许多其他编程语言中存在的有用结构和功能。 在您的基础架构代码中使用循环可以大大减少代码重复并提高可读性,从而使将来的重构更容易并具有更大的灵活性。 HCL 还提供了一些常见的数据结构,例如列表和映射(在其他语言中也分别称为数组和字典),以及执行路径分支的条件。

Terraform 的独特之处在于能够手动指定所依赖的资源。 虽然它在运行代码时构建的执行图已经包含检测到的链接(在大多数情况下都是正确的),但您可能会发现自己需要强制执行 Terraform 无法检测到的依赖关系。

在本文中,我们将回顾 HCL 提供的数据结构、其资源循环功能(count 键、for_eachfor)、用于处理已知和未知值,以及资源之间的依赖关系。

先决条件

  • DigitalOcean 个人访问令牌,您可以通过 DigitalOcean 控制面板创建它。 您可以在 DigitalOcean 产品文档中找到说明,如何创建个人访问令牌
  • Terraform 安装在您的本地计算机上,并使用 DigitalOcean 提供程序设置了一个项目。 完成 How To Use Terraform with DigitalOcean 教程的 Step 1Step 2,并确保将项目文件夹命名为 terraform-flexibility,而不是loadbalance。 在 Step 2 期间,您在配置提供程序时不需要包含 pvt_key 变量和 SSH 密钥资源。

注意: 本教程专门用 Terraform 1.0.2 测试过。


HCL 中的数据类型

在您了解更多关于循环和 HCL 的其他使您的代码更灵活的功能之前,我们将首先介绍可用的数据类型及其用途。

Hashicorp 配置语言支持 primitivecomplex 数据类型。 原始数据类型是字符串、数字和布尔值,它们是不能从其他类型派生的基本类型。 另一方面,复杂类型将多个值组合成一个值。 两种类型的复杂值是结构类型和集合类型。

结构类型允许将不同类型的值组合在一起。 主要示例是用于指定基础架构外观的资源定义。 与结构类型相比,集合类型也对值进行分组,但仅对相同类型的值进行分组。 我们感兴趣的 HCL 中可用的三种集合类型是列表、映射和集合。

列表

列表类似于其他编程语言中的数组。 它们包含已知数量的相同类型的元素,可以使用数组符号 ([]) 通过它们的整数索引进行访问,从 0 开始。 这是一个列表变量声明的示例,其中包含您将在接下来的步骤中部署的 Droplet 的名称:

variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third"]
}

对于 type,您指定它是一个元素类型为字符串的列表,然后提供其 default 值。 在 HCL 中,括号中枚举的值表示一个列表。

地图

映射是键值对的集合,其中每个值都使用其 string 类型的键来访问。 有两种方法可以在大括号内指定映射:使用冒号 (:) 或等号 (=) 指定值。 在这两种情况下,值都必须用引号括起来。 使用冒号时,键也必须括起来。

以下包含不同环境的 Droplet 名称的地图定义使用等号编写:

variable "droplet_env_names" {
  type = map(string)

  default = {
    development = "dev-droplet"
    staging = "staging-droplet"
    production = "prod-droplet"
  }
}

如果键以数字开头,则必须使用冒号语法:

variable "droplet_env_names" {
  type = map(string)

  default = {
    "1-development": "dev-droplet"
    "2-staging": "staging-droplet"
    "3-production": "prod-droplet"
  }
}

集合不支持元素排序,这意味着不能保证遍历集合每次都产生相同的顺序,并且不能以有针对性的方式访问它们的元素。 它们包含仅重复一次的唯一元素,并且多次指定相同的元素将导致它们被合并,并且集合中仅存在一个实例。

声明一个集合类似于声明一个列表,唯一的区别是变量的类型:

variable "droplet_names" {
  type    = set(string)
  default = ["first", "second", "third", "fourth"]
}

既然您已经了解了 HCL 提供的数据结构类型并查看了列表、映射和集合的语法,我们将在本教程中使用它们,您将继续尝试一些灵活的方法来部署多个实例Terraform 中的相同资源。

使用 count 键设置资源数量

在本节中,您将使用 count 键创建同一资源的多个实例。 count 键是可用于所有资源的参数,用于指定要创建的实例数量。

您将通过编写一个 Droplet 资源来了解其工作原理,该资源将存储在您作为先决条件创建的项目目录中名为 droplets.tf 的文件中。 通过运行创建并打开它进行编辑:

nano droplets.tf

添加以下行:

terraform-flexibility/droplets.tf

resource "digitalocean_droplet" "test_droplet" {
  count  = 3
  image  = "ubuntu-20-04-x64"
  name   = "web"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

此代码定义了一个名为 test_droplet 的 Droplet 资源,运行 Ubuntu 20.04,具有 1GB RAM 和一个 CPU 内核。

请注意,count 的值设置为 3,这意味着 Terraform 将尝试创建同一资源的三个实例。 完成后,保存并关闭文件。

您可以计划项目以查看 Terraform 将通过运行执行的操作:

terraform plan -var "do_token=${DO_PAT}"

输出将与此类似:

Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.test_droplet[0] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
        name                 = "web"
        ...
    }

  # digitalocean_droplet.test_droplet[1] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
        name                 = "web"
        ...
    }

  # digitalocean_droplet.test_droplet[2] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
        name                 = "web"
        ...
    }

Plan: 3 to add, 0 to change, 0 to destroy.
...

Terraform 将创建三个 test_droplet 实例的输出详细信息,它们都具有相同的名称 web。 虽然可能,但不是首选,因此让我们修改 Droplet 定义以使每个实例的名称唯一。 打开droplets.tf进行编辑:

nano droplets.tf

修改突出显示的行:

terraform-flexibility/droplets.tf

resource "digitalocean_droplet" "test_droplet" {
  count  = 3
  image  = "ubuntu-20-04-x64"
  name   = "web.${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

保存并关闭文件。

count 对象提供了 index 参数,其中包含当前迭代的索引,从 0 开始。 当前索引使用 字符串插值 替换为 Droplet 的名称,它允许您通过替换变量来动态构建字符串。 您可以再次计划项目以查看更改:

terraform plan -var "do_token=${DO_PAT}"

输出将与此类似:

Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.test_droplet[0] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
        name                 = "web.0"
        ...
    }

  # digitalocean_droplet.test_droplet[1] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
        name                 = "web.1"
        ...
    }

  # digitalocean_droplet.test_droplet[2] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
        name                 = "web.2"
        ...
    }

Plan: 3 to add, 0 to change, 0 to destroy.
...

这一次,test_droplet 的三个实例将在它们的名称中包含它们的索引,使它们更易于跟踪。

您现在知道如何使用 count 键创建资源的多个实例,以及在配置期间获取和使用实例的索引。 接下来,您将学习如何从列表中获取 Droplet 的名称。

从列表中获取液滴名称

在同一资源的多个实例需要具有自定义名称的情况下,您可以从您定义的列表变量中动态检索它们。 在本教程的其余部分中,您将看到几种从名称列表中自动部署 Droplet 的方法,从而提高灵活性和易用性。

您首先需要定义一个包含 Droplet 名称的列表。 创建一个名为 variables.tf 的文件并打开它进行编辑:

nano variables.tf

添加以下行:

terraform-flexibility/variables.tf

variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

保存并关闭文件。 此代码定义了一个名为 droplet_names 的列表,其中包含字符串 firstsecondthirdfourth

打开droplets.tf进行编辑:

nano droplets.tf

修改突出显示的行:

terraform-flexibility/droplets.tf

resource "digitalocean_droplet" "test_droplet" {
  count  = length(var.droplet_names)
  image  = "ubuntu-20-04-x64"
  name   =  var.droplet_names[count.index]
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

为了提高灵活性,您无需手动指定恒定数量的元素,而是将 droplet_names 列表的长度传递给 count 参数,该参数将始终返回列表中的元素数量。 对于名称,您使用数组括号表示法获取位于 count.index 的列表元素。 完成后保存并关闭文件。

再次尝试规划项目。 您将收到与此类似的输出:

Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.test_droplet[0] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
      + name                 = "first"
        ...
    }

  # digitalocean_droplet.test_droplet[1] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
      + name                 = "second"
        ...
    }

  # digitalocean_droplet.test_droplet[2] will be created
  + resource "digitalocean_droplet" "test_droplet" {
        ...
      + name                 = "third"
        ...
    }

  # digitalocean_droplet.test_droplet[3] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "fourth"
      ...

Plan: 4 to add, 0 to change, 0 to destroy.
...

作为这些修改的结果,将部署四个 Droplet,依次以 droplet_names 列表的元素命名。

您已经了解了 count、它的特性和语法,并且已经将它与一个列表一起使用来修改资源实例。 您现在将看到它的缺点,以及如何克服它们。

了解count的缺点

现在您知道如何使用 count 了,让我们来看看它在修改它所使用的列表时的缺点。

让我们尝试将 Droplets 部署到云端:

terraform apply -var "do_token=${DO_PAT}"

出现提示时输入 yes。 输出的结尾将与此类似:

OutputApply complete! Resources: 4 added, 0 changed, 0 destroyed.

现在让我们通过扩大 droplet_names 列表再创建一个 Droplet 实例。 打开variables.tf进行编辑:

nano variables.tf

在列表的开头添加一个新元素:

terraform-flexibility/variables.tf

variable "droplet_names" {
  type    = list(string)
  default = ["zero", "first", "second", "third", "fourth"]
}

完成后,保存并关闭文件。

规划项目:

terraform plan -var "do_token=${DO_PAT}"

您将收到如下输出:

Output...
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # digitalocean_droplet.test_droplet[0] will be updated in-place
  ~ resource "digitalocean_droplet" "test_droplet" {
      ...
      ~ name               = "first" -> "zero"
      ...
    }

  # digitalocean_droplet.test_droplet[1] will be updated in-place
  ~ resource "digitalocean_droplet" "test_droplet" {
      ...
      ~ name               = "second" -> "first"
      ...
    }

  # digitalocean_droplet.test_droplet[2] will be updated in-place
  ~ resource "digitalocean_droplet" "test_droplet" {
      ...
      ~ name               = "third" -> "second"
      ...
    }

  # digitalocean_droplet.test_droplet[3] will be updated in-place
  ~ resource "digitalocean_droplet" "test_droplet" {
      ...
      ~ name               = "fourth" -> "third"
      ...
    }

  # digitalocean_droplet.test_droplet[4] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "fourth"
      ...
    }

Plan: 1 to add, 4 to change, 0 to destroy.
...

输出显示 Terraform 将重命名前四个 Droplet 并创建第五个称为 fourth,因为它将实例视为有序列表并通过列表中的索引号标识元素(Droplet)。 这就是 Terraform 最初考虑四个 Droplet 的方式:

索引号 0 1 2 3
液滴名称 第一的 第二 第三 第四

当新的 Droplet zero 添加到开头时,其内部列表表示如下所示:

索引号 0 1 2 3 4
液滴名称 第一的 第二 第三 第四

四个初始的 Droplet 现在向右移动了一个位置。 然后 Terraform 比较表中表示的两种状态:在位置 0,Droplet 被称为 first,并且因为它在第二个表中不同,它计划更新操作。 这一直持续到位置 4,它在第一个表中没有可比较的元素,而是计划了一个 Droplet 供应操作。

这意味着将新元素添加到列表的任何位置,但最后会导致资源在不需要时被修改。 如果删除了 droplet_names 列表中的一个元素,将计划进行类似的更新操作。

不完整的资源跟踪是使用 count 部署动态数量的同一资源的不同实例的主要缺点。 对于恒定数量的常量实例,count 是一个运行良好的简单解决方案。 但是,在这种情况下,当从变量中提取某些属性时,您将在本教程后面了解的 for_each 循环是一个更好的选择。

引用当前资源 (self)

count 的另一个缺点是在某些情况下无法通过索引引用资源的任意实例。

主要示例是 destroy-time provisioners,它在资源计划被销毁时运行。 原因是请求的实例可能不存在(它已经被销毁)或者会创建一个相互依赖的循环。 在这种情况下,您可以通过 self 关键字仅访问当前资源,而不是通过实例列表引用对象。

为了演示它的用法,您现在将在 test_droplet 定义中添加一个销毁时本地配置程序,它会在运行时显示一条消息。 打开droplets.tf进行编辑:

nano droplets.tf

添加以下突出显示的行:

terraform-flexibility/droplets.tf

resource "digitalocean_droplet" "test_droplet" {
  count  = length(var.droplet_names)
  image  = "ubuntu-20-04-x64"
  name   =  var.droplet_names[count.index]
  region = "fra1"
  size   = "s-1vcpu-1gb"

  provisioner "local-exec" {
    when    = destroy
    command = "echo 'Droplet ${self.name} is being destroyed!'"
  }
}

保存并关闭文件。

local-exec 配置器在运行 Terraform 的本地计算机上运行命令。 因为when参数设置为destroy,所以只有在资源即将被销毁时才会运行。 它运行的命令将字符串回显到 stdout,它使用 self.name 替换当前资源的名称。

因为您将在下一节中以不同的方式创建 Droplet,所以通过运行以下命令来销毁当前部署的 Droplet:

terraform destroy -var "do_token=${DO_PAT}"

出现提示时输入 yes。 您将收到运行四次的 local-exec 配置程序:

Output...
digitalocean_droplet.test_droplet[0] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet first is being destroyed!'"]
digitalocean_droplet.test_droplet[1] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet second is being destroyed!'"]
digitalocean_droplet.test_droplet[1] (local-exec): Droplet second is being destroyed!
digitalocean_droplet.test_droplet[2] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet third is being destroyed!'"]
digitalocean_droplet.test_droplet[2] (local-exec): Droplet third is being destroyed!
digitalocean_droplet.test_droplet[3] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet fourth is being destroyed!'"]
digitalocean_droplet.test_droplet[3] (local-exec): Droplet fourth is being destroyed!
digitalocean_droplet.test_droplet[0] (local-exec): Droplet first is being destroyed!
...

在这一步中,您了解了 count 的缺点。 您现在将了解 for_each 循环构造,它克服了它们并适用于更广泛的变量类型数组。

使用 for_each 循环

在本节中,您将考虑 for_each 循环、它的语法以及它在定义具有多个实例的资源时如何提高灵活性。

for_each 是每个资源上可用的参数,但与需要创建多个实例的 count 不同,for_each 接受映射或集合。 所提供集合的每个元素都被遍历一次,并为其创建一个实例。 for_each 使 each 关键字下的键和值可用作属性(该对的键和值分别为 each.keyeach.value)。 提供集合时,键和值将相同。

因为它提供了 each 对象中的当前元素,所以您不必像使用列表那样手动访问所需的元素。 在集合的情况下,这甚至是不可能的,因为它在内部没有可观察到的顺序。 列表也可以传入,但必须首先使用 toset 函数将它们转换为集合。

使用 for_each 的主要优点除了能够枚举所有三种集合数据类型外,还在于只有受影响的元素才会被修改、创建或删除。 如果您更改输入中元素的顺序,则不会计划任何操作,如果您从输入中添加、删除或修改元素,则只会为该元素计划适当的操作。

让我们将 Droplet 资源从 count 转换为 for_each,看看它在实践中是如何工作的。 通过运行打开 droplets.tf 进行编辑:

nano droplets.tf

修改突出显示的行:

terraform-flexibility/droplets.tf

resource "digitalocean_droplet" "test_droplet" {
  for_each = toset(var.droplet_names)
  image    = "ubuntu-20-04-x64"
  name     = each.value
  region   = "fra1"
  size     = "s-1vcpu-1gb"
}

您可以删除 local-exec 供应商。 完成后,保存并关闭文件。

第一行替换 count 并调用 for_each,使用 toset 函数以集合的形式传入 droplet_names 列表,它会自动转换给定的输入。 对于 Droplet 名称,您指定 each.value,它保存来自 Droplet 名称集合的当前元素的值。

通过运行来计划项目:

terraform plan -var "do_token=${DO_PAT}"

输出将详细说明 Terraform 将采取的步骤:

Output...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.test_droplet["first"] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "first"
      ...
    }

  # digitalocean_droplet.test_droplet["fourth"] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "fourth"
      ...
    }

  # digitalocean_droplet.test_droplet["second"] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "second"
      ...
    }

  # digitalocean_droplet.test_droplet["third"] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "third"
      ...
    }

  # digitalocean_droplet.test_droplet["zero"] will be created
  + resource "digitalocean_droplet" "test_droplet" {
      ...
      + name                 = "zero"
      ...
    }

Plan: 5 to add, 0 to change, 0 to destroy.
...

与使用 count 相比,Terraform 现在单独考虑每个实例,而不是作为有序列表的元素。 每个实例都链接到给定集合的一个元素,如将创建的每个资源旁边的括号中显示的字符串元素所示。

通过运行将计划应用到云:

terraform apply -var "do_token=${DO_PAT}"

出现提示时输入 yes。 完成后,您将从 droplet_names 列表中删除一个元素,以证明其他实例不会受到影响。 打开variables.tf进行编辑:

nano variables.tf

将列表修改为如下所示:

terraform-flexibility/variables.tf

variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

保存并关闭文件。

再次规划项目,您将收到以下输出:

Output...

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  - destroy

Terraform will perform the following actions:

  # digitalocean_droplet.test_droplet["zero"] will be destroyed
  - resource "digitalocean_droplet" "test_droplet" {
      ...
      - name               = "zero" -> null
      ...
    }

Plan: 0 to add, 0 to change, 1 to destroy.
...

这一次,Terraform 将仅销毁已移除的实例 (zero),而不会触及任何其他实例,这是正确的行为。

在这一步中,您已经了解了 for_each、如何使用它以及它相对于 count 的优势。 接下来,您将了解 for 循环、它的语法和用法,以及何时可以使用它来自动执行某些任务。

使用 for 循环

for 循环适用于集合,并通过对输入的每个元素应用转换来创建新集合。 输出的确切类型取决于循环是否被括号([])或大括号({})包围,分别给出一个列表或一个映射。 因此,它适用于查询资源并形成结构化输出以供以后处理。

for 循环的一般语法是:

for element in collection:
transform(element)
if condition

与其他编程语言类似,您首先命名遍历变量(element)并指定collection 进行枚举。 循环体是转换步骤,可选的 if 子句可用于过滤输入集合。

现在,您将使用输出来完成一些示例。 您将它们存储在一个名为 outputs.tf 的文件中。 通过运行以下命令创建它以进行编辑:

nano outputs.tf

添加以下行以输出已部署的 Droplet 名称及其 IP 地址对:

terraform-flexibility/outputs.tf

output "ip_addresses" {
  value = {
    for instance in digitalocean_droplet.test_droplet:
    instance.name => instance.ipv4_address
  }
}

此代码指定一个名为 ip_addresses 的输出,并指定一个 for 循环,该循环遍历您在前面步骤中自定义的 test_droplet 资源的实例。 因为循环被大括号包围,所以它的输出将是一个地图。 Map 的转换步骤类似于其他编程语言中的 lambda 函数,这里它通过将实例名称作为键及其私有 IP 作为其值来创建一个键值对。

保存并关闭文件,然后通过运行刷新 Terraform 状态以考虑新输出:

terraform refresh -var "do_token=${DO_PAT}"

Terraform refresh 命令使用云中的实际基础设施状态更新本地状态。

然后,检查输出的内容:

Outputip_addresses = {
  "first" = "ip_address"
  "fourth" = "ip_address"
  "second" = "ip_address"
  "third" = "ip_address"
}

Terraform 显示了 ip_addresses 输出的内容,它是由 for 循环构造的地图。 (条目的顺序对您来说可能不同。)循环将无缝地为每个条目数量工作——这意味着您可以向 droplet_names 列表和新的 Droplet 添加一个新元素,这将被创建没有任何进一步的手动输入,也会自动显示在此输出中。

通过将 for 循环括在方括号中,您可以使输出成为一个列表。 例如,您可以只输出 Droplet IP 地址,这对于可能正在解析数据的外部软件很有用。 代码如下所示:

terraform-flexibility/outputs.tf

output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
  ]
}

在这里,转换步骤选择 IP 地址属性。 它将给出以下输出:

Outputip_addresses = [
  "ip_address",
  "ip_address",
  "ip_address",
  "ip_address",
]

如前所述,您还可以使用 if 子句过滤输入集合。 以下是编写循环以按 fra1 区域过滤的方法:

terraform-flexibility/outputs.tf

output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
    if instance.region == "fra1"
  ]
}

在 HCL 中,== 运算符检查两侧值的相等性——这里它检查 instance.region 是否等于 fra1。 如果是,则检查通过并将 instance 转换并添加到输出中,否则将跳过它。 此代码的输出将与前面的示例相同,因为根据 test_droplet 资源定义,所有 Droplet 实例都在 fra1 区域中。 if 条件在您想要过滤项目中其他值的输入集合时也很有用,例如 Droplet 大小或分布。

因为您将在下一节中以不同的方式创建资源,所以通过运行以下命令来销毁当前部署的资源:

terraform destroy -var "do_token=${DO_PAT}"

提示完成该过程时输入 yes

我们已经了解了 for 循环、它的语法以及输出中的使用示例。 您现在将了解条件以及如何将它们与 count 一起使用。

指令和条件

在前面的部分中,您已经了解了 count 键及其工作原理。 您现在将了解可以在 Terraform 代码的其他地方使用的三元条件运算符,以及如何将它们与 count 一起使用。

三元运算符的语法是:

condition ? value_if_true : value_if_false

condition 是一个计算为布尔值(真或假)的表达式。 如果条件为真,则表达式的计算结果为 value_if_true。 另一方面,如果条件为假,则结果将是 value_if_false

三元运算符的主要用途是根据变量的内容启用或禁用单个资源的创建。 这可以通过将比较结果(10)传递给所需资源上的 count 键来实现。

如果您使用三元运算符从列表或集合中获取单个元素,则可以使用 one 函数。 如果给定集合为空,则返回 null。 否则,它返回集合中的单个元素,如果有多个则抛出错误。

让我们添加一个名为 create_droplet 的变量,它将控制是否创建 Droplet。 首先打开variables.tf进行编辑:

nano variables.tf

添加突出显示的行:

terraform-flexibility/variables.tf

variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

variable "create_droplet" {
  type = bool
  default = true
}

此代码定义了 bool 类型的 create_droplet 变量。 保存并关闭文件。

然后,要修改 Droplet 声明,请运行以下命令打开 droplets.tf 进行编辑:

nano droplets.tf

修改您的文件,如下所示:

terraform-flexibility/droplets.tf

resource "digitalocean_droplet" "test_droplet" {
  count  = var.create_droplet ? 1 : 0
  image  = "ubuntu-20-04-x64"
  name   =  "test_droplet"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

对于 count,如果 create_droplet 变量为 true,则使用三元运算符返回 1,如果为 false,则返回 0,这将导致没有 Droplets正在供应。 完成后保存并关闭文件。

通过运行将变量设置为 false 来规划项目执行计划:

terraform plan -var "do_token=${DO_PAT}" -var "create_droplet=false"

您将收到以下输出:

OutputChanges to Outputs:
  + ip_addresses = {}

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

因为create_droplet传入了false的值,所以实例的count0,不会创建Droplet,所以不会有IP要输出的地址。

您已经回顾了如何将三元条件运算符与 count 键一起使用,以便在选择是否部署所需资源时实现更高级别的灵活性。 接下来,您将了解如何为您的资源显式设置资源依赖项。

显式设置资源依赖关系

在为您的项目创建执行计划时,Terraform 会检测资源之间的依赖链并隐式排序它们,以便以适当的顺序构建它们。 在大多数情况下,它能够通过扫描资源中的所有表达式并构建图形来检测关系。

但是,当一个资源为了被供应而需要访问控制设置已经部署在云提供商处时,Terraform 没有明确的迹象表明它们是相关的。 反过来,Terraform 将不知道它们在行为上相互依赖。 在这种情况下,必须使用 depends_on 参数手动指定依赖关系。

depends_on 键在每个资源上都可用,用于指定特定资源之间的隐藏依赖链接。 当一个资源依赖于另一个人的行为时,隐藏的依赖关系就形成了,而在其声明中没有使用它的任何数据,这将促使 Terraform 以一种方式连接它们。

下面是如何在代码中指定 depends_on 的示例:

resource "digitalocean_droplet" "droplet" {
  image  = "ubuntu-20-04-x64"
  name   = "web"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  depends_on = [
    # Resources...
  ]
}

它接受对其他资源的引用列表,并且不接受任意表达式。

depends_on 应谨慎使用,并且仅在用尽所有其他选项时使用。 它的使用意味着您试图声明的内容超出了 Terraform 的自动依赖检测系统的边界; 它可能表示该资源显式地依赖于比它需要的更多的资源。

您现在已经了解了如何使用 depends_on 键为资源显式设置附加依赖项,以及何时应该使用它。

结论

在本文中,我们介绍了 HCL 提高代码灵活性和可扩展性的功能,例如 count 用于指定要部署的资源实例的数量,for_each 作为高级循环收集数据类型和自定义实例的方法。 如果使用得当,它们将大大减少代码重复和管理已部署基础架构的运营开销。

您还了解了条件和三元运算符,以及如何利用它们来控制是否部署资源。 虽然 Terraform 的自动依赖分析系统非常强大,但在某些情况下,您可能需要使用 depends_on 键手动指定资源依赖关系。

本教程是 如何使用 Terraform 管理基础架构系列的一部分。 该系列涵盖了许多 Terraform 主题,从首次安装 Terraform 到管理复杂的项目。