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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 09:07:03