Case study / Governance as code

Azure Governance Automation Lab.

An audit-first personal lab for expressing subscription policy, bounded access, cost notifications and lifecycle safety as reviewable Bicep and guarded scripts.

Status: Lab: CI validated; Azure deployment pendingRole: Governance design, IaC & testsScope: Personal lab

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.

Offline Bicep and guard tests precede reviewed Bash and PowerShell operations. The authored subscription baseline contains an audit-first policy initiative, budget notifications and a governance resource group protected by a CanNotDelete lock.
Prepared architecture; no authenticated deployment or runtime evidence. Select the diagram for a scalable view.

Policy baseline

Three definitions, one initiative

Required resource-group tags and allowed locations default to Audit. Inherited missing tags default to Disabled.

Cost notifications

Budget → action group

Actual and forecast thresholds provide signals. The example contains no receiver and a budget does not stop spending.

Protected control plane

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 remediation

04 / 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.

Where would you like to go?