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

Terraform首次运行创建AWS账号及SSO分配for_each报错修复方案

问题场景

需要在首次执行Terraform工作流时,一次性完成AWS组织账号创建与对应SSO账号分配配置,避免plan阶段因无法预判动态值触发for_each参数无效错误,报错信息为:

Invalid for_each argument local will be known only after apply

已尝试以下方案但均未解决问题:

  • 将aws_organizations_account资源与aws_ssoadmin_account_assignment资源拆分到完全独立的Terraform模块
  • 在相关资源、模块间添加depends_on显式依赖声明

当前使用Terraform版本为v1.2.4,报错截图如下:
Terraform for_each报错截图

涉及的原有配置代码:

子模块main.tf原有代码

resource "aws_organizations_account" "account" {
  name  = var.aws_account_name
  email = "${var.aws_account_name}@gmail.com"

  tags = {
    Name  = var.aws_account_name
  }

  parent_id = var.aws_org_folder_id
}

data "aws_identitystore_group" "this" {
  for_each          = local.group_list
  identity_store_id = local.identity_store_id

  filter {
    attribute_path  = "DisplayName"
    attribute_value = each.key
  }
}

data "aws_identitystore_user" "this" {
  for_each          = local.user_list
  identity_store_id = local.identity_store_id

  filter {
    attribute_path  = "UserName"
    attribute_value = each.key
  }
}

data "aws_ssoadmin_instances" "this" {}

locals {
  assignment_map = {
    for a in var.account_assignments :
    format("%v-%v-%v-%v", aws_organizations_account.account.id, substr(a.principal_type, 0, 1), a.principal_name, a.permission_set_name) => a
  }

  identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0]
  sso_instance_arn  = tolist(data.aws_ssoadmin_instances.this.arns)[0]

  group_list = toset([for mapping in var.account_assignments : mapping.principal_name if mapping.principal_type == "GROUP"])
  user_list  = toset([for mapping in var.account_assignments : mapping.principal_name if mapping.principal_type == "USER"])
}

resource "aws_ssoadmin_account_assignment" "this" {
  for_each = local.assignment_map

  instance_arn       = local.sso_instance_arn
  permission_set_arn = each.value.permission_set_arn

  principal_id   = each.value.principal_type == "GROUP" ? data.aws_identitystore_group.this[each.value.principal_name].id : data.aws_identitystore_user.this[each.value.principal_name].id
  principal_type = each.value.principal_type

  target_id   = aws_organizations_account.account.id
  target_type = "AWS_ACCOUNT"
}

根模块main.tf原有代码

module "sso_account_assignments" {
    source = "./modules/aws"

  account_assignments = [
    {
        permission_set_arn = "arn:aws:sso:::permissionSet/ssoins-0000000000000000/ps-31d20e5987f0ce66",
        permission_set_name = "ReadOnlyAccess",
        principal_type = "GROUP",
        principal_name = "Administrators"
    },
    {
        permission_set_arn = "arn:aws:sso:::permissionSet/ssoins-0000000000000000/ps-955c264e8f20fea3",
        permission_set_name = "ReadOnlyAccess",
        principal_type = "GROUP",
        principal_name = "Developers"
    },
    {
        permission_set_arn = "arn:aws:sso:::permissionSet/ssoins-0000000000000000/ps-31d20e5987f0ce66",
        permission_set_name = "ReadOnlyAccess",
        principal_type = "GROUP",
        principal_name = "Developers"
    },
  ]
}
问题根因

Terraform对for_each参数有硬性要求:plan阶段必须能确定for_each传入集合的所有键值,才能计算出需要管理的资源数量和地址。
原有配置中local.assignment_map的键拼接了aws_organizations_account.account.id,这个值是AWS账号创建完成后才会返回的动态值,首次执行plan阶段完全未知,直接导致for_each校验失败。
depends_on和模块拆分只能控制资源的执行先后顺序,无法让plan阶段提前拿到apply后才会生成的值,因此无法解决该问题。

最简最佳实践解决方案

不需要拆分模块、不需要加显式依赖,只需要修改local.assignment_map的键生成规则,全部使用plan阶段即可确定的静态字段作为map键,移除键中对动态账号ID的引用即可。

修改后的locals块代码如下:

locals {
  # 键仅使用入参中plan阶段就已知的静态字段,不再引用需apply后生成的账号ID
  assignment_map = {
    for a in var.account_assignments :
    format("%v-%v-%v", substr(a.principal_type, 0, 1), a.principal_name, a.permission_set_name) => a
  }

  identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0]
  sso_instance_arn  = tolist(data.aws_ssoadmin_instances.this.arns)[0]

  group_list = toset([for mapping in var.account_assignments : mapping.principal_name if mapping.principal_type == "GROUP"])
  user_list  = toset([for mapping in var.account_assignments : mapping.principal_name if mapping.principal_type == "USER"])
}

方案说明

  • Terraform仅要求for_each的键集合在plan阶段已知,资源参数中引用动态值是完全允许的。修改后aws_ssoadmin_account_assignment资源里的target_id依然引用aws_organizations_account.account.id,Terraform会自动处理依赖关系:等组织账号创建完成拿到真实ID后,再执行SSO分配资源的创建,无需手动声明depends_on。
  • 修改后首次执行terraform plan即可正常生成执行计划,terraform apply可以一次性完成账号创建和SSO分配全流程,不需要分步执行。
  • 如果后续扩展为批量创建多个组织账号,只需要把每个账号自定义的静态唯一标识(比如账号名称、业务编码)作为for_each键的一部分即可,永远不要将apply后才生成的动态资源ID作为for_each的键。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:39:17