infrastructure 也可以 for each 之三
課程內容與代碼會放在 Github 上: https://github.com/chechiachang/terraform-30-days
賽後文章會整理放到個人的部落格上 http://chechia.net/
要管理重複的 resource block ,Terrafrom 還提供另一個 meta-argument count。範例
azurerm_virtual_machine
resource "azurerm_virtual_machine" "main" {
count = 3
name = "${var.prefix}-vm-${count.index}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
network_interface_ids = [azurerm_network_interface.main.id]
vm_size = "Standard_DS1_v2"
...
}
實際 output 會類似
azurerm_virtual_machine.main
,這邊會是一組 collection,可以使用 index 存取azurerm_virtual_machine.main[0]
azurerm_virtual_machine.main[1]
azurerm_virtual_machine.main[2]
resource "azurerm_virtual_machine" "main[0]" {
name = "${var.prefix}-vm-0"
...
}
resource "azurerm_virtual_machine" "main[1]" {
name = "${var.prefix}-vm-1"
...
}
resource "azurerm_virtual_machine" "main[2]" {
name = "${var.prefix}-vm-2"
...
}
Count 與 for each 是互斥的,意思是 resource block 中只能使用其中一個 meta-argument,一起使用的話會在 validate 出 syntax error
for each 使用 input variable 的 key 作為 key,count 使用則搭配 count.index,在 collection 取得參數值
首先是 node pool 範例,使用 for each meta-argument
# modules/kubernetes_cluster/node_pool.tf
resource "azurerm_kubernetes_cluster_node_pool" "main" {
for_each = var.node_pools
name = each.value.name
...
}
var.node_pools
# modules/kubernetes_cluster/node_pool.tf
resource "azurerm_kubernetes_cluster_node_pool" "main" {
count = length(var.node_pools)
name = var.node_pools[count.index].name
...
}
展開變成
# modules/kubernetes_cluster/node_pool.tf
resource "azurerm_kubernetes_cluster_node_pool" "main[0]" {
name = var.node_pools[0].name
...
}
resource "azurerm_kubernetes_cluster_node_pool" "main[1]" {
name = var.node_pools[1].name
...
}
注意:上面使用 count 與 for each 的取值方式不同,這裡會可能造成 count.index 的錯亂
var.node_pools
有改變, plan 的時候重新計算 resource block,便有可能導致順序錯亂Terraform 官方在 When to use count and for each 說明 count 與 for each 建議的使用時機,已經不建議如此使用 count 了
那為何 count 還是會存在?是歷史緣故 resource 中的 count 支援版本很早,for each 要到 0.12 之後的 terraform 版本才支援。也就是說,古人沒有 for each 可以用被迫使用 count + count.index
count 歷史悠久,可以分享一些常見的用法
variable "vm_names" {
default = ["vm-1", "vm-2", "vm-3"]
type = list
}
variable "vm_sizes" {
default = ["Standard_DS1_v2", "Standard_DS2_v2", "Standard_DS4_v2"]
type = list
}
resource "azurerm_virtual_machine" "main" {
count = length(var.vm_names)
name = var.vm_names[count.index]
vm_size = var.vm_sizes[count.index]
}
上面這個用法的問題
新版 terraform 請使用
vm_names
與 vm_sizes
合併成為單一 variablecount 可以接受 0 為參數,意思是就產生 0 個 resource block
resource "azurerm_virtual_machine" "main" {
count = 0
name = "${var.prefix}-vm-${count.index}"
...
}
這又產生了另外一個用法,來有條件的控制 resource block 與 module
variable "enable_azurerm_virtual_machine" {
type = bool
default = false
}
resource "azurerm_virtual_machine" "main" {
count = var.enable_azurerm_virtual_machine ? 1 : 0
name = "${var.prefix}-vm-${count.index}"
...
}
上面這個範例
enable_azurerm_virtual_machine
== true 的時候,會產生 count = 1,也就是產生一個 azurerm_virtual_machine
enable_azurerm_virtual_machine
== false 的時候,會產生 count = 0,也就是產生一個 azurerm_virtual_machine
實務上這樣子的使用情境還算蠻多的,一個 resource / module 啟用或不啟用
for_each
的 argument literate 下去是 empty 的話,for each 出來就會產生 0 個 resource / moduleTerraform 官方在 When to use count and for each 說明 count 與 for each 建議的使用時機
能用 for each 的時候就用 for each
熟 golang 的不妨看一下 source code