TerraformPilot

DevOps

Fix Terraform Error - Azure Key Vault Access Policy Conflict

Fix Key Vault access policy conflicts in Terraform for Azure. Handle duplicate policies, RBAC vs access policy models, and soft-delete recovery issues.

LLuca Berton1 min read

Quick Answer

#

A Key Vault access policy for that object_id already exists, or you're mixing inline access_policy blocks with separate azurerm_key_vault_access_policy resources. Pick one approach and stick with it. Also check if the vault was soft-deleted — Key Vault names are reserved after deletion.

The Error

#
Error: creating Access Policy for Key Vault: 
Access policy already exists for object ID "xxxx-xxxx"

What Causes This

#

1. Mixing Inline and Separate Access Policies

#
# ❌ CONFLICT — inline + separate resource for same principal
resource "azurerm_key_vault" "main" {
  access_policy {
    object_id = data.azurerm_client_config.current.object_id
    key_permissions = ["Get", "List"]
  }
}
 
resource "azurerm_key_vault_access_policy" "app" {
  key_vault_id = azurerm_key_vault.main.id
  object_id    = data.azurerm_client_config.current.object_id  # Same object_id!
  key_permissions = ["Get", "List", "Create"]
}

2. Soft-Deleted Vault with Same Name

#

Azure Key Vault has soft delete enabled by default (mandatory since February 2025). Deleted vaults retain their name for 90 days.

3. Policy Created Outside Terraform

#

Someone added an access policy via Azure Portal.

How to Fix It

#

Solution 1: Use Only Separate Access Policy Resources

#
resource "azurerm_key_vault" "main" {
  name                = "${var.project}-kv"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  sku_name            = "standard"
  tenant_id           = data.azurerm_client_config.current.tenant_id
 
  # NO inline access_policy blocks
}
 
resource "azurerm_key_vault_access_policy" "terraform" {
  key_vault_id = azurerm_key_vault.main.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = data.azurerm_client_config.current.object_id
 
  key_permissions    = ["Get", "List", "Create", "Delete"]
  secret_permissions = ["Get", "List", "Set", "Delete"]
}
 
resource "azurerm_key_vault_access_policy" "app" {
  key_vault_id = azurerm_key_vault.main.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azurerm_user_assigned_identity.app.principal_id
 
  secret_permissions = ["Get", "List"]
}

Solution 2: Use RBAC Instead of Access Policies

#
resource "azurerm_key_vault" "main" {
  name                       = "${var.project}-kv"
  location                   = azurerm_resource_group.main.location
  resource_group_name        = azurerm_resource_group.main.name
  sku_name                   = "standard"
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  enable_rbac_authorization  = true  # Use RBAC instead of access policies
}
 
resource "azurerm_role_assignment" "kv_admin" {
  scope                = azurerm_key_vault.main.id
  role_definition_name = "Key Vault Secrets Officer"
  principal_id         = data.azurerm_client_config.current.object_id
}

Solution 3: Recover Soft-Deleted Vault

#
# List soft-deleted vaults
az keyvault list-deleted
 
# Recover it
az keyvault recover --name my-kv
 
# Or purge it to reuse the name
az keyvault purge --name my-kv

Troubleshooting Checklist

#
  1. ✅ Are you mixing inline and separate access policies?
  2. ✅ Is the object_id duplicated across policies?
  3. ✅ Was the Key Vault soft-deleted?
  4. ✅ Should you use RBAC instead of access policies?
#

Conclusion

#

Never mix inline access_policy blocks with separate azurerm_key_vault_access_policy resources. Use separate resources for modularity, or switch to RBAC (enable_rbac_authorization = true) for cleaner permission management. Check for soft-deleted vaults if creation fails.

#Terraform#Troubleshooting#DevOps#Error Fix#Azure

Share this article