Azure Key Vault
Volume 4 — Azure Foundations
Key Vault is where secrets, encryption keys, and certificates should live instead of appsettings.json, a connection string hardcoded in a pipeline variable, or an environment variable set by hand in the Portal. This chapter covers the access model and — importantly — a caching behavior that has caused real confusion in Traxs deployments.
1. What Key Vault Stores
| Object type | Examples |
|---|---|
| Secrets | Connection strings, API keys, passwords — arbitrary string values |
| Keys | Cryptographic keys used for encryption/signing operations, managed without the key material ever leaving the vault |
| Certificates | TLS certificates, with Key Vault handling the private key material securely alongside the public certificate |
For the vast majority of day-to-day Traxs usage, Secrets is the relevant object type — ConnectionStrings--Default, SendGrid API keys, and similar values.
2. Access Policies vs. Azure RBAC
Key Vault historically used its own permission model (access policies), separate from Azure RBAC entirely — and can still be configured either way, which is a common source of confusion when checking "who can access this."
| Access Policies (legacy model) | Azure RBAC (recommended) | |
|---|---|---|
| Granularity | Per-vault, lists of principals and their allowed operations (get, list, set, delete) | Standard Azure role assignments (see the Azure RBAC chapter), scoped to the vault or a resource group |
| Consistency | A separate permission system, not visible in az role assignment list | Unified with every other Azure resource's permission model |
| Recommendation | Legacy — still supported, but Microsoft recommends migrating to RBAC | Current recommended approach for new vaults |
Check which model a given vault is using before troubleshooting access — az role assignment list will show nothing relevant for a vault still on the legacy access-policy model, which looks identical to "no access granted" until you check the right place.
az keyvault show --name traxs-vault --query "properties.enableRbacAuthorization"
# true = RBAC model, false/null = legacy access policies
3. Managed Identity Access — the Preferred Pattern
The same principle from the Azure Storage chapter applies here even more directly: an App Service accessing Key Vault should authenticate via its own managed identity, not a static credential — since the entire point of Key Vault is removing secrets from configuration, using a secret to access it would defeat the purpose.
# Enable a system-assigned managed identity on the App Service
az webapp identity assign --name roundtrip-api --resource-group traxs-prod
# Grant that identity permission to read secrets
az role assignment create \
--assignee "<app-service-managed-identity-object-id>" \
--role "Key Vault Secrets User" \
--scope "/subscriptions/<sub-id>/resourceGroups/traxs-prod/providers/Microsoft.KeyVault/vaults/traxs-vault"
4. Key Vault References in App Service Settings
Rather than an application reading secrets from Key Vault at runtime via SDK calls, App Service supports Key Vault references directly in application settings — the setting's value is a pointer to a Key Vault secret, and App Service resolves it transparently before the app ever sees it.
ConnectionStrings__Default = @Microsoft.KeyVault(SecretUri=https://traxs-vault.vault.azure.net/secrets/ConnectionStrings--Default/)
From the application's perspective, ConnectionStrings__Default is just a normal environment variable with the actual resolved value — the app code has no idea (and needs no code) to know it came from Key Vault at all. This requires the App Service's managed identity to have Key Vault Secrets User access, as set up in Section 3.
5. The Secret Caching Gotcha
This is the detail that has caused real confusion in Traxs deployments, and it's worth internalizing precisely:
App Service caches resolved Key Vault reference values. Updating a secret's value in Key Vault does not automatically propagate to an App Service already running with the old cached value.
Symptom: rotated a secret in Key Vault → App Service still behaves as if it has the old value
Expected fix that DOESN'T work: az webapp restart
Actual fix required: fully STOP then START the App Service (not just restart)
az webapp stop --name roundtrip-api --resource-group traxs-prod
az webapp start --name roundtrip-api --resource-group traxs-prod
A plain az webapp restart recycles the application process but does not reliably force App Service to re-fetch Key Vault reference values from scratch — only a full stop/start cycle reliably does. This is easy to miss because a restart looks like it should be equivalent, and in almost every other configuration-change scenario, it is — this is a specific, documented exception for Key Vault reference caching.
6. Secret Versioning
Key Vault keeps every previous version of a secret when it's updated, rather than overwriting in place:
az keyvault secret set --vault-name traxs-vault --name ApiKey --value "new-value"
az keyvault secret list-versions --vault-name traxs-vault --name ApiKey --output table
A Key Vault reference without a specific version pinned (as in Section 4's example, using just the secret name) always resolves to the current version — which is almost always what you want for application configuration, since it means a secret rotation doesn't require updating the reference itself, just the underlying value (subject to the caching behavior in Section 5).
7. Practical Rotation Workflow
# 1. Set the new secret value (creates a new version, old version still retained)
az keyvault secret set --vault-name traxs-vault --name ConnectionStrings--Default --value "new-connection-string"
# 2. Force App Service to actually pick it up
az webapp stop --name roundtrip-api --resource-group traxs-prod
az webapp start --name roundtrip-api --resource-group traxs-prod
# 3. Verify
az webapp log tail --name roundtrip-api --resource-group traxs-prod
8. Troubleshooting Playbook
| Symptom | Likely cause | What to check |
|---|---|---|
| Rotated a secret, App Service still behaves like the old value | Key Vault reference caching, restart alone doesn't clear it | Full az webapp stop then az webapp start, not just restart |
| App Service can't resolve a Key Vault reference at all — shows as unresolved/error in the Portal | Managed identity not granted access, or wrong access model (RBAC vs. legacy policy) checked | Confirm which access model the vault uses (Section 2); grant the correct type of permission accordingly |
403 Forbidden accessing a secret via CLI, but the Portal shows you as an Owner on the resource group | Vault is using the legacy access-policy model, and Owner-level Azure RBAC doesn't automatically grant vault data access under that model | Check enableRbacAuthorization; add an explicit access policy if the vault is still on the legacy model |
| Secret works for one App Service, not an identical one | Each App Service has its own distinct managed identity — a role/policy granted to one doesn't apply to another | Confirm access was granted to the correct App Service's specific managed identity object ID |
| Local development can't reach Key Vault at all | Local developer identity not granted access, or no az login session active locally | az login locally, and confirm the developer's own identity (not just the App Service's) has appropriate access if local dev reads from Key Vault directly |
9. Quick Reference
| Category | Item | Detail |
|---|---|---|
| Objects | Secrets / Keys / Certificates | What Key Vault stores |
| Access model | RBAC (recommended) vs. legacy access policies | Check enableRbacAuthorization before troubleshooting access |
| Preferred access | Managed identity + Key Vault Secrets User role | No static credential needed |
| App Service | Key Vault reference | @Microsoft.KeyVault(SecretUri=...) in an app setting |
| Critical gotcha | Secret rotation caching | Full stop/start required — restart is not sufficient |
| Command | az keyvault secret set | Create a new version of a secret |
| Command | az webapp stop / az webapp start | Force Key Vault reference re-resolution |
Part of the Traxs Engineering Handbook — Volume 4: Azure Foundations. Companion chapters in this volume: Azure CLI, Azure RBAC, Azure Networking, Azure Storage, Microsoft Entra ID.