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

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最佳实践,细节说明如下:

  1. 变量定义:variable "groups"使用list(object)类型,清晰定义组结构,便于通过tfvars批量配置,可读性和扩展性良好。
  2. for_each使用:
    • azuread_group.group通过for_each遍历组列表,每个组作为独立资源管理,避免列表索引变更导致的资源重建问题。
    • data.azuread_user.user和azuread_group_member.group_member用${item.display_name}:${item.user}作为唯一key,避免同一用户加入多组时的资源冲突,逻辑严谨。
  3. 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,原因如下:

  1. 符合Azure统一权限管理模型,无需单独维护Key Vault访问策略,与其他Azure资源权限管理逻辑一致。
  2. 复用已创建的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:54:58