如何让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
相关产品推荐
相关产品推荐

