如何用for_each根据tfvars映射的startup_script控制GCS Bucket Object部署
需求说明
需要通过for_each控制google_storage_bucket_object资源的部署,规则如下:
- 当
var.vm_map中实例的startup_script键不存在,或该键值不为false时,部署资源 - 当
startup_script明确为false时,跳过部署
示例vm_map变量定义:
vm_map = { fileserver = { name = "qlikviewserver" machine_type = "n1-standard-8" zone = "europe-west1-b" ip = "11.111.111.11" os = "windows" service = "qlikview" boot_disk_image = "windows-2016-base-latest" boot_disk_size = "120" boot_disk_type = "pd-ssd" policy = "daily" enable_display = true startup_script = false } }
错误尝试及问题分析
尝试1:使用contains判断键存在性
for_each = { for k, v in var.vm_map : k => v if !contains(keys(v), "startup_script") || v["startup_script"] != "false" }
触发报错:
Error: Invalid index
if !contains(keys(v), "startup_script") || v["startup_script"] != "false"
The given key does not identify an element in this collection value.
问题原因:Terraform的逻辑或运算会同时求值左右两边表达式,当startup_script键不存在时,右边的v["startup_script"]会直接触发索引错误,因为无法访问不存在的键。
尝试2:使用三元运算符结合can函数
for_each = { for k, v in var.vm_map : v["startup_script"] != "false" || !can(v["startup_script"]) ? k => v : null }
触发报错:
Error: Missing false expression in conditional
│
│ on google_compute_instance.tf line 50, in resource "google_storage_bucket_object" "script":
│ 50: v["startup_script"] != "false" || !can(v["startup_script"]) ? k => v : null
│
│ The conditional operator (...?...:...) requires a false expression, delimited by a colon.
╵╷
│ Error: Invalid 'for' expression
│
│ on google_compute_instance.tf line 50, in resource "google_storage_bucket_object" "script":
│ 48: for_each = {
│ 49: for k, v in var.vm_map :
│ 50: v["startup_script"] != "false" || !can(v["startup_script"]) ? k => v : null
│
│ Extra characters after the end of the 'for' expression.
问题原因:Terraform的for表达式语法不支持这种三元运算符的写法,正确的for过滤应该使用if子句,而非在键值对部分用三元判断。
正确实现方案
方案1:使用lookup函数(推荐)
lookup函数可以在键不存在时返回指定默认值,避免索引错误,同时简化条件判断:
resource "google_storage_bucket_object" "script" { for_each = { for k, v in var.vm_map : k => v if lookup(v, "startup_script", true) != false } // 其他资源配置... }
逻辑说明:
lookup(v, "startup_script", true):如果v中没有startup_script键,返回默认值true;如果存在则返回对应值- 条件
!= false:覆盖两种符合要求的情况——键不存在(默认true)、键存在且值不为false
方案2:使用can函数判断访问安全性
通过can函数确保只有当键存在时才访问它,避免索引错误:
resource "google_storage_bucket_object" "script" { for_each = { for k, v in var.vm_map : k => v if !can(v.startup_script) || v.startup_script != false } // 其他资源配置... }
逻辑说明:
!can(v.startup_script):当startup_script键不存在时,can返回false,取反后为true,满足条件- 逻辑或的短路特性:当左边为
true时,右边的v.startup_script != false不会被求值,避免了键不存在时的索引错误 - 当键存在时,判断其值是否不等于
false,满足则保留该实例
内容的提问来源于stack exchange,提问作者Wysong

