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

Terraform中基于JSON批量创建关联AWS资源的实现问题

实现Terraform关联多资源实例创建(解决count与for_each冲突)

需求

  • 第一类资源:按JSON文件的每个条目创建
  • 第二类资源:依赖第一类资源的ID,且每个JSON条目中的多个email值对应一个实例(即一个第一类资源对应多个第二类资源)

原始数据与基础配置

file.json内容

[
    {
        "name": "one",
        "env": "dev",
        "org": "IT",
        "email": "one@gmail.com, two@gmail.com"
    },
    {
        "name": "two",
        "env": "stg",
        "org": "HR",
        "email": "three@gmail.com, four@gmail.com, five@gmail.com"
    }
]

本地变量定义

locals {
    data = jsondecode(file("${path.module}/file.json"))
}

第一类资源(原配置)

resource "aws_sagemaker_domain" "domain" {
    count = length(local.data)
    domain_name = local.data[count.index].name
}

错误尝试与问题

原第二类资源配置混用count和for_each,触发报错:

resource "aws_sagemaker_user_profile" "user_profile" {
    count = length(local.data)
    domain_id = aws_sagemaker_domain.domain[count.index].id
    for_each = split(",", local.data[count.index].email)
    user_profile_name = each.value
}

the count and for_each meta-arguments are mutually-exclusive, only one should be used to be explicit about the number of resources to be created.

正确实现方式

1. 预处理数据为扁平化结构

先将原始数据展开,每个email对应一个包含所属domain信息的条目,再转换为带唯一键的map:

locals {
  data = jsondecode(file("${path.module}/file.json"))
  
  # 扁平化:拆分每个domain的email,生成单独用户条目
  flattened_users = flatten([
    for domain in local.data : [
      for email in split(", ", domain.email) : {
        domain_name = domain.name
        user_email  = trimspace(email)
      }
    ]
  ])
  
  # 转换为map,用"domain名-邮箱"作为唯一键,适配for_each
  user_profile_map = {
    for user in local.flattened_users : "${user.domain_name}-${user.user_email}" => user
  }
}

2. 优化第一类资源(改用for_each更稳定)

替换count为for_each,用domain的name作为唯一标识,避免条目顺序变动导致资源错误重建:

resource "aws_sagemaker_domain" "domain" {
  for_each = { for d in local.data : d.name => d }
  domain_name = each.value.name
}

3. 创建第二类资源

遍历预处理后的map,关联对应domain的ID:

resource "aws_sagemaker_user_profile" "user_profile" {
  for_each = local.user_profile_map

  domain_id         = aws_sagemaker_domain.domain[each.value.domain_name].id
  user_profile_name = each.value.user_email
}

说明

  • trimspace(email)用于清理split后email字符串的首尾空格,避免无效的用户名称
  • 用domain_name关联资源比count.index更可靠,当JSON条目顺序变动时,不会导致资源错误重建

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:55:31