Terraform从tfvars创建AD组并配置Key Vault访问策略咨询
问题
我原本有一段简单的Terraform脚本(如下),可以创建由Terraform服务主体拥有的Azure AD组,并添加指定用户主体名称(UPN)的成员。现在我扩展了功能,通过dev.tfvars定义多个组及对应的多个成员,当前脚本能运行,但作为Terraform新手,想确认写法是否正确;另外我的脚本已经创建了Key Vault,希望为其中一个组添加权限以解决无法列出密钥的问题,不确定用访问策略还是RBAC更合适。
原始基础脚本
# 获取当前Azure AD客户端配置 data "azuread_client_config" "current" {} # 创建名为"Developers"的Azure AD组 resource "azuread_group" "devs" { display_name = "Developers" owners = [data.azuread_client_config.current.object_id] security_enabled = true } # 通过UPN获取特定用户的详情 data "azuread_user" "specific_user" { user_principal_name = "info@site.com" } # 将特定用户添加为"Developers"组的成员 resource "azuread_group_member" "member" { group_object_id = azuread_group.devs.object_id member_object_id = data.azuread_user.specific_user.object_id }
扩展后的代码片段
// variables.tf variable "groups" { description = "要创建的组列表" type = list(object({ display_name = string users = list(string) # 用户主体名称(UPNs) })) } // dev.tfvars groups = [ { display_name = "ProdDev_Software_Developers", users = ["info@site.com", "john@NETORGFT11072195.onmicrosoft.com"] }, { display_name = "Fin_Financial_Analysts", users = ["info@site.com", "john@NETORGFT11072195.onmicrosoft.com"] } ] // user_groups.tf locals { group_user_pairs = flatten([ for group in var.groups : [ for user in group.users : { display_name = group.display_name user = user } ] ]) } resource "azuread_group" "group" { for_each = { for group in var.groups : group.display_name => group } display_name = each.key owners = [data.azuread_client_config.current.object_id] security_enabled = true } data "azuread_user" "user" { for_each = { for item in local.group_user_pairs : "${item.display_name}:${item.user}" => item } user_principal_name = each.value.user } resource "azuread_group_member" "group_member" { for_each = { for item in local.group_user_pairs : "${item.display_name}:${item.user}" => item } group_object_id = azuread_group.group[each.value.display_name].object_id member_object_id = data.azuread_user.user[each.key].object_id }
待添加的访问策略需求
我的脚本已创建Key Vault,希望在其创建完成后,为指定组添加如下权限(解决无法列出密钥的问题),也可以采用RBAC方案,不确定哪种更优:
access_policy { tenant_id = data.azurerm_client_config.current.tenant_id object_id = var.devs_group_object_id key_permissions = [ "Get", "List", "Update", "Create", "Import", "Delete", "Recover", "Backup", "Restore" ] secret_permissions = [ "Get", "Set", "List", "Delete", "Recover", "Backup", "Restore" ] }
回答
一、多组多成员Terraform写法验证
你的扩展代码写法是正确的,符合Terraform最佳实践,细节说明如下:
- 变量定义:
variable "groups"使用list(object)类型,清晰定义组结构,便于通过tfvars批量配置,可读性和扩展性良好。 - for_each使用:
azuread_group.group通过for_each遍历组列表,每个组作为独立资源管理,避免列表索引变更导致的资源重建问题。data.azuread_user.user和azuread_group_member.group_member用${item.display_name}:${item.user}作为唯一key,避免同一用户加入多组时的资源冲突,逻辑严谨。
- locals处理:
group_user_pairs通过嵌套for循环+flatten将组与用户的多对多关系展开为一维列表,为后续资源遍历提供清晰数据源,写法高效。
小优化建议:
- 可在变量的object类型中增加
description = optional(string),给azuread_group添加描述字段,让配置更完整。 - 若用户UPN可能跨租户重复,可在user的key中加入租户ID,但当前写法已覆盖你的场景需求。
二、Key Vault权限方案选择:访问策略 vs RBAC
方案对比
| 维度 | 访问策略(Access Policy) | RBAC(基于角色的访问控制) |
|---|---|---|
| 权限粒度 | 细粒度到单个密钥/机密/证书的具体操作(如仅List密钥) | 基于预定义角色,粒度较粗(如Key Vault Secrets User包含Get/List等权限) |
| 管理复杂度 | 需单独维护每个主体的权限,主体多时配置繁琐 | 依托Azure AD组管理权限,变更只需调整组成员,维护更简单 |
| 兼容性 | 支持所有Key Vault功能,适配旧版部署 | 需要Key Vault启用Azure RBAC权限模型(创建时或后续配置开启) |
| 审计与合规 | 权限日志单独记录,整合Azure AD审计较麻烦 | 直接对接Azure AD审计日志,便于统一管理 |
推荐方案
优先选择RBAC,原因如下:
- 符合Azure统一权限管理模型,无需单独维护Key Vault访问策略,与其他Azure资源权限管理逻辑一致。
- 复用已创建的Azure AD组,后续调整权限只需修改组成员或切换角色,无需改动Terraform代码中的权限列表。
实现代码
1. 启用Key Vault的RBAC权限模型
如果是新创建的Key Vault,在azurerm_key_vault资源中添加配置:
resource "azurerm_key_vault" "example" { name = "example-keyvault" location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name tenant_id = data.azurerm_client_config.current.tenant_id soft_delete_retention_days = 7 purge_protection_enabled = true # 启用RBAC权限模型 enable_rbac_authorization = true }
若为已存在的Key Vault,添加该字段后执行terraform apply即可切换模型(切换后原有访问策略失效,需通过RBAC重新配置权限)。
2. 为目标组分配RBAC角色
以给ProdDev_Software_Developers组分配密钥和机密管理权限为例:
# 分配密钥管理员角色 resource "azurerm_role_assignment" "kv_key_admin" { scope = azurerm_key_vault.example.id role_definition_name = "Key Vault Key Administrator" principal_id = azuread_group.group["ProdDev_Software_Developers"].object_id } # 分配机密管理员角色 resource "azurerm_role_assignment" "kv_secret_admin" { scope = azurerm_key_vault.example.id role_definition_name = "Key Vault Secrets Administrator" principal_id = azuread_group.group["ProdDev_Software_Developers"].object_id }
若仅需列出密钥权限,分配Key Vault Secrets User或Key Vault Key User角色即可,无需管理员权限。
若坚持使用访问策略
需确保Key Vault未启用RBAC,然后在azurerm_key_vault的access_policy块中直接引用已创建的组对象ID:
resource "azurerm_key_vault" "example" { name = "example-keyvault" location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name tenant_id = data.azurerm_client_config.current.tenant_id soft_delete_retention_days = 7 purge_protection_enabled = true # 禁用RBAC(默认禁用,若之前启用过需设为false) enable_rbac_authorization = false access_policy { tenant_id = data.azurerm_client_config.current.tenant_id # 直接引用创建好的组对象ID,无需额外变量 object_id = azuread_group.group["ProdDev_Software_Developers"].object_id key_permissions = [ "Get", "List", "Update", "Create", "Import", "Delete", "Recover", "Backup", "Restore" ] secret_permissions = [ "Get", "Set", "List", "Delete", "Recover", "Backup", "Restore" ] } }
内容的提问来源于stack exchange,提问作者nop
相关产品推荐
相关产品推荐

