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

首次用Terraform通过专用端点部署Key Vault密钥失败求助

问题:首次Terraform部署Azure Key Vault及密钥时触发403禁止错误

问题现象

  • 首次通过Terraform同时部署禁用公网访问的Azure Key Vault和密钥时,出现403错误;Key Vault已存在或第二次执行部署时可正常创建密钥
  • 报错信息:
Error: checking for presence of existing Key "example-key1" (Key Vault "https://xx-xxx-xxx.vault.azure.net/"): keyvault.BaseClient#GetKey: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="Forbidden" Message="Public network access is disabled and request is not from a trusted service nor via an approved private link.\r\nCaller: appid=xxx;oid=xxx;iss=xxx: xx-xxx-xxx;location=westeurope" InnerError={"code":"ForbiddenByConnection"}
with azurerm_key_vault_key.example, on main.tf line 97, in resource "azurerm_key_vault_key" "example": 97: resource "azurerm_key_vault_key" "example" {

环境配置

  • 采用中心辐射(hub-spoke)网络模型,Key Vault强制禁用公网访问
  • 使用现有privatelink.vaultcore.azure.net专用DNS区域,已与中心VNet关联
  • 分支VNet已连接到中心VNet,部署环境(VS Code/自托管代理)位于分支VNet内

已尝试的无效方案

  • 在代码中添加1-20分钟的time_sleep等待
  • 将专用DNS区域直接与分支VNet关联

解决方案

核心问题是:Terraform等待私有端点创建完成后,Azure私有DNS解析记录尚未同步到分支VNet,导致请求仍走公网被拒绝。以下是可行修复方案:

方案1:强制使用私有IP访问Key Vault(推荐)

通过私有端点的私有IP直接访问Key Vault,绕过DNS解析延迟问题。修改azurerm_key_vault_key资源,添加endpoint参数:

resource "azurerm_key_vault_key" "example" {
  name         = "key-test"
  key_vault_id = azurerm_key_vault.example.id
  key_type     = "RSA"
  key_size     = 2048
  key_opts     = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"]
  # 强制使用私有端点IP访问,跳过DNS解析
  endpoint     = "https://${azurerm_private_endpoint.example.private_service_connection[0].private_ip_address}"
  depends_on   = [azurerm_private_endpoint.example]

  tags = {
    "source" = "Terraform"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}

方案2:等待DNS解析生效后创建密钥

通过本地执行脚本,验证Key Vault域名已解析到私有IP,再创建密钥:

resource "null_resource" "wait_for_dns" {
  provisioner "local-exec" {
    command = <<EOT
      until nslookup ${azurerm_key_vault.example.name}.vault.azure.net | grep -q "${azurerm_private_endpoint.example.private_service_connection[0].private_ip_address}"; do
        echo "Waiting for DNS resolution to propagate..."
        sleep 30
      done
    EOT
  }
  depends_on = [azurerm_private_endpoint.example]
}

resource "azurerm_key_vault_key" "example" {
  # 保留原有配置
  name         = "key-test"
  key_vault_id = azurerm_key_vault.example.id
  key_type     = "RSA"
  key_size     = 2048
  key_opts     = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"]
  # 依赖DNS验证完成
  depends_on   = [null_resource.wait_for_dns]

  tags = {
    "source" = "Terraform"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}

方案3:临时启用公网访问(不推荐,仅应急)

创建Key Vault时临时启用公网访问,密钥创建完成后再禁用:

resource "azurerm_key_vault" "example" {
  name                       = "test-kv"
  location                   = data.azurerm_resource_group.resource_group.location
  resource_group_name        = data.azurerm_resource_group.resource_group.name
  sku_name                   = "standard"
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days = 7
  purge_protection_enabled   = true

  enabled_for_disk_encryption     = false
  enabled_for_template_deployment = false
  enabled_for_deployment          = false

  public_network_access_enabled = true # 初始临时启用
  enable_rbac_authorization     = true

  network_acls {
    default_action = "Deny"
    bypass         = "AzureServices"
  }

  tags = {
    "source" = "Terraform"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}

# 创建密钥后更新Key Vault,禁用公网访问
resource "azurerm_key_vault" "example_post_key" {
  name                       = azurerm_key_vault.example.name
  location                   = azurerm_key_vault.example.location
  resource_group_name        = azurerm_key_vault.example.resource_group_name
  sku_name                   = azurerm_key_vault.example.sku_name
  tenant_id                  = azurerm_key_vault.example.tenant_id
  soft_delete_retention_days = azurerm_key_vault.example.soft_delete_retention_days
  purge_protection_enabled   = azurerm_key_vault.example.purge_protection_enabled

  enabled_for_disk_encryption     = azurerm_key_vault.example.enabled_for_disk_encryption
  enabled_for_template_deployment = azurerm_key_vault.example.enabled_for_template_deployment
  enabled_for_deployment          = azurerm_key_vault.example.enabled_for_deployment

  public_network_access_enabled = false # 最终禁用
  enable_rbac_authorization     = azurerm_key_vault.example.enable_rbac_authorization

  network_acls {
    default_action = azurerm_key_vault.example.network_acls[0].default_action
    bypass         = azurerm_key_vault.example.network_acls[0].bypass
  }

  tags = azurerm_key_vault.example.tags

  depends_on = [azurerm_key_vault_key.example]
}

resource "azurerm_key_vault_key" "example" {
  # 保留原有密钥配置
  name         = "key-test"
  key_vault_id = azurerm_key_vault.example.id
  key_type     = "RSA"
  key_size     = 2048
  key_opts     = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"]
  depends_on   = [azurerm_key_vault.example]

  tags = {
    "source" = "Terraform"
  }

  lifecycle {
    ignore_changes = [tags]
  }
}

核心原因

首次部署时,私有端点资源已创建,但Azure私有DNS区域的解析记录需要时间同步到分支VNet的DNS服务器。Terraform的time_sleep仅等待资源创建完成,无法感知DNS解析状态,导致请求Key Vault时仍解析为公网IP,触发403禁止错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 23:31:13