You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从两种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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:57:16