Skip to content

Azure Key Vault & Managed Identity

Key Points

  • Key Vault stores secrets, keys, certificates. RBAC or vault access policies.
  • Managed Identity is Azure AD identity for an Azure resource (App Service, Function, Container App, VM, AKS pod). System-assigned (tied to resource lifecycle) or User-assigned (independent; reusable across resources).
  • Combination: app uses managed identity → reads secrets from Key Vault → no secrets in config.
  • DefaultAzureCredential auto-discovers credentials in dev (CLI, VS) and prod (managed identity).
  • See also Secrets Management.

Concepts (deep dive)

Why these two services exist together

The historical problem these services solve is "secrets in config files." Connection strings, API keys, certificates, signing keys — they ended up in appsettings.json, in environment variables that leaked into screenshots, in git history that nobody could purge, and in keyboards-passwords-to-junior-devs handoffs. Key Vault and Managed Identity together replace that with zero-secret access:

   Old world                              New world
   ─────────                              ─────────
   appsettings.json                       Key Vault
   ├─ DbConn = "...; Pwd=..."             ├─ DbConn (RBAC: Secrets User)
   ├─ ApiKey = "..."                      └─ ApiKey
   └─ JwtSigningKey = "..."                  ▲
                                             │ "I am <app's managed identity>"
                                             │ (token from Azure metadata service,
                                             │  auto-rotated, never seen by code)
                                       ┌─────┴──────────┐
                                       │   Your app     │
                                       │  (App Service, │
                                       │   Function,    │
                                       │   Container    │
                                       │   App, AKS pod)│
                                       └────────────────┘
  • Key Vault is the vault: hardware-backed (HSM-grade in Premium) storage for secrets, keys, and certificates, with RBAC, audit, soft-delete, and versioning. Anything you wouldn't paste in a Slack channel belongs here.
  • Managed Identity is the credential: an Entra ID identity automatically attached to an Azure resource. The runtime reads a short-lived token from a local metadata endpoint; the token authenticates the app to Key Vault (and Storage, SQL, Service Bus, …) over RBAC. No password ever exists in the app's process.

The combination eliminates the failure modes of secret rotation (the platform rotates the identity's token; you only rotate the backing secret, never the credential the app uses). It's the single highest-leverage security improvement you can make in an Azure app.

Key Vault

az keyvault create -g rg -n mykv --enable-rbac-authorization
az keyvault secret set --vault-name mykv -n DbConnectionString --value "..."

Three object types: - Secrets: arbitrary strings (connection strings, API keys). - Keys: cryptographic keys (RSA, EC). Sign/encrypt operations. - Certificates: X.509 certs.

RBAC vs access policies

  • RBAC (recommended in 2026): standard Azure RBAC roles like "Key Vault Secrets User". Audit via Azure activity log.
  • Access policies (legacy): per-principal allow lists.

App reads secrets

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

var secret = await client.GetSecretAsync("DbConnectionString");
var connStr = secret.Value.Value;

Or as config provider:

builder.Configuration.AddAzureKeyVault(new Uri(vaultUri), new DefaultAzureCredential());
var connStr = builder.Configuration["DbConnectionString"];   // transparently fetched

Managed Identity

az webapp identity assign -g rg -n myapp                # system-assigned
az identity create -g rg -n myidentity                   # user-assigned
// In app:
var credential = new DefaultAzureCredential();
// Auto-discovers managed identity on Azure resources

DefaultAzureCredential chain

DefaultAzureCredential is the SDK's "figure out who I am" helper. It walks a list of credential providers in order and uses the first one that succeeds — so the same line of code works in dev (you're az login'd as yourself) and in prod (the app is a managed identity) without environment-specific branching. The trade-off: the chain is probing, which means a misconfigured environment can silently fall through to a slower or weaker credential. For prod code, consider pinning to ManagedIdentityCredential directly.

Tries in order: 1. EnvironmentCredential (env vars) 2. WorkloadIdentityCredential (AKS workload identity) 3. ManagedIdentityCredential (Azure resource identity) 4. AzureCliCredential (az login) 5. AzurePowerShellCredential 6. VisualStudioCredential 7. InteractiveBrowserCredential

Same code in dev (CLI logged in) and prod (managed identity). Magic.

Specific credentials

new ChainedTokenCredential(
    new ManagedIdentityCredential(),
    new AzureCliCredential());

For tighter control. Avoids the surprise of falling through to interactive in prod.

User-assigned vs system-assigned

Aspect System User
Lifecycle Tied to resource Independent
Sharing One resource Many resources
Recreation Recreates with resource Survives
When Single-use Reusable, AKS, multiple apps

Workload Identity (AKS)

metadata:
  annotations:
    azure.workload.identity/client-id: <aad-app-id>
spec:
  serviceAccountName: app-sa

Pod service account ↔ AAD app. No secrets in pod.

new DefaultAzureCredential()   // picks up workload identity

Connection strings via Key Vault references

# App Service config
az webapp config appsettings set -n myapp --settings \
  ConnectionString="@Microsoft.KeyVault(SecretUri=https://mykv.vault.azure.net/secrets/Db/abc)"

App Service auto-resolves at runtime; managed identity reads the secret.

Secret rotation

// IOptionsMonitor picks up rotated secret
public class C(IOptionsMonitor<DbOptions> opts) { /* ... */ }

Combine with Key Vault config provider's ReloadInterval.

builder.Configuration.AddAzureKeyVault(uri, cred,
    new AzureKeyVaultConfigurationOptions { ReloadInterval = TimeSpan.FromMinutes(5) });

For zero-downtime DB credential rotation: dual-key window.

Keys (cryptographic)

var keyClient = new KeyClient(uri, new DefaultAzureCredential());
var keyVaultKey = await keyClient.GetKeyAsync("my-rsa-key");

var crypto = new CryptographyClient(keyVaultKey.Value.Id, new DefaultAzureCredential());
var encrypted = await crypto.EncryptAsync(EncryptionAlgorithm.RsaOaep, plaintext);

Key never leaves Key Vault. Managed HSM tier for FIPS 140-3 Level 3.

Certificates

var certClient = new CertificateClient(uri, new DefaultAzureCredential());
var cert = await certClient.GetCertificateAsync("mycert");

App Service can mount KV-stored cert directly to TLS endpoint.

Pricing

  • Standard: per-operation. ~$0.03 / 10K ops.
  • Premium: HSM-backed; higher cost.
  • Managed HSM: FIPS 140-3 L3 isolated HSM.

For most apps, Standard. Compliance/regulated → Premium or Managed HSM.

Networking

  • Public (default).
  • Service endpoint (VNet allow-list).
  • Private endpoint (VNet-only access).

Production: private endpoint.

Soft delete + purge protection

az keyvault create --enable-soft-delete --enable-purge-protection

Deleted items recoverable for 90 days. Purge protection prevents permanent delete during retention. Enable both.

Audit

Diagnostic Settings → Log Analytics → KQL on AuditEvent.

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName == "SecretGet"
| summarize count() by identity_claim_appid_g, ResultSignature

Find rogue access patterns.

Common mistakes

  • No managed identity — secrets in config.
  • Access policies vs RBAC mixed — confusing.
  • Soft delete disabled — accidental purge irrecoverable.
  • No private endpoint — public access in prod.
  • DefaultAzureCredential falling through to interactive in prod (silent hang).

Cross-tenant scenarios

For multi-tenant apps: - User-assigned identity. - Federated Identity Credential — workload identity for cross-cloud (GitHub Actions, K8s).

Federated Identity Credentials (FIC)

az identity federated-credential create --identity-name mi --resource-group rg \
  --name github-actions --issuer https://token.actions.githubusercontent.com \
  --subject "repo:org/repo:ref:refs/heads/main" --audience api://AzureADTokenExchange

GitHub Actions authenticates to Azure AD without storing secrets. Same for AKS workload identity.


Code: correct vs wrong

❌ Wrong: secret in app settings

az webapp config appsettings set --settings DbPassword=hunter2

✅ Correct: Key Vault reference

az webapp config appsettings set --settings DbPassword="@Microsoft.KeyVault(SecretUri=...)"

❌ Wrong: vault access via key

new SecretClient(uri, new ClientSecretCredential(tenantId, clientId, clientSecret));

✅ Correct: managed identity

new SecretClient(uri, new DefaultAzureCredential());

Design patterns for this topic

Pattern 1 — "Managed identity + Key Vault"

  • Intent: zero-secret cloud auth.

Pattern 2 — "DefaultAzureCredential everywhere"

  • Intent: same code dev/prod.

Pattern 3 — "RBAC over access policies"

  • Intent: standard Azure model.

Pattern 4 — "Soft delete + purge protection"

  • Intent: safety.

Pattern 5 — "Federated Identity for CI/CD"

  • Intent: GitHub Actions without secrets.

Pros & cons / trade-offs

Aspect Pros Cons
Key Vault Centralized; audited Network dep
Managed Identity No secrets Azure-only
User-assigned Reusable More to manage
RBAC Standard model Migration from access policies

When to use / when to avoid

  • Always managed identity over connection strings.
  • Use Key Vault for any secret you can't eliminate.
  • Use Premium / Managed HSM for compliance.
  • Avoid access policies in new vaults.

Interview Q&A

Q1. System-assigned vs user-assigned identity? System: tied to resource. User: independent; reusable.

Q2. DefaultAzureCredential? Chain that auto-picks the right credential — managed identity in prod, CLI/VS in dev.

Q3. Why prefer RBAC over access policies? Standard Azure model; activity log audit; familiar.

Q4. Key Vault references in App Service? @Microsoft.KeyVault(SecretUri=...) — auto-resolved with managed identity.

Q5. Soft delete + purge protection? Recover deleted items; prevent permanent purge during retention.

Q6. Workload Identity (AKS)? Pod service account ↔ AAD app. No secrets in pod.

Q7. Federated Identity Credentials? External issuers (GitHub Actions, K8s) authenticate to Azure AD without secrets.

Q8. Key vs Secret? Key: crypto operations (sign/encrypt). Secret: arbitrary string. Key never leaves vault.

Q9. Managed HSM? Single-tenant HSM-backed Key Vault. FIPS 140-3 L3.

Q10. Audit Key Vault access? Diagnostic Settings → Log Analytics. KQL queries.

Q11. Reload secrets without restart? Key Vault config provider reload interval + IOptionsMonitor.

Q12. Common pitfall in dev? DefaultAzureCredential falling through to interactive auth — silent hangs. Use ChainedTokenCredential in prod.


Gotchas / common mistakes

  • ⚠️ Secrets in config — forever leaked.
  • ⚠️ No purge protection — accidental loss.
  • ⚠️ DefaultAzureCredential interactive in prod.
  • ⚠️ Public Key Vault in regulated env.
  • ⚠️ Access policies + RBAC mixed — confused permissions.

Further reading