The brief
The client's requirement is now the one senior engineers are actually hired to satisfy: "Nobody should deploy by clicking, ever. Every change to our infrastructure arrives as a reviewed proposal, ships to a test environment automatically, and reaches production only when a named person approves it — and if our whole GitHub organisation leaked tomorrow, no attacker should gain a single credential to our Azure estate." That last clause is the one most portfolios cannot honour, and it is the reason this build exists. Anyone can wire a pipeline; the interview separates candidates on whether the pipeline holds a secret.
- Deliverable
- The Build II environment expressed as Bicep, deployed by a GitHub Actions pipeline that plans on every pull request and gates production behind an approval — authenticating to Azure by OIDC federation, with gh secret list returning empty.
This build consumes the two before it. The estate is Build I's; the network and private storage are Build II's; here they stop being things you clicked and become things a file asserts and a robot enforces.1 Budget a full weekend — the OIDC trust and the environment gate each have a fiddly first-time setup, and getting them wrong is itself part of the learning the interviewer questions reward.
Requirements — the ledger you build against
Rows in dependency order. R5 is the row the whole build is really about; everything above it is scaffolding for the claim it makes.
| # | Requirement | Taught in | Evidence |
|---|---|---|---|
| R1 | The Build II environment as Bicep — VNet, subnets, storage, private endpoint, DNS zone — composed from modules, in a Git repository | Class 20 | The repository; a main.bicep that composes named modules rather than one long file |
| R2 | Branch protection on main: pull request required, one approval, force-push blocked, a status check required to pass | Classes 18–19 | The ruleset screenshot; a direct push to main refused |
| R3 | A validation workflow: on every pull request, build the Bicep and post a what-if — the reviewer approves a named list of changes, not a hope | Classes 20, 22 | A pull request showing the what-if output as a check; a broken template turning the check red |
| R4 | A deploy workflow: on merge to main, deploy to nonprod; then request production and stop for approval | Classes 22–23 | One run showing nonprod deployed automatically and production waiting on a human |
| R5 | OIDC federation to Azure — two service principals, each with a federated credential and resource-group-scoped Contributor; no client secret anywhere | Class 23 | gh secret list returning empty; the federated-credential subjects shown; a successful keyless deploy in the logs |
| R6 | Environment protection: the production environment carries a required-reviewer rule, and its credential trusts only the environment:production subject | Class 23 | The approval gate firing; the subject claim shown to be environment-scoped, not branch-wide |
| R7 | Pipeline hygiene: timeout-minutes on every job, a least-privilege permissions block, and concurrency control on the deploy | Class 24 | The three settings visible in the workflow files; one README paragraph on why each is there |
Nothing stored, nothing to steal.
The boundaries. Do not over-build: no monitoring, no cost alerts, no AI — those are Build IV and Phase Four, and adding them here dilutes the one story this build tells cleanly. Do not fake R5: a client secret hidden in a repository secret and called "temporary" is the exact anti-pattern Class Twenty-Three exists to kill, and a reviewer who runs gh secret list finds it in four seconds. The empty list is the deliverable; everything else is how you earn the right to show it.
The rubric — how a reviewer marks it
The bands, sharpened for a build whose subject is trust.
- Functional — the floor
- The pipeline plans on a pull request, deploys to nonprod on merge, gates production, and authenticates by OIDC with no stored secret. The loop closes; the story is thin.
- Reasoned — the target
- Functional, plus the README defends the shape: why the what-if is posted before approval, why two service principals instead of one, why the production subject is environment-scoped. The difference between wiring a pipeline and designing one.
- Professional — the ceiling
- Reasoned, plus the pipeline audit run on your own repository — the four Class Thirty-One questions answered in writing — and evidence that the gate cannot be bypassed: a feature-branch run shown failing to reach the production credential because its subject does not match. This is the version that ends the interview early.
The capping deduction is the build's whole thesis in one line: any stored long-lived credential — a client secret, a publish profile, a storage key — caps the build at Functional, no matter how elegant the rest. The pipeline that holds a secret has not solved the problem; it has relocated it.
What an interviewer will ask
The questions this artifact provokes, in arrival order. R5 draws most of them, which is why it draws most of the marks.
- "How does your pipeline authenticate to Azure?"
- The whole Class Twenty-Three handshake, aloud: GitHub signs a token about the run, Entra checks it against a federated credential, and issues minutes of access — nothing stored on either side. Walk the three actors and you have answered the question most candidates cannot.
- "What does the subject claim protect?"
- It is the filter deciding which runs may become which principal. Environment-scoped for production means only a run that already passed the approval gate carries the subject the credential trusts — the gate and the key are the same object.
- "Why post the what-if before approval?"
- Because approving a pull request should mean approving a named list of creates, changes, and deletes — not trusting that the diff does what its author believes. The delete line is the one that justifies the whole practice.
- "A feature branch is compromised — what can it reach?"
- Nonprod at most, and only if its subject matches; production's credential shrugs, because the branch's token subject is not environment:production. If you tested this yourself, say so — proven blast-radius limits read as seniority.
- "What would you add before calling this production-ready?"
- Observability and cost guardrails over the whole loop — which is precisely Build IV, named as the deliberate next step. Scope chosen, not stumbled into, for the third build running.
The page Campux shows interviewers
Campux built this exact loop across Phase Three: the environment became Bicep in Class Twenty, the validation workflow landed in Class Twenty-Two, and Class Twenty-Three closed the credential question with federation — gh secret list returning empty, described there as "the artifact worth walking an interviewer through slowly." This build is you earning that same empty list. When you reach Build IV, this pipeline is what grows monitoring and a budget; keep it, because the observability you add next has to watch something, and this loop is the something.
- If Builds I and II were torn down, you can rebuild their Azure side from this build's Bicep once R1 is written — which is the entire promise of infrastructure as code arriving for real. The federated-credential setup has a genuine ordering trap: the app registration and its federated credential must exist before the first workflow run, or the login step fails with an error that looks like a permissions problem and is actually a timing one. The README should note the order you found. ↩