从两种Terraform变量提取Azure Key Vault ID失败问题排查
问题:Terraform提取Azure Key Vault ID报错及解决
变量定义
第一类:object()类型变量
variable "var1" { type = object({ host_name = string key_vault_id = optional(string) }) default = { host_name = "api.example.com" key_vault_id = "https://keyvault1.vault.azure.net/secrets/example-cert1" } } variable "var2" { type = object({ host_name = string key_vault_id = optional(string) }) default = { host_name = "portal.example.com" key_vault_id = "https://keyvault2.vault.azure.net/secrets/example-cert2" } }
第二类:map(object())类型变量
variable "var3" { type = map(object({ value = string secret = bool secret_id = optional(string) identity_client_id = optional(string) })) default = { "my-named-value-1" = { value = "" secret = true secret_id = "https://keyvault3.vault.azure.net/secrets/secret1003" } "my-named-value-2" = { value = "" secret = true secret_id = "https://keyvault4.vault.azure.net/secrets/secret1003" } } }
当前提取代码及报错
提取代码
locals { key_vault_names = distinct( compact( concat( [ var.var1!= null ? regex("https://([a-zA-Z0-9-]{3,24})", var.var1.key_vault_id) : null, var.var2!= null ? regex("https://([a-zA-Z0-9-]{3,24})", var.var2.key_vault_id) : null ], [ for value in values(var.var3) : regex("https://([^.]+).vault.azure.net", value.secret_id) if value.secret_id != "" ] ) ) ) } data "azurerm_key_vault" "key_vaults" { for_each = toset(local.key_vault_names) name = each.key resource_group_name = var.resource_group_name } locals { key_vault_ids = [ for kv in data.azurerm_key_vault.key_vaults : kv.id ] }
报错信息
Error: Invalid function argument │ │ │ 26: compact( │ 27: concat( │ 28: [ │ 29: var.var1!= null ? regex("https://([a-zA-Z0-9-]{3,24})", var.var1.key_vault_id) : null, │ 30: var.var2!= null ? regex("https://([a-zA-Z0-9-]{3,24})", var.var2.key_vault_id) : null │ 32: ], │ 33: # [ │ 34: # for value in values(var.var3) : regex("https://([^.]+).vault.azure.net", value.secret_id) if value.secret_id != "" │ 35: # ] │ 36: ) │ 37: ) │ ├──────────────── │ │ while calling compact(list) │ │ var.var2 is a object │ │ var.var2.key_vault_id is a string │ │ var.var1 is a object │ │ var.var1.key_vault_id is a string │ │ var.var3 is map of object with 2 elements │ │ Invalid value for "list" parameter: element 0: string required. ╵ Operation failed: failed running terraform plan (exit 1)
报错原因
regex函数在包含捕获组时,返回的是字符串列表(每个捕获组对应一个元素),而非单个字符串。原代码中concat后的数组包含列表类型元素,而compact函数要求入参列表的元素必须是字符串或null,因此触发类型错误。
此外,原判断条件var.var1!= null无意义:因为var1是object类型且有默认值,永远不会为null,应该判断key_vault_id是否为空字符串(因该字段是optional(string),可能被显式设置为空)。
解决方案
修正后的提取代码
使用element(regex(...), 0)提取捕获组的第一个结果(即Key Vault名称),确保返回单个字符串;同时调整判断条件为检查key_vault_id是否非空:
locals { key_vault_names = distinct( compact( concat( [ # 检查key_vault_id非空,提取名称 var.var1.key_vault_id != "" ? element(regex("https://([a-zA-Z0-9-]{3,24})", var.var1.key_vault_id), 0) : null, var.var2.key_vault_id != "" ? element(regex("https://([a-zA-Z0-9-]{3,24})", var.var2.key_vault_id), 0) : null ], [ # 遍历var3,提取有效secret_id中的Key Vault名称 for value in values(var.var3) : element(regex("https://([^.]+).vault.azure.net", value.secret_id), 0) if value.secret_id != "" ] ) ) ) } data "azurerm_key_vault" "key_vaults" { for_each = toset(local.key_vault_names) name = each.key resource_group_name = var.resource_group_name } locals { key_vault_ids = [ for kv in data.azurerm_key_vault.key_vaults : kv.id ] }
更简洁的替代方案(使用replace)
如果不需要严格的格式校验,可直接用replace函数替换掉URL中多余部分,代码更直观:
locals { key_vault_names = distinct( compact( concat( [ var.var1.key_vault_id != "" ? replace(var.var1.key_vault_id, "^https://([a-zA-Z0-9-]+)\\..*$", "$1") : null, var.var2.key_vault_id != "" ? replace(var.var2.key_vault_id, "^https://([a-zA-Z0-9-]+)\\..*$", "$1") : null ], [ for value in values(var.var3) : replace(value.secret_id, "^https://([a-zA-Z0-9-]+)\\..*$", "$1") if value.secret_id != "" ] ) ) ) }
验证逻辑
修正后,concat生成的列表元素均为字符串或null,compact会过滤掉null值,distinct去重后得到唯一的Key Vault名称列表,最终通过data.azurerm_key_vault获取对应的ID。
内容的提问来源于stack exchange,提问作者mystack
相关产品推荐
相关产品推荐

