A CI/CD pipeline is not a YAML file — it is a DAG executor with a security
boundary bolted to the side. This lab builds the three mechanisms a principal must
own under the hood: the stage/job dependency graph that decides what runs and what
gets skipped when something fails, the OIDC token-exchange that lets a pipeline
authenticate to Azure with no stored secret (and rejects the wrong branch), and
the rollout controller that does blue-green and canary deploys with an automatic
health-gated rollback. Build these and "the pipeline is stuck" / "why did prod not
deploy" / "how does keyless auth actually work" stop being mysteries.
Stage + run_pipeline — a deterministic topological executor for a pipeline
DAG. Stages have depends_on edges, a condition (succeeded / always /
failed), and an optional approval gate. A stage is skipped when a dependency
didn't satisfy its condition, blocked when approval is required and not granted,
else it runs to succeeded / failed. Cycles and unknown dependencies raise.
exchange_oidc_token + FederatedCredential + subject_matches — the
workload-identity-federation decision. A token is exchanged for an Azure access
token iff a configured federated credential matches its iss + sub + aud.
Issuer and audience match exactly; subject matches exactly (with one documented
trailing-* pattern). Deny by default. A wrong-branch sub is rejected — the
security boundary.
blue_green_swap + canary_rollout — the deployment-strategy logic. Blue-green
returns the slot that ends up live (instant swap, instant rollback). Canary ramps
through traffic percentages, calls a health check at each, and rolls back to 0% on
the first failing step — no human paged.
pip install -r requirements.txt
pytest test_lab.py -v # against your lab.py
LAB_MODULE=solution pytest test_lab.py -v # against the reference
python solution.py # the worked example
You can explain why test_skip_on_failed_dependency returns skipped (not
failed) for deploy: a succeeded condition needs all deps green; a failed dep
means the condition is unmet, so the stage never runs.
You can explain the difference between always and failed conditions, and which
one a rollback stage uses vs a notify stage.
You can explain why test_oidc_reject_wrong_subject_branch is the whole point of
OIDC federation: same repo, same issuer, same audience — but a different sub
(branch) is denied, so a PR from a fork or a push to a feature branch cannot mint a
prod credential.
You can state, in one sentence, why OIDC federation beats a stored service-principal
secret (no long-lived credential to leak; the token is per-run and subject-scoped).
You can explain why canary_rollout returns final_pct: 0 (not the last step) on
rollback, and final_pct: 100 on success regardless of the last step value.
SLSA provenance: add attest_provenance(artifact_digest, builder_id) that emits a
signed in-toto-style statement, and verify_provenance that a deploy step calls
before promoting — model the "only deploy artifacts our builder produced" gate.
Matrix fan-out: extend Stage to expand a matrix={"os": [...], "ver": [...]}
into one job per combination, all depending on the same upstream — and assert the
Cartesian-product count.
Concurrency / fan-in: return not just statuses but a schedule (which stages
could run in parallel at each topological "level"), the thing that decides pipeline
wall-clock time.
Real az: configure a GitHub OIDC federated credential against an Entra app and
deploy with azure/login@v2 using permissions: id-token: write — no secret in the
repo. Then break it on purpose by deploying from a non-main branch and watch the
token exchange fail with AADSTS700213.
Built a deterministic CI/CD DAG executor (stages, dependsOn, conditions, approval
gates) that models Azure Pipelines / GitHub Actions execution and failure propagation.
Implemented an OIDC workload-identity-federation token-exchange evaluator that
authenticates pipelines to Entra ID with no stored secret and denies tokens whose
subject (branch/environment) is not the configured one.
Built blue-green and health-gated canary rollout controllers with automatic rollback.