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

Terraform多层Map遍历优化:层级项目资源创建方案问询

问题描述

我在Terraform中定义了一个多层Map类型的变量blueprint_contents,将其视为文件夹结构,需要为每一层的每个条目创建对应的cloud_project资源,且必须保证层级创建顺序:L1项目先于L2创建,L1作为L2的父级,以此类推。当前已通过以下方式实现需求,现咨询是否有更优实现方案,可按需调整变量数据类型。


现有实现代码

本地列表定义

l1_projects_list = flatten([
    for l1_project, l2_project in var.blueprint_contents :
      {
        project_name   = l1_project
        parent = "top_level"
      }
  ])

l2_projects_list = flatten([
    for l1_project_name, l2_project in var.blueprint_contents : [
      for l2_project_name, l3_project in l2_project : {
        project_name   = l2_project_name
        parent = l1_project_name
      }
    ]
  ])

l3_projects_list = flatten([
    for l1_project_name, l2_project in var.blueprint_contents : [
      for l2_project_name, l3_project in l2_project : [
        for l3_project_name, l4_project in l3_project : {
          project_name = l3_project_name
          parent = l2_project_name
        }
      ]
    ]
  ])

l4_projects_list = flatten([
    for l1_project_name, l2_project in var.blueprint_contents : [
      for l2_project_name, l3_project in l2_project : [
        for l3_project_name, l4_project in l3_project : [
          for l4_project_name, l5_project in l4_project : {
            project_name = l4_project_name
            parent = l3_project_name
          }
        ]
      ]
    ]
  ])

资源创建代码

resource "cloud_project" "l1_projects" {
  for_each = { for project in local.l1_projects_list : project.project_name => project }
  name                = each.key
}

resource "cloud_project" "l2_projects" {
  for_each = { for project in local.l2_projects_list : project.project_name => project }
  name                = each.key
  parent_project_id = cloud_project.l1_projects[each.value.parent].id

  depends_on = [cloud_project.l1_projects]
}

resource "cloud_project" "l3_projects" {
  for_each = { for project in local.l3_projects_list : project.project_name => project }
  name                = each.key
  parent_project_id = cloud_project.l2_projects[each.value.parent].id

  depends_on = [cloud_project.l2_projects]
}

resource "cloud_project" "l4_projects" {
  for_each = { for project in local.l4_projects_list : project.project_name => project }
  name                = each.key
  parent_project_id = cloud_project.l3_projects[each.value.parent].id

  depends_on = [cloud_project.l3_projects]
}

变量定义

variable "blueprint_contents" {
  type = map(any)
  default = {
    "0. Templates" = {}

    "1. Highway" = {
      "Data" = {
        "External" = {
          "One" = {}
          "TWO"  = {}
          "Three" = {}
          "Four"  = {}
          "Five" = {}
          "Six"  = {}
        }

        "Internal" = {}
      }
      "Color" = {
        "Red" = {}
      }

      "Shape"           = {}
      "Dimensions" = {}
    }

    "2. Adhoc"      = {}
    "3. Archive"    = {}
    "4. Quarantine" = {}
  }
}

优化方案

现有方案硬编码了层级上限(仅支持4层),后续层级扩展需重复修改代码。可以通过递归展开层级结构+单一资源块的方式实现,支持任意层级,同时自动维护依赖关系。

步骤1:递归展开所有项目层级

定义递归本地值,将多层Map结构展开为包含project_name和parent的结构化列表:

locals {
  # 递归函数:展开任意层级的项目结构
  flatten_projects = func(projects map(any), parent_name string) {
    return flatten([
      for name, children in projects : concat(
        [
          {
            project_name = name
            parent       = parent_name
          }
        ],
        # 若当前项目有子项目,递归展开下一层
        length(children) > 0 ? local.flatten_projects(children, name) : []
      )
    ])
  }

  # 调用递归函数,顶层项目父级设为"top_level"
  all_projects = local.flatten_projects(var.blueprint_contents, "top_level")

  # 转换为map,适配资源for_each要求(需保证项目名全局唯一)
  all_projects_map = { for p in local.all_projects : p.project_name => p }
}

步骤2:单一资源块创建所有项目

利用Terraform的隐式依赖推断,通过父项目ID的引用自动保证创建顺序,无需手动添加depends_on:

resource "cloud_project" "all" {
  for_each = local.all_projects_map

  name                = each.key
  # 顶层项目无父ID,非顶层项目引用对应父项目的ID
  parent_project_id = each.value.parent == "top_level" ? null : cloud_project.all[each.value.parent].id
}

关键优势

  1. 支持任意层级:无需修改代码即可适配项目结构的层级扩展
  2. 自动维护依赖:Terraform会自动识别父项目ID的引用关系,确保父项目优先创建
  3. 代码简洁易维护:消除重复的层级处理逻辑,降低后续维护成本

注意事项

如果存在同名项目(不同层级下的项目名称重复),for_each会因为键冲突报错。可通过添加full_path字段保证唯一性:

locals {
  flatten_projects = func(projects map(any), parent_name string, parent_path string) {
    return flatten([
      for name, children in projects :
        let full_path = parent_path != "" ? "${parent_path}/${name}" : name
        concat(
          [
            {
              project_name = name
              parent       = parent_name
              full_path    = full_path
            }
          ],
          length(children) > 0 ? local.flatten_projects(children, name, full_path) : []
        )
    ])
  }

  all_projects = local.flatten_projects(var.blueprint_contents, "top_level", "")
  all_projects_map = { for p in local.all_projects : p.full_path => p }
}

resource "cloud_project" "all" {
  for_each = local.all_projects_map

  name                = each.value.project_name
  parent_project_id = each.value.parent == "top_level" ? null : cloud_project.all[
    [for p in local.all_projects : p.full_path if p.project_name == each.value.parent][0]
  ].id
}

内容的提问来源于stack exchange,提问作者14578446

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:50:53