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

如何让Terraform等待RBAC角色删除完成后再重建?

问题

在Azure中为数据湖容器配置RBAC权限时,重构模块后for_each的key发生变化,Terraform计划先删除旧RBAC角色分配再创建新的。首次执行apply失败,报错显示删除未完成就启动创建,提示角色分配已存在:

# module.external-storage.azurerm_role_assignment.rbac_permissions["datalakename-gips"] will be destroyed
  # (because key ["datalakename-gips"] is not in for_each map)
  - resource "azurerm_role_assignment" "rbac_permissions" {
      - id                   = "/subscriptions/guid/resourceGroups/rg-datalake-cvdev/providers/Microsoft.Storage/storageAccounts/datalakename/blobServices/default/containers/gips/providers/Microsoft.Authorization/roleAssignments/guid" -> null
      - name                 = "some guid" -> null
      - principal_id         = "some guid" -> null
      - principal_type       = "ServicePrincipal" -> null
      - role_definition_id   = "/subscriptions/some guid/providers/Microsoft.Authorization/roleDefinitions/some guid" -> null
      - role_definition_name = "Storage Blob Data Contributor" -> null
      - scope                = "/subscriptions/some guid/resourceGroups/rg-datalake-cvdev/providers/Microsoft.Storage/storageAccounts/datalakename/blobServices/default/containers/gips" -> null
    }

  # module.external-storage.azurerm_role_assignment.rbac_permissions["datalakename-gips-some guid-Storage Blob Data Contributor"] will be created
  + resource "azurerm_role_assignment" "rbac_permissions" {
      + id                               = (known after apply)
      + name                             = (known after apply)
      + principal_id                     = "some guid"
      + principal_type                   = (known after apply)
      + role_definition_id               = (known after apply)
      + role_definition_name             = "Storage Blob Data Contributor"
      + scope                            = "/subscriptions/some guid/resourceGroups/rg-datalake-cvdev/providers/Microsoft.Storage/storageAccounts/datalakename/blobServices/default/containers/gips"
      + skip_service_principal_aad_check = (known after apply)
    }

 Error: authorization.RoleAssignmentsClient#Create: Failure responding to request: StatusCode=409 -- Original Error: autorest/azure: Service returned an error. Status=409 Code="RoleAssignmentExists" Message="The role assignment already exists."
│ 
│   with module.external-storage.azurerm_role_assignment.rbac_permissions["datalakename-gips-some guid-Storage Blob Data Contributor"],
│   on modules/external-storage/additional_container_permissions.tf line 14, in resource "azurerm_role_assignment" "rbac_permissions":
│   14: resource "azurerm_role_assignment" "rbac_permissions" {

当前模块代码:

resource "azurerm_role_assignment" "rbac_permissions" {
  for_each             = { for container_accesslist_iter in local.container_additional_accesslist : "${container_accesslist_iter.datalake_account_name}-${container_accesslist_iter.container_name}-${container_accesslist_iter.principal}-${container_accesslist_iter.privilege}" => container_accesslist_iter }
  scope                = local.containers_with_resource_manager_id["${each.value.datalake_account_name}-${each.value.container_name}"]
  role_definition_name = each.value.privilege
  principal_id         = each.value.principal
}

重试apply可成功,但需要让Terraform自动等待删除完成后再创建。

解决方案

方法1:拆分执行步骤(手动过渡)

分两次执行Terraform命令,先完成旧角色的销毁,再创建新角色:

  • 第一次执行:terraform apply -destroy-target=module.external-storage.azurerm_role_assignment.rbac_permissions["datalakename-gips"],指定销毁旧角色分配
  • 等待销毁完成后,执行terraform apply完成新角色的创建

方法2:添加临时依赖强制等待

通过空资源绑定旧角色的销毁,让新角色等待销毁完成后再创建,属于临时过渡方案:

# 临时空资源,依赖旧角色的销毁
resource "null_resource" "wait_for_old_rbac_deletion" {
  depends_on = [azurerm_role_assignment.rbac_permissions["datalakename-gips"]]
}

# 修改角色分配资源,添加依赖
resource "azurerm_role_assignment" "rbac_permissions" {
  for_each             = { for container_accesslist_iter in local.container_additional_accesslist : "${container_accesslist_iter.datalake_account_name}-${container_accesslist_iter.container_name}-${container_accesslist_iter.principal}-${container_accesslist_iter.privilege}" => container_accesslist_iter }
  scope                = local.containers_with_resource_manager_id["${each.value.datalake_account_name}-${each.value.container_name}"]
  role_definition_name = each.value.privilege
  principal_id         = each.value.principal
  
  # 强制等待旧角色销毁完成
  depends_on = [null_resource.wait_for_old_rbac_deletion]
}

注意:完成一次成功apply后,需移除该空资源,避免后续不必要的依赖。

方法3:稳定for_each的key(最优解)

从根源上避免不必要的销毁重建,调整for_each的key为角色分配的唯一标识组合(scope+主体ID+角色定义ID),确保业务逻辑不变时key稳定:

# 预先获取角色定义ID,避免使用角色名称可能的歧义
data "azurerm_role_definition" "rbac_role" {
  for_each = toset([for item in local.container_additional_accesslist : item.privilege])
  name     = each.value
}

resource "azurerm_role_assignment" "rbac_permissions" {
  for_each = {
    for item in local.container_additional_accesslist : 
    "${item.datalake_account_name}-${item.container_name}-${item.principal}-${data.azurerm_role_definition.rbac_role[item.privilege].id}" => item
  }
  scope                = local.containers_with_resource_manager_id["${each.value.datalake_account_name}-${each.value.container_name}"]
  role_definition_id   = data.azurerm_role_definition.rbac_role[each.value.privilege].id
  principal_id         = each.value.principal
}

这样重构后,只要权限配置的核心逻辑不变,for_each的key不会变化,也就不会触发销毁重建操作,彻底避免冲突问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:59:01