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

Terraform Azure Key Vault访问策略创建/销毁循环问题求助

Terraform Azure Key Vault 访问策略循环问题及解决方案

问题描述

通过Terraform模块创建Key Vault并配置默认访问策略后,为Synapse系统分配身份添加独立azurerm_key_vault_access_policy资源时,首次apply操作可正常执行,但后续每次执行plan时,Key Vault模块与独立访问策略资源会陷入创建/销毁循环。移除模块内默认策略会导致Terraform权限不足报错,添加服务主体策略又会回到循环状态。

相关代码

synapse.tf

# Key vault Policy 
import {
  to = azurerm_key_vault_access_policy.synapse-identity-policy
  id = module.app3-kv.vault_id
}

resource "azurerm_key_vault_access_policy" "synapse-identity-policy" {
  key_vault_id = module.app3-kv.vault_id
  tenant_id    = azurerm_synapse_workspace.synapse.identity[0].tenant_id
  object_id    = azurerm_synapse_workspace.synapse.identity[0].principal_id

  key_permissions = [
    "Get", "WrapKey", "UnwrapKey"
  ]

}

Key Vault模块 (main.tf)

resource "azurerm_key_vault" "key_vault" {
  name                       = var.kv_name
  location                   = var.location
  resource_group_name        = var.resource_group_name
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  sku_name                   = var.kv_sku_name
  enabled_for_disk_encryption     = var.enabled_for_disk_encryption
  enabled_for_deployment          = var.enabled_for_deployment
  enabled_for_template_deployment = var.enabled_for_template_deployment
  soft_delete_retention_days = 7
  purge_protection_enabled   = true
  tags                       = var.tags

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = var.kv_access_group_id

    key_permissions         = var.key_permissions
    secret_permissions      = var.secret_permissions
    certificate_permissions = var.certificate_permissions
  } 
  
  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

    key_permissions         = var.key_permissions
    secret_permissions      = var.secret_permissions
    certificate_permissions = var.certificate_permissions
  } 

  //public_network_access_enabled = var.public_network_access_enabled
  network_acls {
    bypass         = var.kv_bypass
    default_action = var.kv_default_action
    ip_rules       = var.kv_ip_address
  }
  
}

output "vault_id" {
  value = azurerm_key_vault.key_vault.id  
}

output "vault_name" {
  value = azurerm_key_vault.key_vault.name
}

output "vault_resource_group_name" {
  value = azurerm_key_vault.key_vault.resource_group_name
}

key_vault.tf

module "app3-kv" {
  source = "../modules/key_vault"

  kv_name                         = var.kv_name
  resource_group_name             = data.azurerm_resource_group.kv-rg.name
  location                        = var.location
  enabled_for_deployment          = true
  enabled_for_disk_encryption     = true
  enabled_for_template_deployment = true
  tags                            = var.kv_tags

  depends_on = [data.azurerm_resource_group.kv-rg]

}

Plan输出示例

# module.spexs-app3-kv.azurerm_key_vault.key_vault will be updated in-place
  ~ resource "azurerm_key_vault" "key_vault" {
      ~ access_policy                   = [
            # (1 unchanged element hidden)
            {
                application_id          = ""
                certificate_permissions = [
                    "Get",
                    "List",
                    "GetIssuers",
                    "ListIssuers",
                ]
                key_permissions         = [
                    "Get",
                    "Create",
                    "Delete",
                    "List",
                    "Recover",
                    "Restore",
                    "UnwrapKey",
                    "WrapKey",
                    "List",
                    "GetRotationPolicy",
                    "SetRotationPolicy",
                ]
                object_id               = "964c5f8c-c077-44b0-8be4-60c775720e5e"
                secret_permissions      = [
                    "Get",
                    "List",
                    "Set",
                    "Delete",
                    "Recover",
                    "Restore",
                ]
                storage_permissions     = []
                tenant_id               = ""
            },
          - {
              - application_id          = ""
              - certificate_permissions = []
              - key_permissions         = [
                  - "Get",
                  - "WrapKey",
                  - "UnwrapKey",
                ]
              - object_id               = "b5957f89-c575-42f5-8fe1-c4ba8265eaa6"
              - secret_permissions      = []
              - storage_permissions     = []
              - tenant_id               = ""
            },
        ]
        id                              = "/subscriptions/subid/resourceGroups/AGV-C-AKV-RGP-04-001/providers/Microsoft.KeyVault/vaults/AGV-C-AKV-04-003-SYNAPSE"
        name                            = "AGV-C-AKV-04-003-SYNAPSE"
        tags                            = {
            "Billing Project" = ""
            "Component"       = "Azure Key Vault"
            "Environment"     = ""
            "System"          = ""
            "Terraform"       = ""
        }
        # (12 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

问题原因与解决方案

原因

这是Terraform Azure Provider的已知行为:当同时使用azurerm_key_vault资源内的access_policy块和独立的azurerm_key_vault_access_policy资源时,两者会互相覆盖对方的策略。因为azurerm_key_vault的access_policy块管理的是所有访问策略,而独立资源会尝试添加自己的策略,导致Terraform在plan时检测到不一致,触发循环。

解决方案

方案1:统一用独立azurerm_key_vault_access_policy资源管理所有策略

完全移除azurerm_key_vault模块内的access_policy块,改用独立资源管理所有访问策略,包括Terraform服务主体和默认组的权限:

  1. 在Key Vault模块中删除所有access_policy块
  2. 为Terraform服务主体和默认组创建独立访问策略:
resource "azurerm_key_vault_access_policy" "terraform-sp" {
  key_vault_id = azurerm_key_vault.key_vault.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = data.azurerm_client_config.current.object_id

  key_permissions         = var.key_permissions
  secret_permissions      = var.secret_permissions
  certificate_permissions = var.certificate_permissions
}

resource "azurerm_key_vault_access_policy" "default-group" {
  key_vault_id = azurerm_key_vault.key_vault.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = var.kv_access_group_id

  key_permissions         = var.key_permissions
  secret_permissions      = var.secret_permissions
  certificate_permissions = var.certificate_permissions
}
  1. 保留Synapse身份的独立访问策略资源

这样所有策略都由独立资源管理,不会出现互相覆盖的情况。

方案2:在Key Vault模块中包含所有访问策略(包括Synapse身份)

如果不想拆分策略,可以将Synapse的访问策略整合到Key Vault模块中,通过变量传入需要授权的身份信息:

  1. 在Key Vault模块中添加变量:
variable "additional_access_policies" {
  type = list(object({
    tenant_id               = string
    object_id               = string
    key_permissions         = list(string)
    secret_permissions      = list(string)
    certificate_permissions = list(string)
  }))
  default = []
}
  1. 在azurerm_key_vault资源中动态生成访问策略:
resource "azurerm_key_vault" "key_vault" {
  // 其他属性不变

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = var.kv_access_group_id

    key_permissions         = var.key_permissions
    secret_permissions      = var.secret_permissions
    certificate_permissions = var.certificate_permissions
  } 
  
  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

    key_permissions         = var.key_permissions
    secret_permissions      = var.secret_permissions
    certificate_permissions = var.certificate_permissions
  } 

  dynamic "access_policy" {
    for_each = var.additional_access_policies
    content {
      tenant_id               = access_policy.value.tenant_id
      object_id               = access_policy.value.object_id
      key_permissions         = access_policy.value.key_permissions
      secret_permissions      = access_policy.value.secret_permissions
      certificate_permissions = access_policy.value.certificate_permissions
    }
  }

  // 其他属性不变
}
  1. 在调用模块时传入Synapse身份的策略:
module "app3-kv" {
  source = "../modules/key_vault"

  // 其他变量不变

  additional_access_policies = [
    {
      tenant_id               = azurerm_synapse_workspace.synapse.identity[0].tenant_id
      object_id               = azurerm_synapse_workspace.synapse.identity[0].principal_id
      key_permissions         = ["Get", "WrapKey", "UnwrapKey"]
      secret_permissions      = []
      certificate_permissions = []
    }
  ]
}

这种方式将所有策略集中管理,避免独立资源和模块内策略的冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:05:55