这篇是 infrastructure 也可以 for each 第四篇,上次漏发了,今天补发
本章介绍 terraform 的 for syntax 与 dynamic block,快速迭代 resource block
课程内容与代码会放在 Github 上: https://github.com/chechiachang/terraform-30-days
赛後文章会整理放到个人的部落格上 http://chechia.net/
For expression 是 terraform configuration language (hcl) 内的 syntax 语法
for each meta-argument 是
for_each
是在计算 resource block {} 的 meta 时使用https://www.terraform.io/docs/language/expressions/for.html
poolNameTuple = { for p in var.node_pools : name => p.name }
poolNameList = [
for p in var.node_pools : "p.name"
]
output "pool_name_list" {
value = [for p in var.node_pools : p.name]
}
两个语法的定义位阶不同
for_each = [for p in var.node_pools : p.name]
for 产出的是 list
for_each = [
var.node_pools["spot"].name
var.node_pools["on-demand"].name
...
]
一样需要考量 for each 的限制
请依照Terraform 官方文件 for example,尝试每个 example,以熟悉 for syntax 使用
想要管理很多类似的多个 resource block,我们可以参考使用 meta-argument,来管理 resource block
有个时候,一个 resource block 中,会有 repeatable nested blocks arguments,例如
storage_data_disk
resource "azurerm_virtual_machine" "main" {
name = "${var.prefix}-vm"
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"
storage_os_disk {
name = "myosdisk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
storage_data_disk {
name = "data1"
caching = "None"
create_option = "Attach"
disk_size_gb = "100"
}
storage_data_disk {
name = "data2"
caching = "None"
create_option = "Attach"
disk_size_gb = "200"
}
storage_data_disk {
name = "data3"
caching = "None"
create_option = "Attach"
disk_size_gb = "300"
}
...
}
这时候,是想要管理一个 block 中的 field block,让他一据 input 动态产生,这时可以使用 Dynamic block
以上面这个例子,可以改写成 dynamic blocks
locals {
storage_data_disks = [
{
name = "data1"
disk_size_gb = "100"
},
{
name = "data2"
disk_size_gb = "200"
},
{
name = "data3"
disk_size_gb = "300"
}
]
}
resource "azurerm_virtual_machine" "main" {
name = "${var.prefix}-vm"
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"
storage_os_disk {
name = "myosdisk1"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
dynamic "storage_data_disk" {
for_each = var.storage_data_disks
content {
name = storage_data_disk.value["name"]
caching = "None"
create_option = "Attach"
disk_size_gb = storage_data_disk.value["disk_size_gb"]
}
}
...
}
许多高阶程序语言有提供 nested loop,terraform dynamic block 也提供 nested
variable "load_balancer_origin_groups" {
type = map(object({
origins = set(object({
hostname = string
}))
}))
}
resource "load_balancer" "main" {
dynamic "origin_group" {
for_each = var.load_balancer_origin_groups
content {
name = origin_group.key
dynamic "origin" {
for_each = origin_group.value.origins
content {
hostname = origin.value.hostname
}
}
}
}
}
这个范例,在复杂的网路设定相关的 resource 很有机会看到。double nested dynamic block 的问题
通常比较好的 provider (例如三大公有云)会提供另外的 resource block 来管理这些 nested block
load_balancer
resourceorigin_group
resource熟 golang 的不妨看一下 source code
要管理重复的 resource block ,Terrafrom 还提供另一个 meta-argument count。范例
azurerm_virtual_machine
resource "azurerm_virtual_machine" "main" {
count = 3
name = "${var.prefix}-vm-${count.index}"
...
}
实际 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
Terraform 官方在 When to use count and for each 说明 count 与 for each 建议的使用时机
能用 for each 的时候就用 for each
熟 golang 的不妨看一下 source code
>>: EP 11: Passing Data for Navigation in TopStore App - II
首先 CentOS 7 做 mini install putty ssh 登入安装 LogAnaly...
这系列只是这两年自己的工作学习笔记 内容相当的杂, 但希望藉由这次的铁人赛期间顺便复习 一开始会复习...
在上一篇文章中,我们介绍作者如何分析MLB赛事,并找出影响比赛胜负较为重要的因子,而今天我们就来看看...
Dart是什麽? Dart 是一个静态强型别的语言,同时支援物件导向程序设计(OOP)及函数式程序设...
上一回,我提到 CC: Tweaked 的 Computer 方块有许多基础指令 但我不打算逐一介绍...