The ticket
Repo leak. The SQL password is in plaintext. Junior “rotates” it by pasting a new string into appsettings and restarting IIS. Three other copies remain: a pipeline variable, a screenshot, last week’s backup VHD.
Key Vault holds secrets, keys, and certificates. The workload authenticates with a managed identity (system-assigned on pay-api, or user-assigned if several apps share it) — no client secret in config. Data plane: prefer Azure RBAC (e.g. Key Vault Secrets User) over the older access-policy model; do not mix both unless you enjoy debugging denials. Turn on soft-delete (recover a deleted secret/vault) and purge protection (block immediate purge). Control plane RBAC is who can touch the vault resource; data plane is who can read secret values.
Tenant techclick-lab.in · MG tc-root / tc-landing / tc-workloads · subs sub-hub sub-spoke-app · hub VNet 10.40.0.0/16 · spoke 10.41.0.0/16 · Azure Firewall 10.40.0.4 · VPN GW public 203.0.113.50 · on-prem FortiGate WAN 203.0.113.10. Not a live tenant.
Mental model
| Wrong | This lab | |
|---|---|---|
| Where the secret lives | appsettings / Git / ticket | kv-tc-lab secret pay-api-sql |
| How the app authenticates | Copied client secret | System-assigned MI on pay-api |
| Who may Get the secret | Everyone with VM login | MI → Secrets User (RBAC) |
| Delete accident | Gone forever | Soft-delete + purge protection |
No client-id/secret pair in the repo. Rotate the vault value; recycle the app.
How to choose
| Choice | Use | Skip when |
|---|---|---|
| System-assigned MI | One VM / one app, this lab | You need the same identity on many resources |
| User-assigned MI | Several compute share one identity | You want it to die with the VM |
| Vault RBAC | New vaults — recommended model | A legacy vault still on access policies (migrate, don’t mix blindly) |
Key Vault Secrets User
Runbook
Side A — vault
Create
kv-tc-labwith Azure RBAC, soft-delete, purge protection. Putpay-api-sqlin as a secret. Firewall the vault (private endpoint in the next lesson).Side B — identity
Enable system-assigned MI on the pay-api VM. Grant that object Key Vault Secrets User on the vault (or the secret, if you scope tight).
Side C — prove
From the VM, token + GET secret. Delete the value from appsettings and Git history plan. Rotate the vault version. App still works.
az keyvault show -n kv-tc-lab --query "[properties.enableSoftDelete, properties.enablePurgeProtection, properties.enableRbacAuthorization]" -o tsv # true # true # true az vm identity show -g rg-spoke -n pay-api --query principalId -o tsv # 33333333-3333-3333-3333-333333333333 az role assignment list --scope /subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-spoke/providers/Microsoft.KeyVault/vaults/kv-tc-lab -o table # Principal Role # 33333333-3333-3333-3333-333333333333 Key Vault Secrets User
Four failures
1 · Secret still in appsettings
Vault is decoration. Grep the disk and the repo.
2 · Access policy + RBAC both on
One allow, one deny, three opinions. Pick a model. Prefer RBAC on new vaults.
3 · Soft-delete off / purge protection off
Malware or a bad script purges the vault. You have no holdback.
4 · Human Owner can also Get secrets
Standing data-plane read for people. Use PIM / just-in-time, not Owner-as-reader.
How to prove it
1) MI principal has data-plane read. 2) App retrieves the secret without a copied credential. 3) Soft-delete and purge protection are on. 4) The old string is gone from config.
Traps
| Symptom | Look at |
|---|---|
| 403 from the vault | RBAC vs access-policy model, and control plane vs data plane |
| Works in portal, fails on VM | Your user has access; the MI does not |
| Deleted secret “gone” | Soft-delete recovery — unless you purged |
Knowledge check
Judgment items. One best answer. Reasons send you back to the matching section.
Azure security class series: Shared + Entra · Landing zone · NSG vs Firewall vs WAF · Conditional Access · Key Vault + MI · Private Link · Defender + Sentinel · Hub-spoke · Identity path · Interview
Sources
Related: Azure session factory · Migration series.