

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 了解 Terraform 函数、表达式和元参数
<a name="functions-expressions"></a>

对使用声明式配置文件而不是常见编程语言的 IaC 工具的一种批评是，它们使实现自定义编程逻辑变得更加困难。在 Terraform 配置中，这个问题是通过使用函数、表达式和元参数来解决的。

## 函数
<a name="functions"></a>

使用代码来配置基础架构的最大优势之一是能够存储常见的工作流程并一次又一次地重复使用它们，通常每次都传递不同的参数。Terraform 函数与 AWS CloudFormation [内部函数](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html)类似，尽管它们的语法与编程语言中函数的调用方式更为相似。在本指南的示例中，你可能已经注意到一些 Terraform 函数，例如 [substr](https://developer.hashicorp.com/terraform/language/functions/substr)、[concat](https://developer.hashicorp.com/terraform/language/functions/concat)、lengt [h](https://developer.hashicorp.com/terraform/language/functions/length) 和 [base64decode](https://developer.hashicorp.com/terraform/language/functions/base64decode)。 CloudFormation 与内部函数一样，Terraform 有一系列可在您的配置中使用的[内置函数](https://developer.hashicorp.com/terraform/language/functions)。例如，如果特定的资源属性采用一个非常大的 JSON 对象，而直接粘贴到文件中效率低下，则可以将该对象放在 **.json** 文件中，然后使用 Terraform 函数对其进行访问。在以下示例中，该`file`函数以字符串形式返回文件内容，然后该`jsondecode`函数将其转换为对象类型。

```
resource "example_resource" "example_resource_name" {
  json_object = jsondecode(file("/path/to/file.json"))
}
```

## Expressions
<a name="expressions"></a>

Terraform 还允许使用[条件表达式](https://developer.hashicorp.com/terraform/language/expressions/conditionals)，条件表达式与 CloudFormation `condition`函数类似，不同之处在于它们使用更传统的[三元](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator)运算符语法。在以下示例中，两个表达式返回的结果完全相同。第二个例子是 Terraform 所说的 s [pl](https://developer.hashicorp.com/terraform/language/expressions/splat) at 表达式。星号会让 Terraform 循环浏览列表并仅使用每个项目的`id`属性来创建一个新列表。

```
resource "example_resource" "example_resource_name" {
  boolean_value  = var.value ? true : false
  numeric_value  = var.value > 0 ? 1 : 0
  string_value   = var.value == "change_me" ? "New value" : var.value
  string_value_2 = var.value != "change_me" ? var.value : "New value"
}
There are two ways to express for loops in a Terraform configuration:
resource "example_resource" "example_resource_name" {
  list_value   = [for object in var.ids : object.id]
  list_value_2 = var.ids[*].id
}
```

## 元参数
<a name="meta-arguments"></a>

在前面的代码示例中，`list_value`和`list_value_2`被称为*参数*。你可能已经熟悉其中一些元参数了。Terraform 还有一些*元参数，它们的作用就像参数*一样，但有一些额外的功能：
+ [de [pends\$1on](https://developer.hashicorp.com/terraform/language/meta-arguments/depends_on) 元参数与该属性非常相似。 CloudFormation DependsOn](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html)
+ [提供](https://developer.hashicorp.com/terraform/language/meta-arguments#provider)者元参数允许您同时使用多个提供程序配置。
+ [生命周期](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle)元参数允许您自定义资源设置，类似于中的[删除和[删除](https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk/RemovalPolicy.html)](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html)策略。 CloudFormation

其他元参数允许将函数和表达式功能直接添加到资源中。例如，[count](https://developer.hashicorp.com/terraform/language/meta-arguments/count) 元参数是同时创建多个相似资源的有用机制。以下示例演示了如何在不使用`count`元参数的情况下创建两个亚马逊弹性容器服务 (Amazon EKS) 集群。

```
resource "aws_eks_cluster" "example_0" {
  name     = "example_0"
  role_arn = aws_iam_role.cluster_role.arn
  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true
    subnet_ids              = var.subnet_ids[0]
  }
}

resource "aws_eks_cluster" "example_1" {
  name     = "example_1"
  role_arn = aws_iam_role.cluster_role.arn
  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true
    subnet_ids              = var.subnet_ids[1]
  }
}
```

以下示例演示如何使用`count`元参数创建两个 Amazon EKS 集群。

```
resource "aws_eks_cluster" "clusters" {
  count    = 2
  name     = "cluster_${count.index}"
  role_arn = aws_iam_role.cluster_role.arn
  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true
    subnet_ids              = var.subnet_ids[count.index]
  }
}
```

要为每个单位命名，您可以访问资源块中的列表索引，网址为`count.index`。但是，如果你想创建多个稍微复杂一点的类似资源，该怎么办？ 这就是 [for\$1](https://developer.hashicorp.com/terraform/language/meta-arguments/for_each) each 元参数的用武之地。`for_each`元参数与非常相似`count`，不同之处在于你传入的是列表或对象，而不是数字。Terraform 为列表或对象的每个成员创建一个新资源。它与您设置时类似`count = length(list)`，不同之处在于您可以访问列表的内容而不是循环索引。

这既适用于项目列表，也适用于单个对象。以下示例将创建两个以`id-0`和`id-1`为其的资源 IDs。

```
variable "ids" {
  default = [
    { id = "id-0" },
    { id = "id-1" },
  ]
}

resource "example_resource" "example_resource_name" {
  # If your list fails, you might have to call "toset" on it to convert it to a set
  for_each = toset(var.ids)
  id       = each.value
}
```

以下示例还将创建两个资源，一个用于贵宾犬 Sparky，另一个用于吉娃娃 Fluffy。

```
variable "dogs" {
  default = {
    poodle    = "Sparky"
    chihuahua = "Fluffy"
  }
}

resource "example_resource" "example_resource_name" {
  for_each = var.dogs
  breed    = each.key
  name     = each.value
}
```

就像你可以使用 count.index 访问 count.index 中的循环索引一样，你可以使用每个对象访问 for\$1each 循环中每个项目的键和值。由于 for\$1each 会迭代列表和对象，因此跟踪每个键和值可能会有些混乱。下表显示了使用 for\$1each 元参数的不同方式以及如何在每次迭代时引用这些值。


****  

| 示例 | `for_each` 类型 | 第一次迭代 | 第二次迭代 | 
| --- | --- | --- | --- | 
| A | <pre>["poodle", "chihuahua"]</pre> | <pre>each.key = "poodle"<br /><br />each.value = null</pre> | <pre>each.key = "chihuahua"<br /><br />each.value = null</pre> | 
| B | <pre>[<br /><br />{<br /><br />type = "poodle",<br /><br />name = "Sparky"<br /><br />},<br /><br />{<br /><br />type = "chihuahua",<br /><br />name = "Fluffy"<br /><br />}<br /><br />]</pre> | <pre>each.key = {<br /><br />type = "poodle",<br /><br />name = "Sparky"<br /><br />}<br /><br />each.value = null</pre> | <pre>each.key = {<br /><br />type = "chihuahua",<br /><br />name = "Fluffy"<br /><br />}<br /><br />each.value = null</pre> | 
| C | <pre>{<br /><br />poodle = "Sparky",<br /><br />chihuahua = "Fluffy"<br /><br />}</pre> | <pre>each.key = "poodle"<br /><br />each.value = "Sparky"</pre> | <pre>each.key = "chihuahua"<br /><br />each.value = "Fluffy"</pre> | 
| D | <pre>{<br /><br />dogs = {<br /><br />poodle = "Sparky",<br /><br />chihuahua = "Fluffy"<br /><br />},<br /><br />cats = {<br /><br />persian = "Felix",<br /><br />burmese = "Morris"<br /><br />}<br /><br />}</pre> | <pre>each.key = "dogs"<br /><br />each.value = {<br /><br />poodle = "Sparky",<br /><br />chihuahua = "Fluffy"<br /><br />}</pre> | <pre>each.key = "cats"<br /><br />each.value = {<br /><br />persian = "Felix",<br /><br />burmese = "Morris"<br /><br />}</pre> | 
| E | <pre>{<br /><br />dogs = [<br /><br />{<br /><br />type = "poodle",<br /><br />name = "Sparky"<br /><br />},<br /><br />{<br /><br />type = "chihuahua",<br /><br />name = "Fluffy"<br /><br />}<br /><br />],<br /><br />cats = [<br /><br />{<br /><br />type = "persian",<br /><br />name = "Felix"<br /><br />},<br /><br />{<br /><br />type = "burmese",<br /><br />name = "Morris"<br /><br />}<br /><br />]<br /><br />}</pre> | <pre>each.key = "dogs"<br /><br />each.value = [<br /><br />{<br /><br />type = "poodle",<br /><br />name = "Sparky"<br /><br />},<br /><br />{<br /><br />type = "chihuahua",<br /><br />name = "Fluffy"<br /><br />}<br /><br />]</pre> | <pre>each.key = "cats"<br /><br />each.value = [<br /><br />{<br /><br />type = "persian",<br /><br />name = "Felix"<br /><br />},<br /><br />{<br /><br />type = "burmese",<br /><br />name = "Morris"<br /><br />}<br /><br />]</pre> | 

 

因此`var.animals`，如果等于行 E，则可以使用以下代码为每只动物创建一个资源。

```
resource "example_resource" "example_resource_name" {
  for_each = var.animals
  type     = each.key
  breeds   = each.value[*].type
  names    = each.value[*].name
}
```

或者，您可以使用以下代码为每只动物创建两个资源。

```
resource "example_resource" "example_resource_name" {
  for_each = var.animals.dogs
  type     = "dogs"
  breeds   = each.value.type
  names    = each.value.name
}

resource "example_resource" "example_resource_name" {
  for_each = var.animals.cats
  type     = "cats"
  breeds   = each.value.type
  names    = each.value.name
}
```