Governance
Organizational structure, classification, and resource controls.
What is governance?
Governance defines how cloud resources are organized and controlled.
It answers: what folders exist, what environments are available, how resources
are classified, and who pays.
Scope
Scope determines where governance operates — the organizational boundary.
| Scope |
Meaning |
GCP parent |
| Folder |
Sub-org governance under an existing folder |
folders/123456 |
| Organization |
Full org-level governance |
organizations/123456 |
Scope constrains which tiers are available.
Tiers
Tiers describe what governance features are active — the maturity level.
Following Google Cloud naming: Starter / Standard / Enterprise.
Tier × Scope matrix
| Feature |
Starter |
Standard |
Enterprise |
| Folder hierarchy |
Single root folder |
Environment folders (dev/prod) |
Full organizational tree |
| Classification |
— |
— |
Tag keys and dimensions |
| Billing export |
— |
— |
Billing account exported |
| Org-level exports |
At Organization scope |
At Organization scope |
OrgID exported downstream |
| Available at Folder scope |
✅ |
✅ |
❌ |
| Available at Organization scope |
✅ |
✅ |
✅ |
Enterprise requires Organization scope. Classification is a GCP org-level
resource — tag keys cannot be created at folder level. The OrgID export is
scope-driven: any tier deployed at Organization scope exports it.
Moving between tiers
Going up is additive and safe — Pulumi creates new resources:
- Starter → Standard: add environment children
- Standard → Enterprise: change scope to Organization, add dimensions
Going down is destructive — Pulumi deletes resources:
- Enterprise → Standard: removes tag keys, org exports
- Standard → Starter: removes environment folders
Domain model
plan.Plan Desired governance state (pure data)
scope.Scope Organization or Folder — determines capabilities
hierarchy.Config Folder tree definition
classification.Dimension Tag key / label axis
invariant.Rule / Check A named rule and its evidence-carrying outcome
gate.Gate / Policy Policy gate engine and execution barrier
Verifying that governance holds
invariant is the other half of a plan: a plan says what should be true,
invariant records whether it is. Each rule has a name and a number, carries
the evidence it observed (on success as well as on failure), and can report an
ANOMALY — "a human should look" — separately from a FAIL. Collapsing those two
forces a choice between crying wolf and staying silent.
b := invariant.New("environment-folders-present", 3)
if missing > 0 {
b.Failf("%d environment folders missing", missing)
} else {
b.Notef("all %d environment folders present", want)
}
check := b.Done()
Rules collected in a slice make a suite enumerable — countable, listable, and
checkable for whether every rule has a test of its own.
Policy Gates
gate provides deterministic release and mutation gates parameterized by policy:
- Strict: Fails if ANY rule fails or triggers an anomaly.
- Standard: Fails only if a rule fails (anomalies produce warnings).
- Permissive: Informational only; never blocks execution.
g := gate.New(gate.Strict)
g.Add(gate.FromCheck(check))
verdict, report := g.Evaluate()
if err := g.Enforce(); err != nil {
// Gate blocked
}
The Plan is the port. This module is pure Go with zero external
dependencies — provisioning adapters (Pulumi, Terraform, any cloud)
consume a validated Plan and create the actual resources.
Consumers & Load-Bearing Promises
Consumer Archetypes
- Policy evaluators: work running a rule set over a resource hierarchy and
reporting a verdict.
- Report and export consumers: downstream readers of a stored verdict that
must interpret it the same way months later.
Load-Bearing Promises
- The Verdict Takes The Worst: aggregation is worst-wins. A builder
starts at pass and can only be lowered by findings, never raised past one.
- A Failure Is Permanent: once a check fails, nothing later in the run
upgrades it. An anomaly raises a pass but never rescues a fail.
- Annotation Does Not Change The Verdict: adding a note or a label leaves
the status and the existing wording intact.
- Rules Run In Order, Output Is Sorted: evaluation is deterministic, and
failed and anomalous results come back in a stable order — so two runs over
the same input are diffable.
- Checks Marshal As Plain Strings: the serialised form is plain text
rather than an enum ordinal, so a stored verdict stays readable when the
code that wrote it has moved on.
- Invalid Input Is Rejected At Validation: missing names, missing parents,
empty children and duplicates are refused before evaluation rather than
producing a verdict over nonsense.