Key Vault — one place for the dangerous strings
This bootcamp has spent four classes teaching you where secrets must not live: not in Git (Class Twenty), not in Terraform state left unguarded (Twenty-One), not echoed in pipelines (Twenty-Two), not baked into image layers (Twenty-Six). The affirmative answer is Azure Key Vault — a hardened store for three kinds of dangerous material: secrets (connection strings, API keys, passwords — arbitrary strings, versioned), keys (cryptographic keys that never leave the vault; you send it work, it signs and decrypts inside), and certificates (with lifecycle management — renewal and, paired with Class Thirty's proactive checks, the end of expiry surprises).
- Key Vault
- A per-application store for secrets, keys, and certificates — access-controlled by Entra ID and RBAC, every read and write audit-logged once its diagnostic settings route to a workspace (the Class Twenty-Eight valve; closed by default, opened in Lab 1), so "who can see the database password" becomes a role assignment you can list instead of a rumour you can't.
Two design points carry the class. First, RBAC on the vault itself: the vault is only as good as its door, and the door is Class Eight — data-plane roles like Key Vault Secrets User (read secrets) and Key Vault Secrets Officer (manage them), assigned per identity, per vault.1 The clean pattern stacks three earlier classes: the app's managed identity (Class Nine) gets Secrets User on its own vault — no credential needed to fetch credentials, and the storefront's identity cannot read the payment vault's contents. Second, apps never handle vault plumbing: App Service and Container Apps support Key Vault references — the app setting contains a pointer, the platform fetches the value at start using the managed identity, and the code just reads its configuration. Rotation becomes a vault operation; the app never redeploys, never knows.
Play it through
Three minutes, two gates. Stop a secret before it enters history, then gate the merge so nothing slips past a fork or a rushed review. It plays on its own and stops when it needs your hands.
Least privilege in pipelines — the audit
Phase Three built machinery with real power: identities that deploy to production, tokens that write to your repositories, runners that execute whatever workflows say. This section is the audit you now know enough to run — four questions, asked of every pipeline, each one a callback:
# the pipeline least-privilege audit — run it quarterly
1. What can each service principal reach? (Class 8)
→ Contributor on its own resource group, never the subscription.
2. Which runs can become each principal? (Class 23)
→ subject claims: environment:production, never pull_request.
3. What can the GITHUB_TOKEN do? (Class 24)
→ permissions: contents: read at the top; more only where earned.
4. Who reviews the files that grant all this? (Class 22)
→ .github/ behind CODEOWNERS and required review.
Power accumulates; audits subtract.
The reason this is a recurring audit and not a setup task: privilege only ever drifts upward. Every incident adds a temporary permission somebody forgets to remove; every new feature widens a role "for now"; every convenience argues for Contributor at the subscription. Nothing in the system pushes back except a human with a checklist and a calendar entry — and the checklist above takes an hour a quarter. The interviewer's version of this section is one question — "what could an attacker do with your CI?" — and the strong answer walks these four lines and ends with: less than they could last quarter.
Least privilege that expires
Class Eight gave you least privilege in space: who may touch what, scoped to the narrowest resource that still lets the work happen. It said nothing about time. The Owner role granted at 2am to fix an incident is still Owner at noon the following Tuesday, and the month after that, quietly attached to an account nobody remembers elevating. Standing privilege is the liability the §2 audit keeps finding, because an account that is always an administrator is always worth stealing — and the attacker who lands on it inherits every hour of access it was never using.
Privileged Identity Management (PIM) makes high privilege something you activate rather than something you hold. An engineer is made eligible for a role — Owner on a subscription, say — but carries it inactive. When the work needs it, they activate: with a written justification, a time box that expires on its own (four hours, not forever), and, where it is set, an approver and a fresh MFA prompt. The blast radius of a stolen credential collapses from "everything, always" to "nothing, until someone activates and someone approves." PIM is an Entra ID P2 capability, so it carries a licensing cost worth naming in a design review — but for the handful of roles that can end a company, it is among the cheapest risk reductions on the menu.
- Eligible vs active
- Eligible means "allowed to become"; active means "is, right now." Day to day, your most powerful roles should sit eligible and unused — a role held but not activated cannot be abused in your sleep.
- Activation
- The act of claiming an eligible role: justification, a self-expiring window, and — for the roles that warrant it — approval and MFA at the moment of use, which is exactly when proof of identity is worth demanding.
- Access reviews
- A scheduled recertification: every so often, each assignment's owner confirms it is still needed or it lapses. This is the §2 audit pointed at people instead of pipelines — privilege drifts upward here too, and only a calendar pushes back.
- Break-glass accounts
- Two excluded emergency accounts, cloud-only, outside PIM and Conditional Access, stored offline and heavily alerted — the way back in the day the identity system itself locks you out. Guard them like the spare key they are.
The through-line from §2 is exact. There you subtracted power from pipelines on a quarterly walk; here you subtract it from people on a schedule the tooling enforces. Both answer the same interview question — "what could an attacker do with a credential from your estate?" — and the strong answer is the same shape either way: less than they could a quarter ago, and nothing at all until a human justified it and a clock allowed it.
The official page, and a CAMPUX overview
About Azure Key Vault
learn.microsoft.com/azure/key-vault/general/overview
A secret moved from app setting to vault reference, push protection bouncing a key at the terminal, and a look at an eligible-but-inactive PIM role mid-activation will live here. Video to be added.
Store a secret, and feel the door work both ways
You gated the commit and the merge above. Now store a real secret and feel the door work both ways. Create a vault in RBAC mode, get denied by your own vault, grant yourself the right role, and read the secret back — the §1 door, experienced from both sides.
Create the vault (names are global — adjust yours):
az group create --name rg-vault-lab --location eastus az keyvault create --name campux-kv-<initials> \ --resource-group rg-vault-lab \ --enable-rbac-authorization true
What to notice: the RBAC flag is the §1 decision — the vault's door is now Class 8 role assignments, auditable and consistent with everything else, rather than a separate legacy policy system.Try to store a secret immediately:
az keyvault secret set --vault-name campux-kv-<initials> \ --name db-connection --value "Server=campux-sql;Password=Rosebud!"
On screen: Forbidden — denied by your own vault. Creating a vault grants management of the resource, not access to its contents; the control plane and the data plane have separate doors. This denial is the security model working, and most engineers meet it as a confusing error instead of a lesson.Grant yourself the data-plane role, wait a minute, and retry:
az role assignment create \ --assignee $(az ad signed-in-user show --query id -o tsv) \ --role "Key Vault Secrets Officer" \ --scope $(az keyvault show --name campux-kv-<initials> \ --query id -o tsv) # wait ~60s for the assignment to propagate, then re-run step 2What to notice: the role is scoped to this one vault — not the resource group, not the subscription. An app would get the narrower Secrets User (read-only); Officer is for humans who manage the contents.Read it back, then look at the shape of what you built:
az keyvault secret show --vault-name campux-kv-<initials> \ --name db-connection --query value -o tsv az keyvault secret list --vault-name campux-kv-<initials> -o table az group delete --name rg-vault-lab --yes --no-wait
The lesson: the secret now lives behind an Entra-controlled, audit-logged door, versioned, and rotatable without touching any app. In production the reader is a managed identity with Secrets User and the app setting is a Key Vault reference — the code never sees the vault at all. Delete the group when done; note the vault soft-deletes and lingers recoverable for a retention window, which is itself a safety feature.
Examination
Four drills, then two situations. The situations have no marking scheme — write your answer before you reveal the reasoning, or the exercise is worthless. Nothing is stored; this is between you and the page.
B — three classes stacked into one pattern. The managed identity (Class 9) means no credential is needed to fetch credentials; the scoped Secrets User role (Class 8) means this app can read this vault and nothing else; the reference means the code just reads configuration while the platform does the fetching, and rotation never touches a deploy. A is the joke that writes itself — securing the vault with a secret stored insecurely recreates the original problem one layer up. C survives until the first rotation, after which the settings hold a stale copy (and settings are far more readable than vaults). D hands a web app the subscription. When an interviewer asks "how do apps get secrets," B's three-part sentence is the expected answer, verbatim.
C — the control-plane/data-plane split, and it is a feature. Being able to create, configure, or delete a vault (control plane) deliberately does not imply being able to read what is inside it (data plane) — otherwise every subscription Contributor could read every password in the company, and "who can see the database password" would have a very long answer. The split is why an auditor can manage vault configuration without secret access, and why an app can read secrets without being able to reconfigure the vault. A, B, and D are invented mechanics. The interview shape of this drill: "does Contributor on the resource group let you read the vault's secrets?" — and the answer that gets the nod is no, and here is why that is the point.
# review: storefront app configuration
1. App runs on Container Apps with a system-assigned
managed identity.
2. App settings:
DB_CONNECTION = "Server=campux-sql;User=app;
Password=Xk9#mPq2!vLr"
3. The identity holds Key Vault Secrets User on
campux-kv-storefront.
4. Deployment is via the Class 23 OIDC pipeline with
environment approval on production.
Line two — and the tragedy is that lines one and three built the right answer, then nobody used it. The identity exists, the vault role is granted, and the password still sits in plaintext in app settings — where everyone with Reader-level portal access to the app can read it, where it lands in exported ARM templates and IaC state, and where rotation means a redeploy someone will postpone. App settings are configuration, not a vault: they are shown, copied, logged, and diffed by design. The infrastructure for doing this correctly is already present in the memo, which is the most common real-world shape of this bug — teams build the vault, then keep pasting values out of it.
The one-line fix: the setting becomes a Key Vault reference pointing at the secret in campux-kv-storefront; the platform resolves it through the managed identity at start, and the plaintext leaves the configuration forever. The distractors: system-assigned identities are standard on Container Apps (A), Secrets User — read-only — is exactly right for an app that consumes secrets (C — Officer would violate §2), and the approval gate is Class 23 working as designed (D). Review configuration the way scanners cannot: look for the secret that is present where only a pointer should be.
Eligible, not standing; time-boxed, not forever; recertified, not assumed. That is the whole shift: an Owner role granted at 2am now expires on its own instead of quietly outliving the incident, and the roles nobody uses stay inactive where a stolen credential can't reach them. The two rejects mark real boundaries. PIM subtracts privilege from people on a schedule the tooling enforces; the pipeline audit subtracts it from service principals on a schedule a human runs — the same shape, a different actor, and neither replaces the other. And break-glass accounts are deliberately kept outside PIM (and outside Conditional Access): the whole point of an emergency account is that it still works on the day the identity system holding PIM together is the thing that is down.
Concede the boundary, then name what it does not bound. The private network is real protection against a real class of attacker — external, network-borne — and dismissing it would be wrong. But plaintext settings are not exposed to the network; they are exposed to everything that legitimately crosses it: every engineer with portal Reader access, every IaC export and template diff, every Terraform state file, every screen-share and support ticket that includes a configuration dump, and every pipeline that deploys settings. The threat model for secrets was never primarily "attacker breaches the VNet"; it is "credential quietly copied by, or leaked through, one of the twenty legitimate paths" — and a committed key most often comes from inside the network, at a keyboard, tired, at the end of a long day.
Then price what the settings approach costs even with zero attackers. No audit trail: a vault logs every read with an identity attached — "who accessed the database password in May" is a query; with app settings it is unanswerable, which is exactly the Class Twenty-Three auditor conversation lost in advance. No rotation story: rotating a setting means finding every copy and redeploying, so rotation does not happen, so the credential ages for months or years unnoticed. No blast-radius control: settings are readable at the app's access level, while a vault scopes reads to one identity per Class Eight. Defence in depth is not paranoia; it is the assumption that one layer — any layer — will eventually fail, which the network layer occasionally does (a peering mistake, a misconfigured endpoint, a compromised laptop inside).
Close by shrinking the sprint. The objection's real energy is usually the cost, so cut it honestly: the vaults exist, the identities exist, and a Key Vault reference is a settings change, not an application change — the code keeps reading configuration exactly as before. Sequence it: the three credentials that matter most this week, the long tail behind them, done as each app next deploys. And give the teammate the sentence that resolves the whole argument: the network controls who can knock on the app's door; the vault controls who can read its keys — different questions, both worth answering.
Concede the real cost, then name what standing Owner does not buy. Activation adds seconds — a justification, maybe an approver, an MFA prompt — at the exact moment an incident is burning money, and waving that away as imaginary loses the engineer's trust before the conversation starts. But standing Owner does not buy speed in the case that matters most: the day the credential itself is the incident. A standing Owner grant is active every hour of every day whether anyone is on call or not, which makes it the single biggest thing an attacker can steal from the estate — and it buys that risk in exchange for saving a few seconds on the rare incident that actually needs it.
Offer the fix that solves the friction without buying the risk. Configure the on-call role's PIM activation to skip the approval step — self-activate, no second human in the loop — while keeping the time box and the MFA prompt, since the approver is usually the slow part, not the activation itself. A four-hour window covers nearly every incident and expires before anyone forgets it is on. If a specific class of incident is genuinely too urgent for even a self-service activation, that is what a break-glass account exists for, not a standing Owner grant — and using one is loud, alerted, and reviewed the next morning.
Close on the real disagreement. The objection was never about privilege; it was about speed. PIM's whole design already lets you buy speed with a configuration change — skip the approver, widen the window — in a way no standing grant can match on safety, because every one of those changes still leaves a record of who activated what, and when.
Five things worth carrying out of this class
- Key Vault is the affirmative answer to four classes of "not here": secrets, keys, and certificates behind an Entra-controlled, audit-logged door — with RBAC on the vault itself, and the control-plane/data-plane split as a feature, not a bug you route around.
- The working pattern is three classes stacked: managed identity (9) + scoped Secrets User (8) + Key Vault reference — the app reads configuration, the platform fetches the secret, rotation never touches a deploy.
- Privilege in pipelines only ever drifts upward. The four-question audit — principal scopes, subject claims, GITHUB_TOKEN permissions, who reviews .github/ — is a calendar entry, not a one-time setup task, and it takes an hour a quarter.
- Privileged Identity Management does for people what the audit does for pipelines: a role sits eligible and inactive until a human justifies it and a clock allows it, with access reviews recertifying whatever is left standing.
- Both mechanisms answer the same interview question — "what could an attacker do with a credential from your estate?" — and the strong answer is the same shape either way: less than they could a quarter ago.
- Key Vault's older access model — vault access policies — still exists and still guards many production vaults you will inherit; RBAC authorization is the recommended mode for new vaults and the one this class teaches. Know both names for interviews, and when you meet a policy-mode vault, treat migrating it as ordinary maintenance rather than an emergency. Also worth knowing: soft delete and purge protection, which make vault contents survive accidental — and malicious — deletion. ↩