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, 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.
Defender for Cloud — posture as a score
Security work has a measurement problem: "are we secure?" has no yes. Microsoft Defender for Cloud answers a tractable question instead — how does this estate compare against a checklist of known-good configuration? — and renders the answer as the secure score: a percentage, built from recommendations it continuously evaluates against everything in the subscription. Storage accounts allowing public access, VMs missing patches, vaults without purge protection, subscriptions with too many owners: each finding docks points, each fix earns them back, grouped so that the identity findings (usually the heaviest) sit at the top.
The score's real value is political as much as technical. Security investment is famously hard to argue for — its successes are invisible — but a number that was 43 and is now 71 is a progress bar leadership can read, and the recommendation list under it is a pre-prioritised backlog with the reasoning attached. Treat it as a score you can argue with: some recommendations will not apply to you (a dev subscription need not meet production bars), and Defender lets you exempt them with a documented reason — which is not cheating; it is the argument, recorded. The free tier of this posture management costs nothing and is on by default; the paid Defender plans add active threat detection per workload type — including the Class Twenty-Six image scanning on ACR — and are a per-workload cost decision, not an all-or-nothing one.2
Supply chain — watching what you build on
Campux's storefront is Campux's code plus a few hundred packages written by strangers, built on base images assembled by other strangers. That inheritance is the supply chain, and it is where modern compromises actually arrive — not through your firewall but through a dependency's. Four scanners, all sitting in the GitHub estate you built in Phase Three, watch it — and the code you write on top of it — continuously:
- Dependabot
- Reads your dependency manifests, cross-references every package version against known CVEs, raises an alert per finding — and can open the fix PR itself, version bump ready for review. Your Class Twenty-Two CI then proves the bump breaks nothing, which is the quiet payoff of having CI at all.
- Code scanning
- Static analysis — GitHub's engine is CodeQL — reads your own code for vulnerable patterns: injection paths, unsanitised input reaching a query, secrets handled carelessly. It runs as a Class Twenty-Two workflow on every pull request, so the finding arrives as a review comment before the flaw merges — the cheapest moment any security bug will ever be fixed.
- Secret scanning
- Scans the repository — history included — for strings shaped like credentials, and push protection blocks the commit at push time, before the secret enters history at all. The Class Seventeen lesson made mechanical: the only good place to catch a committed secret is before it commits.
- Image scanning
- Defender inspects every image pushed to ACR against known vulnerabilities — Class Twenty-Six's rot problem, watched. The finding is almost never your code; it is a base-image layer, and the fix is the container discipline: rebuild on an updated base and redeploy.
Notice what all four have in common: they are free or nearly free to enable, and enabled once. The work is not running them — machines run them — but triaging what they find, which is why the Class Thirty lesson applies verbatim: an unowned scanner alert channel becomes wallpaper in a month. Route findings somewhere owned, and treat "Dependabot PR merged within a week" as a team habit with the same standing as code review.
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.
The posture story you tell in interviews
"Tell me about security in your last environment" is a question most candidates answer with a product list, which is exactly as convincing as answering "are you healthy?" with the contents of a medicine cabinet. The strong answer is a posture story with a shape: where the secrets live (a vault, RBAC'd, referenced by managed identity — nothing stored in code, settings, or pipelines); how you know your standing (a score, its trend, and the exemptions you argued); what watches the code and its supply chain (four scanners, each with an owner); and what bounds the blast radius (the §4 audit, run on a calendar). Four sentences, each backed by something you can screen-share.
End the story the way honest security people do: with the residual risk, named. "The workflow files are still the most powerful text in the company, which is why they get the strictest review" — or whatever your estate's true soft spot is. Naming your own weakness, unprompted, is the highest-credibility move available in a security conversation, because it proves the posture is something you maintain rather than something you recite.3 Auditors, interviewers, and incident retrospectives all reward the same voice — Class Twenty-Three's auditor conversation taught it, and it transfers everywhere.
The 11pm secret, finally laid to rest
Class Nine told the story in passing: a Campux engineer, at 11pm, committed a storage key to the repository, and the "fix" was deleting the line — which, as Class Seventeen later made painfully clear, removes nothing from history. The key has been valid ever since. Nobody exploited it; nobody could prove nobody had. This class runs the full treatment, in the right order. Rotate first: the exposed key is regenerated, and the window of exposure is documented — commit date to rotation date, twenty-two months, a number that makes the room quiet. Then prevent recurrence: secret scanning finds two more forgotten strings in history (both rotated); push protection turns on, and the next 11pm mistake bounces at push time instead of entering history.
Then remove the reason it happened: a Key Vault per application, secrets loaded by reference through each app's managed identity, and the storage key that started it all is no longer in anything — the storefront's identity fetches it from the vault, and rotation is a vault operation the app never notices. Dependabot, code scanning, and image scanning switch on the same week, each finding routed to an owner per Class Thirty's rule. Defender's secure score reads 43 percent the day it is enabled — which is not a crisis but a baseline, and Situation 01 is the meeting where Campux decides what to do about it. The engineer who committed the key, for the record, still works there; the postmortem blamed the missing guardrails, not the tired human. That sentence is also part of the posture.
The official pages, and a CAMPUX overview
About Azure Key Vault
learn.microsoft.com/azure/key-vault/general/overview
What is Microsoft Defender for Cloud?
learn.microsoft.com/azure/defender-for-cloud/defender-for-cloud-introduction
A secret moved from app setting to vault reference, push protection bouncing a key at the terminal, and a first read of a 43-percent secure score will live here. Video to be added.
Store a 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.
Turn on the scanners, then feed one a known-bad dependency
Enable GitHub's four scanning protections, then deliberately add a dependency with published CVEs and watch Dependabot raise its hand.
In the repository: Settings → Advanced Security. Enable Dependabot alerts and Dependabot security updates.
What to notice: two switches — one to be told about vulnerable dependencies, one to let the robot open the fix PR itself. Free for public repositories, and among the highest-value clicks in this bootcamp.On the same page, enable Secret scanning and Push protection — and under Code scanning, choose CodeQL's Default setup.
What to notice: secret scanning covers existing history (the 11pm-secret problem); push protection guards the future — a recognised credential pattern in a push gets bounced with an explanation before it ever becomes the history Class 17 taught you is forever. The code-scanning switch quietly commits a CodeQL analysis that will now read every pull request's diff for vulnerable patterns in your own code.Feed the scanner: on a branch, add a requirements.txt declaring an old, known-vulnerable package version, and merge it:
echo "requests==2.19.0" > requirements.txt git add requirements.txt git commit -m "Add (deliberately old) dependency for lab" git push
What to notice: requests 2.19.0 is years old with published CVEs — a stand-in for the dependency your real project quietly pinned in 2023 and forgot.Wait a few minutes, then open the repository's Security tab → Dependabot alerts.
The lesson: alerts, each naming the CVE, its severity, and the version that fixes it — and if security updates are on, a ready-made PR bumping the pin. Merge the fix PR (your Class 22 checks run against it, which is the whole DevSecOps loop in one screen), then delete requirements.txt to leave the repo clean. Total cost: zero. Total excuse for not enabling this anywhere you work: also zero.
Treating the scanner like a failing test
A dependency scanner flags a known-vulnerable library before the code ever ships. Because security moved left into the pipeline, the fix is a pull request on a Tuesday, not an incident in production. You treat the scanner's output like a failing test — because that is exactly what it is — and the vulnerability never reaches a customer.
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.
Flag known-bad dependencies, block secrets at the door, scan image layers. The two rejects mark the scanners' honest edges. All three tools work from databases of known problems — published CVEs, recognised credential shapes — so a zero-day in a dependency or an unusual secret format walks past them; "no alerts" means "nothing known found", never "nothing wrong". And detection is not remediation: a secret already in history is compromised the moment it landed, and the only fix is rotation — the scanners' job is making sure there is no next one, which is exactly the order the case file ran. Know what your tools cannot do; it is the half of the posture story that makes the other half credible.
# 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 §4), 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.
First, defuse the number. Forty-three percent on day one is not a crisis; it is a baseline — every estate that enables Defender for the first time scores in this neighbourhood, because the checklist is long and nobody was being graded before. The alarming framing to correct is "we are 57 percent insecure"; the accurate framing is "we now have a prioritised, continuously-updated list of hardening work, which yesterday we did not." Leadership's alarm is fuel — spend it on a cadence, not a panic sprint.
The first points come from identity and exposure, and Defender already knows it. The score weights recommendations by impact, and the heavy items cluster predictably: MFA and excess Owner roles on the subscription (the single largest block in most estates, and hours of work, not weeks); anything publicly exposed that should not be — storage accounts with public access, management ports open to the internet, databases without firewall rules; and missing platform hygiene like encryption settings and vault purge protection. Work the list by points per hour: the top ten findings routinely carry a third of the missing score. Some of Campux's items will already be done — the Phase Three OIDC work and Class Twenty-Eight's policies score points the moment Defender notices them — which is worth showing leadership early: the score rises in week one, and momentum is a security control too.
Then run it like the alerting review, not like a death march. Triage the 140 into three piles: do (real findings, assigned owners, tracked like any backlog), argue (recommendations that do not apply — the dev subscription need not meet production bars — exempted with documented reasons, which Defender supports precisely so the score stays honest), and defer (real but low-impact, revisited quarterly). Set the target as a trend, not a number — "ten points a quarter" beats "100 percent by Q4", because the last points cost more than they protect, and a score chased for its own sake produces exemption abuse and checkbox theatre. The sentence for leadership: the score is the compass, the exemptions are the argument, and the trend is the deliverable.
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 the case file's own 11pm commit came from inside the network, at a keyboard, tired.
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 — twenty-two months, in Campux's own history. 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.
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.
- 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.
- Defender's secure score makes posture legible: a baseline, a prioritised backlog, and exemptions-with-reasons. The score is the compass, the trend is the deliverable — first points come from identity and public exposure.
- The code and its supply chain get four scanners — Dependabot, code scanning (CodeQL) on every pull request, secret scanning with push protection, image scanning — cheap to enable, useless without owners. Detection is not remediation: committed secrets get rotated, always.
- Privilege drifts upward; audit quarterly with the four questions — principal scopes, subject claims, GITHUB_TOKEN permissions, and who reviews .github/. The posture story ends with your residual risk, named out loud.
- 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. ↩
- Defender for Cloud's packaging shifts more than most Azure products — the free posture tier, the per-workload paid plans, and the names of both have all changed before and will again. Treat the architecture as stable (free continuous posture assessment plus opt-in paid threat protection per workload type) and check current plan names and prices before quoting them in a meeting. The secure score's exact arithmetic also evolves; the direction — weighted, argue-able, trend-tracked — is the durable part. ↩
- This class covers the security work a platform engineer owns day to day; it is not a security curriculum. Threat modelling, penetration testing, SIEM operations (Microsoft Sentinel — which runs on the Class 28 workspace and speaks Class 29's KQL), and incident forensics are their own disciplines with their own careers. The honest boundary to know and say: you are building the estate that makes a security team's job possible, not replacing the team. ↩