如何使用 Terraform 變量、依賴和條件性提高靈活性

引言

Hashicorp Configuration Language (HCL),Terraform所用之語言,提供許多在其他程式設計語言中也可见的實用結構與功能。在基礎設施代碼中使用循環可以大幅減少代碼重複,並提高可讀性,使未來的重構更簡單,也更加靈活。HCL也提供了一些常見的数据結構,如列表和映射(在其他人類中分別稱為數組和字典),以及用於執行路徑分岔的條件。

獨特的Terraform功能是可以手動指定依賴的資源。雖然在執行您的代碼時它已經建立了执行力圖(其中已經包含偵測到的链接,在大多數情境下都是正確的),但您可能會發現自己需要強制一個Terraform無法侦測到的依賴關係。

在本文中,我們將回顧HCL提供的數據結構、為資源提供的循環特性(count鍵、for_eachfor)、用於處理已知和未知值的條件,以及資源之間的依賴關係。

先決條件

  • 數位 ocean 個人密碼,可通过數位 ocean 控制面板创建。您可以參考 DigitalOcean 產品文件中的指示,如何創建個人密碼
  • 您已安裝 Terraform 於本地電腦上,並已设置好 DigitalOcean 提供者。請完成 使用 Terraform 的第一步第二步 教程,并将專案資料夾命名為 terraform-flexibility,而非 loadbalance。在 第二步 中,您不需要包含 pvt_key 變數和 SSH 密鑰資源。

注意: 本教程已特别測試於 Terraform 1.0.2

HCL 數據類型

在學習更多關於循环和其他功能之前,我們首先會介紹 HCL 可用的數據類型及其用途。

Hashicorp Configuration Language 支援 基本复合 數據類型。基本數據類型包括字串、數字和布林值,這些是不能從其他類型派生出來的基本類型。另一方面,复合類型則是將多個值組合成一個。這兩種复合值分為結構型和集合型。

結構型允許不同類型的值被組合在一起。主要例子是資源定義,你用來指定基礎設施看起來是什麼樣的。與結構型相比,集合型也是組合值,但只限於同類型的值。HCL 中我們關注的三種可用集合類型為列表、地圖和集合。

列表

列表在其他程式設計語言中的數組相似。它們包含已知數量的同類型元素,可以透過數組表示法([])利用整數索引,從 0 開始存取。以下是一個持有下一步驟中將部署的 Drops 名稱的列表變量宣告的例子:

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` 文件中。通過運行以下命令來創建並打開它進行編輯:

  1. 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將采取哪些动作:

  1. 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] 将被创建 + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } # digitalocean_droplet.test_droplet[1] 将被创建 + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } # digitalocean_droplet.test_droplet[2] 将被创建 + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } Plan: 3 to add, 0 to change, 0 to destroy. ...

透過 Terraform 將會創建三個实例名為 test_droplet ,但這並不是一個好主意,因為這樣會導致命名衝突。讓我們修改 Droplet 的定義,讓每個实例的名称都 uniquely 區別開來。開啟 droplets.tf 進行編輯:

  1. 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 屬性,該屬性會自動填入目前的 iterative 索引,從 0 開始。然後將這個索引用於构建 Droplet 的名稱,透過 字串補償來實現。你現在可以計劃该项目以查看更改:

  1. 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 鍵來创建多个資源的实例,以及如何在 provisioning 中fetch 和 use instance 的索引。下一節,你將會學習如何从列表中取 Droplet 的名。

從清單中獲取液滴名稱

當需要為同一個資源的多個實例指定自定義名稱時,您可以從您定義的清單變量中動態地獲取它們。在教程的後面,您將看到幾種從名稱清單中自動部署液滴的方法,這增加了靈活性和易用性。

您首先需要定義一個包含液滴名稱的清單。創建一個名為variables.tf的文件並打開它以進行編輯:

  1. nano variables.tf

添加以下行:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

保存並關閉文件。這行程式碼定義了一個名為droplet_names的清單,其中包含字符串firstsecondthirdfourth

打開droplets.tf以進行編輯:

  1. 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] 將被創建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "first" ... } # digitalocean_droplet.test_droplet[1] 將被創建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "second" ... } # digitalocean_droplet.test_droplet[2] 將被創建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "third" ... } # digitalocean_droplet.test_droplet[3] 將被創建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... Plan: 4 to add, 0 to change, 0 to destroy. ...

由於這些更改,將部署四台Droplet,分別的名稱依序取自droplet_names清單的元素。

您已經學習了count的作用、特性和語法,並已與清單一起使用它來修改資源實例。接下來您將看到它的缺點,以及如何克服它們。

了解count的缺點

現在您已經知道如何使用count,讓我們來研究當它被用於修改其所用於的清單時的缺點。

讓我們嘗試將Droplet部署到雲平台上:

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

當提示時,請輸入yes。您的輸出尾端將與以下类似:

Output
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

現在讓我們通過扩充droplet_names清單來創建另一台Droplet實例。打開variables.tf進行編輯:

  1. nano variables.tf

在清單的開頭新增一個新元素:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["zero", "first", "second", "third", "fourth"]
}

完成後,保存並關閉文件。

計劃專案:

  1. 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]將會在原地更新 ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "first" -> "zero" ... } # digitalocean_droplet.test_droplet[1]將會在原地更新 ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "second" -> "first" ... } # digitalocean_droplet.test_droplet[2]將會在原地更新 ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "third" -> "second" ... } # digitalocean_droplet.test_droplet[3]將會被创建 ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "fourth" -> "third" ... } # digitalocean_droplet.test_droplet[4]將會被创建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... } Plan: 1 to add, 4 to change, 0 to destroy. ...

输出表明Terraform会将前四个Droplets重命名,并创建一个名为fourth的第五个Droplet,因为Terraform将实例视为一个有序列表,并通过索引号来识别元素(Droplets)。这就是为什么Terraform最初认为这四个Droplets是这样的:

Index Number 0 1 2 3
Droplet Name first second third fourth

当在开头添加一个新的Droplet zero时,其内部列表表示看起来像这样:

Index Number 0 1 2 3 4
Droplet Name zero first second third fourth

最初的四个Droplets现在向右移动了一个位置。然后Terraform比较两个状态表:第一个表格中位于0的位置的Droplet叫做first,因为在第二个表格中它不同,所以计划了一个更新操作。这个过程一直持续到位置4,该位置在第一个表格中没有对应的项,而是在第二个表格中新添加的,因此计划了一个创建操作。

這表示除了最後一個元素以外,在任何位置添加新元素都會導致資源在被修改時不需要被改變。同樣的更新行動也會計劃如果從droplet_names列表中删除元素。

使用count來部署相同資源的不同实例的主要缺点是资源跟踪不完整。对于数量和类型都固定的情况,count是一个简单且有效的解决方案。但是,当某些属性是从变量获取时,例如在这种情况下,for_each循环是更好的选择,您将在本教程的后面部分学习到。

引用当前資源(self)

另一个缺点是,在某些情况下,不能通过索引引用资源列表中的任意一个实例。

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

以下為將上述内容轉譯為繁體中文:

為了示範用途,現在將為 test_droplet 定義添加一個 destroy-time 本地 provisioner,這會在執行時顯示一條訊息。開啟 droplets.tf 進行編輯:

  1. 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 提供者会在當地機器上執行命令。因為 when 参数被设置為 destroy,它只在資源即將被刪除的時候才會執行。它會執行一個命令,該命令會在当前資源的名稱上輸出字符串,使用 self.name

由於您將在下一節以不同的方式創建 Droplets,請刪除目前部署的所有 Droplets 執行以下命令:

  1. 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 loop 構造,它克服了這些缺點,並且適用於更廣泛的變數類型。

使用 for_each 循環

在這一節中,您將考慮 for_each 迴圈,其语法以及它是如何幫助定義具有多個实例的資源的。

for_each 是一個在每個資源上都可用的參數,但它與count不同,後者需要指定要創建的實例數量。for_each 接受一個映射或一個集合。提供的每個集合元素只會被遍歷一次,並為其創建一個實例。for_eacheach關鍵詞下的鍵和值作為屬性(對應的鍵和值作為 each.keyeach.value)。當提供一個集合時,鍵和值會是相同的。

由於它在each物件中提供當前元素,因此您不需要手動訪問想要元素的做法,就像以前對列表那樣。對於集合來說,這是無法實現的,因為內部並無法观测到任何排序。也可以傳入列表,但首先需要使用 toset 函數將其轉換為一個集合。

使用 for_each 的主要優點是,除了能夠枚舉這三种集合數據類型外,只有受影響的元素才会被修改、創建或刪除。如果您更改輸入元素中的元素順序,將不會計劃任何動作,而且如果您從輸入中添加、刪除或修改元素,只有對應的元素將會計劃合適的動作。

讓我們將 Droplet 資源從 count 改為 for_each 並看看它在實際中是如何工作的。通過運行以下命令打開 droplets.tf 進行編輯:

  1. 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 供应者。完成後,保存並關閉文件。

將上述内容轉換為繁體中文:

第一行replace了count并调用了for_each,将droplet_names列表以set的形式传递给toset函数,该函数自动将给定的输入转换为set。对于Droplet名称,您指定each.value,该值持有当前集合元素。

计划项目时运行:

  1. 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"]将被创建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "first" ... } # digitalocean_droplet.test_droplet["fourth"]将被创建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... } # digitalocean_droplet.test_droplet["second"]将被创建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "second" ... } # digitalocean_droplet.test_droplet["third"]将被创建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "third" ... } # digitalocean_droplet.test_droplet["zero"]将被创建 + resource "digitalocean_droplet" "test_droplet" { ... + name = "zero" ... } Plan: 5 to add, 0 to change, 0 to destroy. ...

与使用count不同,Terraform现在考虑每个实例都是单独的,而不是有序列表中的元素。每个实例都与给定集合中的一个元素相关联,如每个资源旁边的显示字符串元素所示,这些资源将被创建。

应用计划到云中,运行:

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

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

  1. 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"]将被销毁 - resource "digitalocean_droplet" "test_droplet" { ... - name = "zero" -> null ... } Plan: 0 to add, 0 to change, 1 to destroy. ...

这一次,Terraform只会销毁删除的实例(zero),而不会影响其他任何实例,这是正确的操作。

在此步骤中,您已经学习了关于for_each的使用方法、它的优点以及如何正确使用它。接下来,您将学习for循环的语法和用法,以及在哪些情况下可以使用它来自动化某些任务。

使用for循环

使用for循环时,其具体类型取决于是否被括号([])或花括号({})包围。如果是用方括号,它会返回一个列表;如果是用花括号,则会返回一个映射。因此,它适用于查询资源并在后续处理中形成结构化输出。

for循环的一般语法为:

for element in collection:
transform(element)
if condition

与其他程式設計語言相似,您首先命名遍歷變量(element)並指定要枚舉的collection。循環的主体是變換步驟,可选的if子句可以用來過濾輸入收集。

您現在將通過一些使用輸出的例子來工作进行。您將把它们存儲在命名為outputs.tf的文件中。通過運行以下命令為編輯創建它:

  1. 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資源實例的實例,您在之前的步驟中進行自定义。由於循環被大括號包圍,其輸出將是一個映射。映射的變換步驟與其他程式設計語言中的lambda函數類似,在此它通過將實例名稱作為鍵與其私有IP作為值組合來創建键值對。

保存並關閉文件,然後通過運行以下命令刷新Terraform狀態以 account for 新的輸出:

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

Terraform refresh命令將本地的狀態與雲中的實際基礎設施狀態更新。

然後,檢查输出的內容:

Output
ip_addresses
= { "first" = "ip_address" "fourth" = "ip_address" "second" = "ip_address" "third" = "ip_address" }

terraform 顯示了 ip_addresses 輸出內容,這是一個通过 for loop 構建的 MAP。loop 的順序可能與您的不同。loop 可以无缝地處理任何數量的entry——这意味着您可以將新的元素添加到 droplet_names 列表中,而无需任何進一步的手動輸入,新创建的Droplet也将自動出现在此输出中。

通過將 loop 括在方括號内,您可以將output 變成一個list。例如,如果您只想输出Droplet的IP地址,則可以使用以下代码:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
  ]
}

在此,transformational 步選擇了IP地址属性。它將給以下output:

Output
ip_addresses
= [ "ip_address", "ip_address", "ip_address", "ip_address", ]

如前所述,您也可以使用 if 子句對input collection進行filtering。下面是如何编写loop以filter by the fra1 region:

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 转换为输出,否则跳过。这段代码的output将与前面的例子相同,因为所有Droplet实例都在fra1区域,根据test_droplet资源定义。if条件也很有用,當您想在项目中过滤其他值時,如Droplet大小或分布。

因為你在下一節將會以不同的方式創建資源,所以請透過以下命令摧毁現有的部署:

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

輸入 yes 來完成这个过程。

我們已經討論過 for loop、其語法以及範例。現在,你將學習關於條件式,以及它們如何與 count 一起使用。

directive and Conditional(指令和條件)

In one of the previous sections, you’ve seen the count key and how it works. You’ll now learn about ternary conditional operators, which you can use elsewhere in your Terraform code, and how they can be used with count.

The syntax of the ternary operator is:

condition ? value_if_true : value_if_false

condition is an expression that computes to a boolean (true or false). If the condition is true, then the expression evaluates to value_if_true. On the other hand, if the condition is false, the result will be value_if_false.

The main use of ternary operators is to enable or disable single resource creation according to the contents of a variable. This can be achieved by passing in the result of the comparison (either 1 or 0) to the count key on the desired resource.

在你需要使用三元操作符从列表或集合中获取单个元素的情况下,你可以使用 one 函数。如果给定的集合是空的,它返回 null。否则,它返回集合中的单个元素,或者抛出一个错误,如果存在多个元素的话。

现在让我们添加一个变量叫做 create_droplet,这个变量将控制是否创建Droplet。首先,打开 variables.tf 进行编辑:

  1. 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
}

这段代码定义了 create_droplet 变量为 bool 类型。保存并关闭文件。

然后,为了修改Droplet声明,打开 droplets.tf 进行编辑,运行:

  1. 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,你使用了一个三元操作符来返回,如果是 true 就返回 1,如果是 false 就返回 0,这将会导致没有Droplets被创建。保存并关闭文件。

使用设置为false的变量执行项目计划,运行:

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

你会收到以下输出:

Output
Changes 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 偵測資源之間的依賴關係,並自動將它們排序為適當的顺序。在大多數情況下,它能夠透過掃描所有資源定義中的表达式來建立 graphs。

然而,當一個資源為了被 provisioning 而需要對雲服務提供者已經部署的访问控制设置時,就没有明顯的迹象告訴 Terraform 它们之間有行為上的依賴關係。因此,Terraform 将不知道它们應該依賴於彼此。在这种情况下,必須手动指定依賴關係使用 depends_on arguments。

depends_on 可在每個資源中使用,用於明確指定隱藏的依賴關係。隱藏的依賴關係發生當一個資源依赖于另一个资源的行为,而没有在其声明中使用其数据时,这会提示Terraform连接它们以另一种方式。

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

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

  depends_on = [
    資源…
  ]
}

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

depends_on 应该被谨慎使用,并且只应在所有其他选项都耗尽时使用。它的使用意味着您尝试声明的是超出 Terraform 的自动依赖检测系统边界的东西;它可能表明您正在尝试声明的资源超出了它所需的所有依赖关系。

你已经学会了如何使用 depends_on 键显式地设置附加依赖项,以及何时需要这样做。

結論

在本篇文章中,我们介绍了 HCL 的功能,这些功能改进了您的代码的灵活性和可伸缩性,例如使用 count 来指定要部署的资源实例的数量,以及使用高级循环操作符(如 for_each)来处理集合数据类型并自定义实例。当正确使用时,它们大大减少了管理已部署的基础设施的操作开销。

你也学习了条件语句和三元运算符,以及如何利用它们来控制是否部署资源。虽然 Terraform 的自动化依赖分析系统相当强大,但有时您可能需要手动指定资源之间的依赖关系,使用 depends_on 键。

本教程是如何使用Terraform管理基礎設施系列的一部分。該系列涵蓋了多個Terraform主題,從第一次安裝Terraform到管理複雜的項目。

Source:
https://www.digitalocean.com/community/tutorials/how-to-improve-flexibility-using-terraform-variables-dependencies-and-conditionals