Azure多存储账户与MSI创建及角色分配Terraform配置问题
问题场景
使用Terraform批量创建Azure存储账户与用户托管标识(MSI),需求是单个存储账户可绑定多个MSI并分配不同角色。目前已在tfvars中为存储账户定义role_assignments块,但因MSI尚未创建,无法直接引用其principal_id;尝试用locals处理时触发「The “for_each” set includes values derived from resource attributes that…」错误,需解决隐式依赖导致的配置问题。
现有模块代码
托管标识模块(../modules/userIdentity)
#------------------- # 托管标识 #------------------- resource "azurerm_user_assigned_identity" "user_assigned_identity" { name = var.name location = var.location resource_group_name = var.resource_group_name }
存储账户模块(../modules/storageAccount)
#------------------- # 存储账户 #------------------- resource "azurerm_storage_account" "storage_account" { name = "storageaccountname" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location account_tier = "Standard" account_replication_type = "GRS" tags = { environment = "staging" } } #------------------- # 存储容器 #------------------- resource "azurerm_storage_container" "storage_container" { for_each = var.containers name = try(each.value.container_name, null) container_access_type = try(each.value.access_type, null) storage_account_name = azurerm_storage_account.storage_account.name } #----------------- # 角色分配 #----------------- resource "azurerm_role_assignment" "role_assignment" { for_each = var.role_assignments description = try(each.value.description, null) role_definition_name = try(each.value.role, null) principal_id = try(each.value.principal_id, null) scope = azurerm_storage_account.storage_account.id }
模块调用代码(根模块)
#------------------- # 存储账户实例 #------------------- module "storage_account" { source = "../modules/storageAccount" for_each = var.storage_account name = lower(replace(each.value.name, "/[[:^alnum:]]/", "")) resource_group_name = each.value.resource_group location = var.location containers = try(each.value.storage_container, {}) } #----------------- # 用户托管标识实例 #----------------- module "UserManagedIdentity" { source = "../modules/userIdentity" for_each = var.UserManagedIdentity name = lower(each.value.name) location = var.location resource_group_name = each.value.resource_group }
TFVARS配置
storage_account = { new_tf_sa = { name = "test_sa" resource_group = "learn_rg" storage_container = { testcontainer = { container_name = "tf-states" access_type = "private" } } role_assignments = { test_mi_role_assignment = { mi_name = "test1_user_identity" role = "Contributor" description = "Assign Contributor role" }, test2_mi_role_assignment = { mi_name = "test2_user_identity" role = "Contributor" description = "Assign Contributor role" } } } } UserManagedIdentity = { #-------------------------------# # 测试托管标识 # #-------------------------------# test1_user_identity = { name = "mi-test1" resource_group = "learn_rg" }, test2_user_identity = { name = "mi-test2" resource_group = "learn_rg" } }
解决方案
核心思路是在根模块中提前关联MSI与存储账户的角色分配关系,确保for_each使用的键为静态变量值,避免依赖未创建的资源属性。
1. 托管标识模块添加输出
在../modules/userIdentity中添加输出,暴露MSI的principal_id:
output "principal_id" { type = string value = azurerm_user_assigned_identity.user_assigned_identity.principal_id description = "用户托管标识的Principal ID" }
2. 根模块创建locals关联角色分配
在根模块中定义locals,将存储账户的role_assignments与对应的MSI principal_id绑定:
locals { # 构建包含principal_id的存储账户角色分配映射 storage_account_role_assignments = { for sa_key, sa in var.storage_account : sa_key => { for ra_key, ra in sa.role_assignments : ra_key => { mi_name = ra.mi_name role = ra.role description = ra.description # 通过MSI模块的键查找对应的principal_id principal_id = module.UserManagedIdentity[ra.mi_name].principal_id } } } }
3. 修改存储账户模块调用
更新根模块中存储账户的调用,传递构建好的带principal_id的角色分配:
module "storage_account" { source = "../modules/storageAccount" for_each = var.storage_account name = lower(replace(each.value.name, "/[[:^alnum:]]/", "")) resource_group_name = each.value.resource_group location = var.location containers = try(each.value.storage_container, {}) # 传递包含principal_id的角色分配 role_assignments = local.storage_account_role_assignments[each.key] }
4. 存储账户模块修正变量与角色分配资源
在../modules/storageAccount中添加变量定义:
variable "role_assignments" { type = map(object({ mi_name = string role = string description = string principal_id = string })) default = {} description = "存储账户的角色分配列表" }
修正角色分配资源,移除不必要的try:
resource "azurerm_role_assignment" "role_assignment" { for_each = var.role_assignments description = each.value.description role_definition_name = each.value.role principal_id = each.value.principal_id scope = azurerm_storage_account.storage_account.id # 跳过AAD检查,避免部分场景下的权限错误 skip_service_principal_aad_check = true }
关键说明
- 确保tfvars中
role_assignments的mi_name与UserManagedIdentity的键完全匹配,才能正确关联到对应的MSI。 - 通过locals将静态变量(mi_name)与资源输出(principal_id)绑定,
for_each使用的键来自静态变量,避免了隐式依赖问题。 - 模块职责拆分清晰:托管标识模块负责创建并输出标识信息,存储账户模块负责资源创建与权限分配,根模块负责依赖关联。
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

