

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 了解 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)、[length](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"))
}
```

## 表達式
<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 呼叫[潑濺表達](https://developer.hashicorp.com/terraform/language/expressions/splat)式。星號會導致 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 也有一些*中繼引數*，其作用方式與引數相同，但具有一些額外的功能：
+ [depends\_on](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)中繼引數可讓您自訂資源設定，類似於 CloudFormation 中的[移除](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)政策。

其他中繼引數允許將函數和表達式功能直接新增至資源。例如，[計數](https://developer.hashicorp.com/terraform/language/meta-arguments/count)中繼引數是同時建立多個類似資源的實用機制。下列範例示範如何在不使用中繼引數的情況下建立兩個 Amazon Elastic Container Service (Amazon EKS) `count` 叢集。

```
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\_each](https://developer.hashicorp.com/terraform/language/meta-arguments/for_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、木桿，另一個用於 chihuahua 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 存取計數中的迴圈索引一樣，您可以使用每個物件存取 for\_each 迴圈中每個項目的索引鍵和值。由於 for\_each 會逐一查看清單和物件，因此每個索引鍵和值都可能會稍微令人困惑地追蹤。下表顯示您可以使用 for\_each 中繼引數的不同方式，以及如何在每次反覆運算時參考這些值。


****  

| 範例 | `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
}
```