← Back to ArticlesKey Vault

Key Vault — Virtual Network Integration and Private Endpoints

Securing Key Vault with virtual network rules, private endpoints, and configuring firewall for secure access.

Key Vault — Virtual Network Integration and Private Endpoints

A comprehensive guide to securing Azure Key Vault with network isolation using firewall rules, VNet service endpoints, private endpoints, and trusted service bypass — following zero-trust principles.


1. Why Network Security for Key Vault Matters

Zero Trust Principles

Azure Key Vault stores your most sensitive assets: encryption keys, certificates, and secrets such as connection strings and API keys. Under a zero-trust model, no network location is inherently trusted. Even traffic originating from within your Azure subscription must be explicitly authorized.

Key principles applied to Key Vault:

Compliance Requirements

Regulatory frameworks mandate network-level controls for cryptographic key stores:

FrameworkRequirement
PCI-DSS 4.0Requirement 1: Install and maintain network security controls
HIPAA§164.312(e)(1): Transmission security — encrypt and restrict access
SOC 2CC6.1: Logical and physical access controls
ISO 27001A.13.1: Network security management

Attack Surface Reduction

A publicly accessible Key Vault exposes a TLS endpoint on the internet. Even with strong authentication, this creates risk:

By restricting network access, you add a defense-in-depth layer that blocks unauthorized traffic before authentication is even evaluated.


2. Architecture Overview

┌──────────────────── Azure Region ───────────────────────┐
│                                                         │
│  VNet 10.0.0.0/16                                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐   │
│  │ App Subnet   │  │ AKS Subnet   │  │ PE Subnet    │   │
│  │ 10.0.1.0/24  │  │ 10.0.2.0/24  │  │ 10.0.3.0/24  │   │
│  │              │  │              │  │              │   │
│  │ App Service  │  │ AKS Cluster  │  │ Private EP   │   │
│  │ (VNet Integ) │  │ (CSI Driver) │  │ 10.0.3.4     │   │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘   │
│         └─────────────────┴─────────────────┘           │
│                           │                             │
│  Private DNS Zone: privatelink.vaultcore.azure.net      │
│  myvault.privatelink.vaultcore.azure.net → 10.0.3.4     │
│                            │                            │
│  ┌─────────────────────────┴─────────────────────────┐  │
│  │ Azure Key Vault (Public Access: Disabled)         │  │
│  │ Firewall: Deny All | Trusted Services: Bypass     │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
       ▲
       │ ExpressRoute / VPN
┌──────┴───────┐
│ On-Premises  │
└──────────────┘

Traffic flow:

  1. App Service uses VNet integration to route outbound traffic through the App Subnet.
  2. DNS resolves myvault.vault.azure.net → CNAME → myvault.privatelink.vaultcore.azure.net10.0.3.4.
  3. Traffic reaches the private endpoint NIC in the PE Subnet.
  4. Key Vault processes the request over the Microsoft backbone — never traversing the public internet.

3. Understanding Key Vault Network Access Models

3.1 Public Access (Default)

By default, Key Vault accepts connections from any IP address on the internet. Authentication is still required, but the endpoint is reachable globally.

# Check current network rules
az keyvault show --name myvault --resource-group rg \
  --query "properties.networkAcls" -o json

Default response:

{
  "bypass": "AzureServices",
  "defaultAction": "Allow",
  "ipRules": [],
  "virtualNetworkRules": []
}

3.2 Service Endpoints

VNet service endpoints extend your VNet identity to Key Vault over the Azure backbone. Traffic still hits the public endpoint, but Key Vault validates the source VNet/subnet.

Limitations:

3.3 Private Endpoints

Private endpoints assign a private IP from your VNet to the Key Vault instance. DNS is configured so that the vault's FQDN resolves to this private IP. Traffic never leaves the Microsoft network.

Advantages over service endpoints:

3.4 Trusted Services Bypass

Certain first-party Azure services need access to Key Vault for platform operations (e.g., disk encryption, SQL TDE, App Service certificate binding). The "trusted services" bypass allows these services even when the firewall denies all other traffic.

Trusted services include:


4. Step-by-Step Implementation

4.1 Configuring Firewall Rules

IP-Based Rules

Allow specific public IPs (e.g., corporate egress, CI/CD runners):

# Add IP rule for corporate office
az keyvault network-rule add \
  --name myvault \
  --resource-group rg \
  --ip-address 203.0.113.0/24

# Add individual IP for CI/CD runner
az keyvault network-rule add \
  --name myvault \
  --resource-group rg \
  --ip-address 198.51.100.42

# Set default action to Deny (blocks all traffic not explicitly allowed)
az keyvault update \
  --name myvault \
  --resource-group rg \
  --default-action Deny

VNet Rules (Service Endpoints)

# Enable service endpoint on the subnet first
az network vnet subnet update \
  --resource-group rg \
  --vnet-name myvnet \
  --name app-subnet \
  --service-endpoints Microsoft.KeyVault

# Add VNet rule to Key Vault
SUBNET_ID=$(az network vnet subnet show \
  --resource-group rg \
  --vnet-name myvnet \
  --name app-subnet \
  --query id -o tsv)

az keyvault network-rule add \
  --name myvault \
  --resource-group rg \
  --subnet $SUBNET_ID

Bicep — Firewall Configuration

resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: 'myvault'
  location: resourceGroup().location
  properties: {
    sku: { family: 'A', name: 'standard' }
    tenantId: subscription().tenantId
    enableRbacAuthorization: true
    publicNetworkAccess: 'Disabled'
    networkAcls: {
      defaultAction: 'Deny'
      bypass: 'AzureServices'
      ipRules: [
        { value: '203.0.113.0/24' }
      ]
      virtualNetworkRules: [
        { id: appSubnet.id }
      ]
    }
  }
}

4.2 Creating Private Endpoints

Azure CLI

# Get Key Vault resource ID
KV_ID=$(az keyvault show --name myvault --resource-group rg --query id -o tsv)

# Create private endpoint
az network private-endpoint create \
  --name kv-private-endpoint \
  --resource-group rg \
  --vnet-name myvnet \
  --subnet pe-subnet \
  --private-connection-resource-id $KV_ID \
  --group-id vault \
  --connection-name kv-pe-connection

# Verify the private endpoint connection
az keyvault private-endpoint-connection list \
  --vault-name myvault \
  --resource-group rg -o table

Bicep — Private Endpoint

param vnetName string
param peSubnetName string
param keyVaultName string
param location string = resourceGroup().location

resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' existing = { name: vnetName }
resource peSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-09-01' existing = {
  parent: vnet
  name: peSubnetName
}
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { name: keyVaultName }

resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-09-01' = {
  name: 'kv-private-endpoint'
  location: location
  properties: {
    subnet: { id: peSubnet.id }
    privateLinkServiceConnections: [
      {
        name: 'kv-pe-connection'
        properties: {
          privateLinkServiceId: keyVault.id
          groupIds: [ 'vault' ]
        }
      }
    ]
  }
}

4.3 DNS Configuration for Private Endpoints

Private endpoints require DNS to resolve the vault FQDN to the private IP. Azure Private DNS Zones automate this.

Azure CLI — Private DNS Zone Setup

# Create private DNS zone
az network private-dns zone create \
  --resource-group rg \
  --name privatelink.vaultcore.azure.net

# Link DNS zone to VNet
az network private-dns link vnet create \
  --resource-group rg \
  --zone-name privatelink.vaultcore.azure.net \
  --name kv-dns-link \
  --virtual-network myvnet \
  --registration-enabled false

# Create DNS zone group (auto-registers A record for the PE)
az network private-endpoint dns-zone-group create \
  --resource-group rg \
  --endpoint-name kv-private-endpoint \
  --name kv-dns-zone-group \
  --private-dns-zone privatelink.vaultcore.azure.net \
  --zone-name keyvault

# Verify DNS resolution
az network private-endpoint dns-zone-group show \
  --resource-group rg \
  --endpoint-name kv-private-endpoint \
  --name kv-dns-zone-group

Bicep — Private DNS Zone and Link

  name: 'privatelink.vaultcore.azure.net'
  location: 'global'
}

resource dnsVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
  parent: privateDnsZone
  name: 'kv-vnet-link'
  location: 'global'
  properties: {
    virtualNetwork: { id: vnet.id }
    registrationEnabled: false
  }
}

resource dnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-09-01' = {
  parent: privateEndpoint
  name: 'kv-dns-zone-group'
  properties: {
    privateDnsZoneConfigs: [
      {
        name: 'keyvault'
        properties: {
          privateDnsZoneId: privateDnsZone.id
        }
      }
    ]
  }
}

DNS Resolution Chain

Client request: myvault.vault.azure.net
  └─► CNAME: myvault.privatelink.vaultcore.azure.net
       └─► A record (Private DNS Zone): 10.0.3.4

For on-premises clients, configure conditional forwarders to forward vaultcore.azure.net queries to Azure DNS (168.63.129.16) via a DNS forwarder VM or Azure DNS Private Resolver.

4.4 Configuring Trusted Microsoft Services Bypass

# Enable trusted services bypass
az keyvault update \
  --name myvault \
  --resource-group rg \
  --bypass AzureServices

# Verify
az keyvault show --name myvault --resource-group rg \
  --query "properties.networkAcls.bypass" -o tsv
# Output: AzureServices

To disable bypass (strict mode — not recommended unless all services use private endpoints):

az keyvault update \
  --name myvault \
  --resource-group rg \
  --bypass None

5. Integration Patterns

5.1 App Service with VNet Integration

App Service requires regional VNet integration to route outbound traffic through your VNet and reach the private endpoint.

# Enable VNet integration on App Service
az webapp vnet-integration add \
  --name mywebapp \
  --resource-group rg \
  --vnet myvnet \
  --subnet app-subnet

# Route all traffic through VNet (required for private endpoint DNS)
az webapp config appsettings set \
  --name mywebapp \
  --resource-group rg \
  --settings WEBSITE_VNET_ROUTE_ALL=1

# Use private DNS — set DNS to Azure DNS
az webapp config appsettings set \
  --name mywebapp \
  --resource-group rg \
  --settings WEBSITE_DNS_SERVER=168.63.129.16

C# — Accessing Key Vault from App Service with Managed Identity:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

var client = new SecretClient(
    new Uri("https://myvault.vault.azure.net"),
    new DefaultAzureCredential());

KeyVaultSecret secret = await client.GetSecretAsync("my-secret");
string secretValue = secret.Value;

No code changes are needed when switching from public to private access — the SDK resolves DNS transparently.

5.2 Azure Functions with Private Endpoint

Azure Functions on the Elastic Premium or App Service plan support VNet integration.

resource functionApp 'Microsoft.Web/sites@2023-01-01' = {
  name: 'myfuncapp'
  location: location
  kind: 'functionapp'
  identity: { type: 'SystemAssigned' }
  properties: {
    serverFarmId: appServicePlan.id
    virtualNetworkSubnetId: appSubnet.id
    siteConfig: {
      vnetRouteAllEnabled: true
      appSettings: [
        { name: 'WEBSITE_DNS_SERVER', value: '168.63.129.16' }
        { name: 'KeyVaultUri', value: 'https://myvault.vault.azure.net' }
      ]
    }
  }
}

5.3 AKS Pods Accessing Key Vault via CSI Driver

The Azure Key Vault Provider for Secrets Store CSI Driver mounts secrets as volumes in pods. With private endpoints, the AKS VNet must resolve the vault's private DNS.

# Enable the addon
az aks enable-addons \
  --addons azure-keyvault-secrets-provider \
  --name myaks \
  --resource-group rg

# Link the private DNS zone to the AKS VNet
az network private-dns link vnet create \
  --resource-group rg \
  --zone-name privatelink.vaultcore.azure.net \
  --name aks-dns-link \
  --virtual-network aks-vnet \
  --registration-enabled false

SecretProviderClass manifest:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: kv-secrets
spec:
  provider: azure
  parameters:
    usePodIdentity: "false"
    useVMManagedIdentity: "true"
    userAssignedIdentityID: "<managed-identity-client-id>"
    keyvaultName: "myvault"
    objects: |
      array:
        - |
          objectName: my-secret
          objectType: secret
    tenantId: "<tenant-id>"

Mount the SecretProviderClass as a CSI volume in your pod spec with driver: secrets-store.csi.k8s.io and secretProviderClass: "kv-secrets".

5.4 Logic Apps with VNet Connector

Standard Logic Apps (single-tenant) support VNet integration natively. Consumption Logic Apps require an Integration Service Environment (ISE) or the newer VNet connector.

# Standard Logic App — enable VNet integration
az logicapp vnet-integration add \
  --name mylogicapp \
  --resource-group rg \
  --vnet myvnet \
  --subnet logic-subnet

For the Key Vault connector action in the workflow, use managed identity authentication with audience https://vault.azure.net.


6. Managed Identity + Private Endpoint Combination

The strongest security posture combines network isolation (private endpoints) with passwordless authentication (managed identity) and fine-grained authorization (RBAC).

6.1 System-Assigned vs User-Assigned Identity

AspectSystem-AssignedUser-Assigned
LifecycleTied to the resourceIndependent
SharingOne per resourceShared across resources
Use caseSingle-purpose servicesShared identity for microservices
DeletionAuto-deleted with resourceMust be manually deleted

Recommendation: Use user-assigned identities for services that share the same Key Vault access pattern (e.g., multiple Function Apps reading the same secrets). Use system-assigned for isolated workloads.

6.2 RBAC Roles for Key Vault

Azure Key Vault supports two authorization models: access policies (legacy) and Azure RBAC (recommended).

Key built-in roles:

RoleScopePermissions
Key Vault AdministratorVaultFull management of all vault objects
Key Vault Secrets OfficerVaultManage secrets (CRUD)
Key Vault Secrets UserVaultRead secret values
Key Vault Crypto OfficerVaultManage keys
Key Vault Crypto UserVaultUse keys for crypto operations
Key Vault Certificates OfficerVaultManage certificates
# Assign "Key Vault Secrets User" to a managed identity
IDENTITY_PRINCIPAL_ID=$(az identity show \
  --name my-identity \
  --resource-group rg \
  --query principalId -o tsv)

az role assignment create \
  --role "Key Vault Secrets User" \
  --assignee-object-id $IDENTITY_PRINCIPAL_ID \
  --assignee-principal-type ServicePrincipal \
  --scope $(az keyvault show --name myvault --resource-group rg --query id -o tsv)

Bicep — RBAC Assignment

@description('Principal ID of the managed identity')
param principalId string

resource kvRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(keyVault.id, principalId, 'Key Vault Secrets User')
  scope: keyVault
  properties: {
    roleDefinitionId: subscriptionResourceId(
      'Microsoft.Authorization/roleDefinitions',
      '4633458b-17de-408a-b874-0445c86b69e6' // Key Vault Secrets User
    )
    principalId: principalId
    principalType: 'ServicePrincipal'
  }
}

6.3 Migrating from Access Policies to RBAC

# Step 1: Enable RBAC authorization (access policies will be ignored)
az keyvault update --name myvault --resource-group rg --enable-rbac-authorization true

# Step 2: Create equivalent RBAC role assignments for all existing access policies
# Step 3: Verify access from each application
# Step 4: Remove legacy access policies to avoid confusion

Important: When enableRbacAuthorization is set to true, all access policies are ignored. Ensure RBAC assignments are in place before enabling this flag.

C# — Full example with User-Assigned Managed Identity:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

// Specify the client ID of the user-assigned managed identity
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
    ManagedIdentityClientId = "<user-assigned-identity-client-id>"
});

var client = new SecretClient(
    new Uri("https://myvault.vault.azure.net"),
    credential);

KeyVaultSecret secret = await client.GetSecretAsync("database-connection-string");

7. Monitoring and Diagnostics

7.1 Diagnostic Settings for Key Vault

Enable diagnostic logs to capture all access attempts, including those blocked by the firewall.

# Create Log Analytics workspace (if not existing)
WORKSPACE_ID=$(az monitor log-analytics workspace show \
  --resource-group rg \
  --workspace-name kv-logs \
  --query id -o tsv)

# Enable diagnostics on Key Vault
az monitor diagnostic-settings create \
  --name kv-diagnostics \
  --resource $(az keyvault show --name myvault --resource-group rg --query id -o tsv) \
  --workspace $WORKSPACE_ID \
  --logs '[
    {"categoryGroup": "allLogs", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}}
  ]' \
  --metrics '[
    {"category": "AllMetrics", "enabled": true, "retentionPolicy": {"enabled": true, "days": 90}}
  ]'

7.2 KQL Queries for Access Denied Events

All failed requests (403 Forbidden)

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where ResultSignature == "Forbidden" or httpStatusCode_d == 403
| project TimeGenerated, CallerIPAddress, OperationName, 
          ResultDescription, Resource, identity_claim_appid_g
| order by TimeGenerated desc
| take 100

Network-denied requests (firewall blocks)

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where ResultDescription contains "request is not allowed"
| summarize Count=count() by CallerIPAddress, bin(TimeGenerated, 1h)
| order by Count desc

Detect access from unexpected IPs

let allowedIPs = dynamic(["10.0.1.0/24", "10.0.2.0/24", "10.0.3.4"]);
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where httpStatusCode_d == 200
| where not(ipv4_is_in_any_range(CallerIPAddress, allowedIPs))
| project TimeGenerated, CallerIPAddress, OperationName, identity_claim_appid_g

7.3 Azure Policy for Enforcing Private Endpoints

Use built-in policies to enforce network security at scale:

# Assign built-in policy: "Azure Key Vault should disable public network access"
az policy assignment create \
  --name "kv-disable-public-access" \
  --display-name "Key Vault must disable public network access" \
  --policy "405c5871-3e91-4644-8a63-58e19d68ff5b" \
  --scope "/subscriptions/<subscription-id>" \
  --params '{"effect": {"value": "Deny"}}'

# Assign built-in policy: "Key vaults should use private link"
az policy assignment create \
  --name "kv-require-private-link" \
  --display-name "Key Vault must use private endpoints" \
  --policy "a6abeaec-4d90-4a02-805f-6b26c4d3fbe9" \
  --scope "/subscriptions/<subscription-id>" \
  --params '{"effect": {"value": "Audit"}}'

8. Migration Strategy: Public to Private Access

8.1 Phased Migration Without Downtime

Moving from public to private access requires careful sequencing to avoid breaking existing applications.

Phase 1: Prepare (no impact)

# 1. Deploy private endpoint and DNS zone
# 2. Verify DNS resolution from within the VNet
nslookup myvault.vault.azure.net
# Expected: resolves to 10.0.3.4 (private IP)

# 3. Enable VNet integration on all consuming services
# 4. Test application connectivity (vault is still publicly accessible)

Phase 2: Add firewall rules (minimal impact)

# Add VNet rules for all subnets that need access
az keyvault network-rule add --name myvault --resource-group rg --subnet $APP_SUBNET_ID
az keyvault network-rule add --name myvault --resource-group rg --subnet $AKS_SUBNET_ID

# Add IP rules for any external access (CI/CD, developer machines)
az keyvault network-rule add --name myvault --resource-group rg --ip-address $CICD_IP

# Set default action to Deny
az keyvault update --name myvault --resource-group rg --default-action Deny

Phase 3: Disable public access (full lockdown)

# Only after confirming all services work via private endpoint
az keyvault update \
  --name myvault \
  --resource-group rg \
  --public-network-access Disabled

8.2 Testing Connectivity

# From within a VM in the VNet
nslookup myvault.vault.azure.net
# Should return: 10.0.3.4

# Test secret retrieval
az keyvault secret show --vault-name myvault --name test-secret

# From App Service (Kudu console / SSH)
nameresolver myvault.vault.azure.net
# Should return the private IP

# From AKS pod
kubectl exec -it my-app -- nslookup myvault.vault.azure.net

C# connectivity test:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using System.Net;

// Verify DNS resolution
var addresses = await Dns.GetHostAddressesAsync("myvault.vault.azure.net");
Console.WriteLine($"Resolved to: {string.Join(", ", addresses.Select(a => a.ToString()))}");

// Verify secret access
var client = new SecretClient(new Uri("https://myvault.vault.azure.net"), new DefaultAzureCredential());
var secret = await client.GetSecretAsync("test-secret");
Console.WriteLine("✓ Secret retrieved successfully");

8.3 Rollback Plan

If issues arise after disabling public access:

# Immediate rollback — re-enable public access
az keyvault update \
  --name myvault \
  --resource-group rg \
  --public-network-access Enabled \
  --default-action Allow

# This takes effect within 1-2 minutes
# Private endpoint continues to work alongside public access

Tip: Keep the rollback command in a runbook. The private endpoint remains functional even when public access is re-enabled, so there is no regression for services already using the private path.


9. Multi-Region and Disaster Recovery

9.1 Key Vault is Regional

Azure Key Vault is a regional service — there is no built-in geo-replication. For multi-region architectures:

Option A: Vault per region with synchronized secrets

East US (kv-eastus, PE: 10.0.3.4) ◄──sync──► West US (kv-westus, PE: 10.1.3.4)

Synchronize secrets using Azure DevOps pipelines or a custom Function App that copies secrets between vaults on a schedule.

Option B: Single vault with cross-region private endpoints

Private endpoints can be created in VNets in different regions from the Key Vault. Traffic routes over the Microsoft backbone.

# Create PE in West US VNet pointing to East US Key Vault
az network private-endpoint create \
  --name kv-pe-westus \
  --resource-group rg-westus \
  --vnet-name vnet-westus \
  --subnet pe-subnet \
  --private-connection-resource-id $KV_EASTUS_ID \
  --group-id vault \
  --connection-name kv-cross-region-connection

9.2 Private Endpoint Considerations for DR

ScenarioApproach
Active-ActiveVault per region, each with local PE
Active-PassivePrimary vault with PE in both regions
FailoverDNS-based routing; update app config to point to DR vault

9.3 Backup and Restore

# Backup a secret (downloads encrypted blob)
az keyvault secret backup --vault-name kv-eastus --name my-secret --file my-secret.bak

# Restore to DR vault (must be in same subscription and geography)
az keyvault secret restore --vault-name kv-westus --file my-secret.bak

Limitation: Key Vault backup/restore only works within the same Azure geography (e.g., US, Europe). Cross-geography restore is not supported.


10. Common Pitfalls, Troubleshooting, and Best Practices

10.1 DNS Resolution Issues

Symptom: Application gets 403 Forbidden or connection timeout after enabling private endpoint.

Root cause: DNS is still resolving to the public IP.

Diagnosis:

# From the failing resource
nslookup myvault.vault.azure.net

# Expected (correct):
# myvault.vault.azure.net → CNAME → myvault.privatelink.vaultcore.azure.net → 10.0.3.4

# Actual (broken):
# myvault.vault.azure.net → 52.x.x.x (public IP)

Fixes:

  1. Ensure the Private DNS Zone is linked to the VNet where the client resides.
  2. For App Service: set WEBSITE_DNS_SERVER=168.63.129.16 and WEBSITE_VNET_ROUTE_ALL=1.
  3. For AKS: link the Private DNS Zone to the AKS VNet (not just the PE VNet).
  4. For on-premises: configure conditional forwarder for vaultcore.azure.net → Azure DNS Private Resolver.

10.2 403 Forbidden Errors

CauseDiagnosticFix
IP not in firewall allow listCheck CallerIPAddress in diagnosticsAdd IP rule
VNet rule missingCheck subnet ID in network rulesAdd VNet rule
Service endpoint not enabled on subnetaz network vnet subnet showEnable Microsoft.KeyVault endpoint
RBAC role not assignedCheck role assignments on vaultAssign appropriate role
Access policy missing (legacy mode)Check access policiesAdd access policy or migrate to RBAC
Managed identity not propagatedWait 5-10 minutes after creationRetry; check principal ID

10.3 Private Endpoint Connection States

az keyvault private-endpoint-connection list \
  --vault-name myvault --resource-group rg -o table
StateMeaningAction
PendingAwaiting approvalApprove the connection
ApprovedActive and workingNone
RejectedDenied by vault ownerRe-create or approve
DisconnectedPE deleted or brokenRe-create PE

10.4 Best Practices Checklist

10.5 Performance Considerations


Summary

LayerControlPurpose
NetworkPrivate Endpoint + FirewallBlock unauthorized network paths
IdentityManaged IdentityPasswordless authentication
AuthorizationAzure RBACLeast-privilege access
MonitoringDiagnostic Logs + KQLDetect and alert on anomalies
GovernanceAzure PolicyEnforce standards at scale

By combining private endpoints, managed identities, RBAC, and monitoring, you achieve a defense-in-depth posture for your most sensitive assets — with no secrets stored in application code and no exposure to the public internet.