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

Terraform foreach模块未按预期串行执行,parallelism=1无效

Terraform foreach模块实例执行顺序不符合预期问题

问题概述

使用for_each调用自定义Terraform模块时,即使指定terraform apply -parallelism=1,Terraform并未按预期执行——没有等待一个模块实例的所有资源创建完成再启动下一个实例,而是先批量创建所有实例的google_project_iam_member.first_me,再依次批量处理后续资源。需要实现单个模块实例全流程完成后再启动下一个实例的串行执行逻辑。

相关代码

模块文件 templates/iam.tf

variable "project_id2" {}
variable "project_id" {}
variable "user" {}
variable "role" {}

resource "google_project_iam_member" "first_me" {
  project = var.project_id2
  role    = var.role
  member  = "user:${var.user}"
}

resource "google_project_iam_member" "then_me" {
  project = var.project_id
  role    = var.role
  member  = "user:${var.user}"
  depends_on = [
    google_project_iam_member.first_me
  ]
}

resource "time_sleep" "wait_seconds" {
  create_duration = "11s"

  depends_on = [google_project_iam_member.then_me]
}

resource "null_resource" "sleep_after_project_import" {
  depends_on = [
    time_sleep.wait_seconds,
    google_project_iam_member.then_me
  ]
}

主文件 main.tf

locals {
  roles_for_bindings = [
    "roles/accessapproval.invalidator",
    "roles/actions.Viewer",
    "roles/serviceconsumermanagement.tenancyUnitsAdmin",
  ]
}

module "role_binding_sleep_test" {
  source     = "./templates/iam"
  for_each   = toset(local.roles_for_bindings)
  project_id = "some_project_id"
  project_id2 = "some_project_id2"
  role       = each.key
  user       = "test@test.com"
}

实际执行日志

module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.first_me: Creation complete after 9s [id=some_project_id2/roles/accessapproval.invalidator/user:test@test.com]
module.role_binding_sleep_test["roles/actions.Viewer"].google_project_iam_member.first_me: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].google_project_iam_member.first_me: Creation complete after 9s [id=some_project_id2/roles/actions.Viewer/user:test@test.com]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.first_me: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.first_me: Creation complete after 9s [id=some_project_id2/roles/serviceconsumermanagement.tenancyUnitsAdmin/user:test@test.com]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.then_me: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.then_me: Creation complete after 9s [id=some_project_id1/roles/accessapproval.invalidator/user:test@test.com]
module.role_binding_sleep_test["roles/actions.Viewer"].google_project_iam_member.then_me: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].google_project_iam_member.then_me: Creation complete after 9s [id=some_project_id1/roles/actions.Viewer/user:test@test.com]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.then_me: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.then_me: Creation complete after 10s [id=some_project_id1/roles/serviceconsumermanagement.tenancyUnitsAdmin/user:test@test.com]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].time_sleep.wait_seconds: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].time_sleep.wait_seconds: Still creating... [10s elapsed]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].time_sleep.wait_seconds: Creation complete after 11s [id=2023-12-04T13:22:47Z]
module.role_binding_sleep_test["roles/actions.Viewer"].time_sleep.wait_seconds: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].time_sleep.wait_seconds: Still creating... [10s elapsed]
module.role_binding_sleep_test["roles/actions.Viewer"].time_sleep.wait_seconds: Creation complete after 11s [id=2023-12-04T13:22:59Z]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].time_sleep.wait_seconds: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].time_sleep.wait_seconds: Still creating... [10s elapsed]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].time_sleep.wait_seconds: Creation complete after 11s [id=2023-12-04T13:23:10Z]
module.role_binding_sleep_test["roles/actions.Viewer"].null_resource.sleep_after_project_import: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].null_resource.sleep_after_project_import: Creation complete after 0s [id=111111]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].null_resource.sleep_after_project_import: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].null_resource.sleep_after_project_import: Creation complete after 0s [id=111112]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].null_resource.sleep_after_project_import: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].null_resource.sleep_after_project_import: Creation complete after 0s [id=111113]

预期执行日志

module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.first_me: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.first_me: Creation complete after 9s [id=some_project_id2/roles/accessapproval.invalidator/user:test@test.com]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.then_me: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].google_project_iam_member.then_me: Creation complete after 9s [id=some_project_id1/roles/accessapproval.invalidator/user:test@test.com]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].time_sleep.wait_seconds: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].time_sleep.wait_seconds: Still creating... [10s elapsed]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].time_sleep.wait_seconds: Creation complete after 11s [id=2023-12-04T13:22:47Z]
module.role_binding_sleep_test["roles/accessapproval.invalidator"].null_resource.sleep_after_project_import: Creating...
module.role_binding_sleep_test["roles/accessapproval.invalidator"].null_resource.sleep_after_project_import: Creation complete after 0s [id=111111]
# now 1st foreach iteration for role should end and next start
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.first_me: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.first_me: Creation complete after 9s [id=some_project_id2/roles/serviceconsumermanagement.tenancyUnitsAdmin/user:test@test.com]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.then_me: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].google_project_iam_member.then_me: Creation complete after 10s [id=some_project_id1/roles/serviceconsumermanagement.tenancyUnitsAdmin/user:test@test.com]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].time_sleep.wait_seconds: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].time_sleep.wait_seconds: Still creating... [10s elapsed]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].time_sleep.wait_seconds: Creation complete after 11s [id=2023-12-04T13:23:10Z]
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].null_resource.sleep_after_project_import: Creating...
module.role_binding_sleep_test["roles/serviceconsumermanagement.tenancyUnitsAdmin"].null_resource.sleep_after_project_import: Creation complete after 0s [id=111112]
# now 2nd foreach iteration for role should end and next start
module.role_binding_sleep_test["roles/actions.Viewer"].google_project_iam_member.then_me: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].google_project_iam_member.then_me: Creation complete after 9s [id=some_project_id1/roles/actions.Viewer/user:test@test.com]
module.role_binding_sleep_test["roles/actions.Viewer"].time_sleep.wait_seconds: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].time_sleep.wait_seconds: Still creating... [10s elapsed]
module.role_binding_sleep_test["roles/actions.Viewer"].time_sleep.wait_seconds: Creation complete after 11s [id=2023-12-04T13:22:59Z]
module.role_binding_sleep_test["roles/actions.Viewer"].null_resource.sleep_after_project_import: Creating...
module.role_binding_sleep_test["roles/actions.Viewer"].null_resource.sleep_after_project_import: Creation complete after 0s [id=111113]

解决方案

Terraform默认会将同类型、无依赖的资源归为一组处理,即使parallelism=1也仅限制同一时间执行的资源数量,不会强制按模块实例串行。要实现单个模块实例全流程完成后再启动下一个,需在模块实例之间显式添加依赖关系。

具体实现步骤

  1. 修改主文件main.tf,利用有序列表的索引特性,为每个模块实例添加对前一个实例的依赖:
locals {
  roles_for_bindings = [
    "roles/accessapproval.invalidator",
    "roles/actions.Viewer",
    "roles/serviceconsumermanagement.tenancyUnitsAdmin",
  ]
  # 为每个角色绑定索引,用于建立顺序依赖
  role_index_map = { for idx, role in local.roles_for_bindings : role => idx }
}

module "role_binding_sleep_test" {
  source     = "./templates/iam"
  for_each   = toset(local.roles_for_bindings)
  project_id = "some_project_id"
  project_id2 = "some_project_id2"
  role       = each.key
  user       = "test@test.com"

  # 让当前实例依赖索引减一的前序实例
  depends_on = [
    for role in local.roles_for_bindings :
    module.role_binding_sleep_test[role]
    if local.role_index_map[role] == local.role_index_map[each.key] - 1
  ]
}
  1. 执行命令:
terraform apply -parallelism=1

原理说明

通过role_index_map为每个角色分配索引,让每个模块实例依赖前一个索引对应的实例,Terraform会严格遵循依赖关系,等前一个实例的所有资源创建完成后,再启动下一个实例的全流程资源创建。


内容的提问来源于stack exchange,提问作者mr.TKO

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:15:55