01 / Problem
Build guardrails that can be reviewed before they write.
Subscription governance can change how every resource is evaluated. This lab makes the default posture deliberately conservative: audit first, keep access narrow, alert on cost signals and require an explicit path before policy can modify resources.
The second problem is lifecycle safety. Conditional resources, policy identities and incremental deployment can survive a parameter change, so deployment and cleanup have to share a deterministic inventory.
02 / Architecture
Policy, access, cost and protection in one baseline.
Three definitions, one initiative
Required resource-group tags and allowed locations default to Audit. Inherited missing tags default to Disabled.
Budget → action group
Actual and forecast thresholds provide signals. The example contains no receiver and a budget does not stop spending.
Governance resource group
A CanNotDelete lock defaults on. Optional group Reader access defaults off.
03 / Implementation
One deployment manifest follows the whole lifecycle.
Subscription-scoped orchestration composes modules for policy, the action group, budget, resource-group lock and optional access. Experimental Bicep assertions guard important parameter combinations at compile time.
Bash and PowerShell paths perform context checks, local compilation, authenticated validation, what-if and exact confirmations. Cleanup reads the named deployment manifest, verifies IDs, scope and ownership, removes dependent resources in order and refuses unexpected inventory.
baseline
Audit policy + Disabled inheritance
budget notifications
protected governance resource group
opt-in write path
Modify → managed identity → Tag Contributor
→ separately approved remediation04 / Decisions & trade-offs
Enforcement follows evidence.
- Audit-first defaults: the baseline observes drift before enforcing or changing resources. Stronger effects require a conscious parameter change.
- Remediation is separate: enabling Modify creates the identity and role assignment; existing resources are not changed until remediation is explicitly requested.
- Budget alerts are signals: actual and forecast notifications support action but cannot guarantee a spending ceiling.
- Conditional identity lifecycle: toggling away from Modify can retain deterministic role assignments under incremental deployment. Full guarded cleanup is required before switching states.
- Experimental assertions: stronger compile-time guards currently require Bicep language version
2.1-experimental.
05 / Validation
The repository proves shape and guard behavior.
- Public CI succeededThe linked main-branch run on 8 August 2026 compiled the subscription template and ran offline static, shell and PowerShell guard tests.Run 31267342614 ↗
- Negative paths are deliberateFixtures exercise subscription mismatch, failed gates, cleanup inventory changes and unsafe confirmation paths without touching Azure.Repository tests · offline only
- Authenticated Azure evidence is pendingNo source evidence proves validation,
what-if, deployment, policy evaluation, remediation, notification delivery, cleanup or cost.No live deployment claim
06 / Current status
Lab: CI validated; Azure deployment pending.
The modules, lifecycle scripts, failure guards and runbooks are authored. The next milestone is a bounded, authorized lab run with sanitized evidence for validation, what-if, deployed controls, evaluation behavior, optional remediation, notifications and complete teardown.
07 / What I learned
Configuration flags create lifecycle commitments.
A disabled condition does not necessarily remove what an earlier incremental deployment created. Policy Modify connects effect, identity, role assignment and remediation. Treating cleanup evidence as part of the design made those state transitions visible before a subscription was involved.