Terraform嵌套变量处理报错:flatten/merge函数使用问题求助
Terraform嵌套映射创建存储账户及容器报错解决
变量定义(variables.tf)
variable "location" { type = string description = "Location for storage account resource." } variable "resource_group_name" { type = string description = "Resource group that will contain the resource." } variable "storage_account" { type = map(object({ name = string access_tier = string account_kind = string account_replication_type = string account_tier = string public_network_access_enabled = bool containers = optional(map(object({ name = string container_access_type = optional(string, "private") }))) network_rules = list(object({ default_action = string bypass = optional(set(string)) ip_rules = optional(set(string)) virtual_network_subnet_ids = optional(set(string)) })) })) }
模块代码(原错误版本)
resource "azurerm_storage_account" "storage" { for_each = var.storage_account name = each.value.name resource_group_name = var.resource_group_name location = var.location account_tier = each.value.account_tier account_replication_type = each.value.account_replication_type account_kind = each.value.account_kind access_tier = each.value.access_tier public_network_access_enabled = each.value.public_network_access_enabled != null ? each.value.public_network_access_enabled : false } locals { containers_list = flatten([ for key, value in var.storage_account : [ for container in value.containers : { name = container storage_account_name = azurerm_storage_account.storage[key].name } ] ]...) } resource "azurerm_storage_container" "storage" { #for_each = { for container in local.containers_list : container.name.name => container } for_each = tomap({ for container in local.containers_list : container.name.name => container }) name = each.value.container.name storage_account_name = each.value.storage_account_name }
模块调用代码
module "storage_account" { source = "./modules/azurerm_storage_account" resource_group_name = "rg-temp" location = "uksouth" tags = {} storage_account = { "storage01" = { name = "storagerandom0001" account_kind = "StorageV2" account_tier = "Standard" account_replication_type = "LRS" access_tier = "Hot" public_network_access_enabled = true network_rules = [] containers = { "container-aa" = { name = "random01" container_access_type = "private" }, "container-ab" = { name = "random02" container_access_type = "private" } } }, "storage02" = { name = "storagerandom0002" account_kind = "StorageV2" account_tier = "Standard" account_replication_type = "LRS" access_tier = "Hot" public_network_access_enabled = true network_rules = [] containers = { "container-01" = { name = "container01" container_access_type = "private" }, "container-02" = { name = "container02" container_access_type = "private" } } } } }
遇到的错误
使用flatten时的错误
│ Error: Too many function arguments │ │ on modules/azurerm_storage_account/main.tf line 17, in locals: │ 17: containers_list = flatten([ │ 18: for key, value in var.storage_account : [ │ 19: for container in value.containers : { │ 20: name = container │ 21: storage_account_name = azurerm_storage_account.storage[key].name │ 22: } │ 23: ] │ 24: ]...) │ ├──────────────── │ │ while calling flatten(list) │ │ azurerm_storage_account.storage is object with 2 attributes │ │ var.storage_account is map of object with 2 elements │ │ Function "flatten" expects only 1 argument(s).
使用merge时的错误
╷ │ Error: Error in function call │ │ on modules/azurerm_storage_account/main.tf line 17, in locals: │ 17: containers_list = merge([ │ 18: for key, value in var.storage_account : [ │ 19: for container in value.containers : { │ 20: name = container │ 21: storage_account_name = azurerm_storage_account.storage[key].name │ 22: } │ 23: ] │ 24: ]...) │ ├──────────────── │ │ while calling merge(maps...) │ │ azurerm_storage_account.storage is object with 2 attributes │ │ var.storage_account is map of object with 2 elements │ │ Call to function "merge" failed: arguments must be maps or objects, got "tuple". ╵
问题分析与解决方法
1. flatten函数参数错误
原代码里flatten([...])...多了末尾的...,flatten函数只接受一个嵌套列表参数,不需要用展开符号把列表拆成多个参数。直接去掉...即可解决这个语法错误。
2. 容器循环的结构与逻辑问题
- 原代码遍历
value.containers时,直接拿container是容器对象,但构造的name字段存了整个对象,后续引用会混乱。 - 没有处理
containers是可选参数的情况,如果某个存储账户没定义容器,会直接报错。 - 生成容器实例的唯一键时,仅用容器名称可能重复(不同存储账户下同名容器),不符合Terraform对
for_each键唯一性的要求。
修正后的模块代码
resource "azurerm_storage_account" "storage" { for_each = var.storage_account name = each.value.name resource_group_name = var.resource_group_name location = var.location account_tier = each.value.account_tier account_replication_type = each.value.account_replication_type account_kind = each.value.account_kind access_tier = each.value.access_tier # 变量已定义为bool类型,无需判断null public_network_access_enabled = each.value.public_network_access_enabled } locals { containers_list = flatten([ for sa_key, sa_value in var.storage_account : [ # 遍历容器的键值对,同时记录存储账户标识 for container_key, container_value in sa_value.containers : { storage_account_key = sa_key storage_account_name = azurerm_storage_account.storage[sa_key].name container_key = container_key container_name = container_value.name access_type = container_value.container_access_type } # 仅当containers存在时才生成数据,避免空值报错 if sa_value.containers != null ] ]) } resource "azurerm_storage_container" "storage" { # 用「存储账户键-容器键」作为唯一标识,避免同名冲突 for_each = { for container in local.containers_list : "${container.storage_account_key}-${container.container_key}" => container } name = each.value.container_name storage_account_name = each.value.storage_account_name container_access_type = each.value.access_type }
修正说明
- 去掉flatten后的
...,符合函数参数要求。 - 遍历容器时同时获取
container_key和container_value,生成包含所有必要属性的结构化数据。 - 添加
if sa_value.containers != null判断,兼容未定义容器的存储账户。 - 用
存储账户键-容器键作为容器资源的唯一键,确保每个实例唯一,避免Terraform报错。 - 简化
public_network_access_enabled的赋值,因为变量类型是bool,不会为null。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

