# IaC governance

> Wire a repository so every Terraform or OpenTofu pull request is checked against 70 built-in rules, your own Rego, and priced budget guardrails before it merges.

Source: https://zop.dev/docs/zopnight/concepts/iac-governance

---

A lot of cloud waste is committed before it is provisioned. A pull request adds an oversized instance class, drops an encryption block, or opens a security group, and nobody sees the cost until the bill lands a month later. IaC governance moves that check left: ZopNight reads your Terraform or OpenTofu plan on every pull request, decides whether it passes your policies, and puts the answer (and the dollar figure) in the PR itself.

![Governance → IaC Validations: counters for governed repositories, attached policies, and blocking policies above the Validations tab, listing each scan's status, repository and ref, trigger, findings, monthly dollar figure, and age](https://storage.googleapis.com/zopdev-blog-resources/1/files/originals/20260924/fcc59f64-8c67-4955-bd5c-82d5a037a19e-iacgovernance.png)

*Governance → IaC Validations: every scan with its verdict, the findings behind it, and the monthly cost the change would add.*

**Note**

ZopNight decides; GitHub enforces. ZopNight reads your plan and returns a passed, advisory or blocked verdict. Your **GitHub Required Check** is what blocks the merge. ZopNight **never runs `apply`** and never holds credentials that could.

## Before you start

- **A GitHub repository** holding Terraform or OpenTofu code.
- **Rights in that repository** to merge a pull request and to mark a status check as required on the branches you want gated.
- **A ZopNight role with the `iac-policy` policies** to attach policies, and `iac-validation:view` to read scans. See [Permissions](#permissions).

## How it works

### What it reads

ZopNight reads two inputs, and each answers a different question.

| Input | Question it answers |
|---|---|
| `terraform plan` / `tofu plan` (the PR gate) | Does this change comply before it merges? |
| `show -json` state (the inventory scan) | Does what we already run still comply, and is any of it unmanaged? |

The inventory scan also links declared resources back to the resources ZopNight discovered in your cloud accounts, so you can see which live resources your IaC actually manages and which are **orphans** that nothing in code accounts for.

### Decision point only

Each scan returns a verdict and stops: the pull request gets a passed, advisory, or blocked result. Policies are attached per repository, and each is either advisory or blocking. Whether a blocked result stops the merge is decided by your own required check, so enforcement stays inside your pipeline.

## Wiring up a repository

**Run CI onboarding**

  Point ZopNight at the repository. It mints a **scoped scan token** (good for scanning, nothing else) and opens a pull request adding a Terraform- and OpenTofu-aware GitHub Action.

**Merge the onboarding PR**

  Once it is in, every subsequent pull request runs the scan automatically.

**Make the check required**

  In your repository settings, mark the ZopNight check as a **Required Check** on the branches you want gated. Until you do, the decision is advisory: it appears on the PR but does not block.

## Attach a policy

![The Attach policy drawer on its Rule step (of Rule, Configure, Scope, Review): rule categories from Cost / FinOps to Change on the left, a rule search, and rules such as Require tags, Encryption at rest, and No public buckets, each with its key, category, and severity](https://storage.googleapis.com/zopdev-blog-resources/1/files/originals/20260924/135ec6e5-d5c7-42a5-a46a-2bf51c16bee8-iacattachpolicy.png)

*Attaching a policy; pick the rule, configure it, choose what it applies to, and review. Severity is shown per rule before you commit to it.*

**Open Governance → IaC Validations → Attach policy**

  The drawer walks through four steps: **Rule**, **Configure**, **Scope**, and **Review**.

**Pick the rule**

  Browse by category or search, for example "encryption", "budget", or "tags". Each rule shows its key, category, and severity.

**Configure and scope it**

  Set the rule's parameters (for example a cost guardrail's threshold) and choose what it applies to.

**Review and attach**

  Check the summary, then attach the policy.

## Custom policies in Rego

When a built-in rule does not express what you need, write the policy yourself in **Rego**, evaluated by real OPA in a sandbox with no network and no clock access.

The engine is native-first: OPA is loaded only when one of your policies actually uses Rego, so an organisation running only built-in rules pays nothing for it.

Attach both kinds from **Governance → IaC Validations → Attach policy**. Your [tagging](https://zop.dev/docs/zopnight/concepts/smart-tags) and recommendation policies live under **Governance → Policies**.

## The rule catalogue

70 built-in rules ship out of the box, grouped into eleven categories:

| Category | What it covers |
|---|---|
| **governance** | Tagging, naming, and ownership requirements on declared resources. |
| **security** | Public exposure, open ingress, and unsafe defaults. |
| **iam** | Wildcard principals, over-broad policies, and privilege escalation paths. |
| **networking** | VPC, subnet, route, and peering shape. |
| **cost** | Priced guardrails; see [Cost guardrails](#cost-guardrails) below. |
| **reliability** | Single-AZ, missing backups, and no retention. |
| **provider** | Provider and version pinning. |
| **change** | Destructive or replace-in-place operations the plan would run. |
| **data** | Encryption, retention, and residency on data services. |
| **observability** | Missing logging, metrics, or flow logs. |
| **kubernetes** | Cluster and workload shape declared in code. |

Every rule is tagged with the compliance frameworks it maps to (CIS, PCI, SOC 2, HIPAA, NIST, ISO, FinOps), so a single scan produces evidence for whichever audit is asking.

## Cost guardrails

Cost governance runs in the same engine, against the **priced** plan, so the guardrail and the dollar figure arrive together:

| Guardrail | What it caps |
|---|---|
| `max_monthly_cost` | The projected monthly cost of any single resource the plan creates |
| `budget_cap_total` | The projected monthly cost of everything the plan creates |
| `max_cost_delta_*` | How much this change may move the bill, in absolute or relative terms |

When one trips, the pull request carries the `$/mo` figure that caused it. A reviewer does not have to reconstruct the number to decide whether the change is worth it.

## Overrides

A policy that can never be overridden gets worked around. When a change has to ship against a failing policy, an admin can record a **governed override**: the merge proceeds, and the override lands in the audit log with the actor, the policy, the reason, and the timestamp.

## The IaC Validations page

**Governance → IaC Validations** is where scans and policies come together. Three counters across the top summarise your coverage: how many **repositories** are governed, how many **policies** are attached, and how many of those are **blocking** rather than advisory.

Below them, two tabs:

| Tab | What it lists |
|---|---|
| **Validations** | Every scan run, with its verdict, the repository and commit it ran against, what triggered it, how many findings it raised, and the monthly dollar figure attached. |
| **Repositories** | Which repositories are wired up and their CI status. |

**Attach policy** is the action in the top right. It walks you through picking a rule, configuring it, scoping it, and reviewing before it goes live.

## Permissions

| Policy | Grants |
|---|---|
| `iac-policy:view` / `:create` / `:update` / `:delete` | Read and manage IaC policies |
| `iac-validation:view` | Read scan runs and their findings |

See [Scopes](https://zop.dev/docs/zopnight/concepts/scopes) for how roles and scoping work.

## Through the AI assistant

Everything on this page is also available through the [MCP server](https://zop.dev/docs/zopnight/integrations/mcp): policy CRUD, CI wiring and token management, reading validation runs, and browsing the policy and tagging catalogues. Ask your assistant "which repositories are failing a cost guardrail this week?" and it answers from your live scans.

## Troubleshooting

**The ZopNight check appears on pull requests but never blocks a merge**

  The check isn't marked as required yet, so the decision is advisory. Mark the ZopNight check as a **Required Check** on the branches you want gated, in your repository settings.

**Pull requests aren**

  The onboarding pull request that adds the GitHub Action hasn't been merged. Merge it, and every later pull request runs the scan. Check the repository's CI status on the **Repositories** tab.

**A change has to ship against a failing policy**

  An admin can record a governed override. The merge proceeds, and the override is logged with the actor, policy, reason, and timestamp.

## Next steps

**Smart Tags**

    The tagging policy domain, managed under Governance → Policies → Tagging.

**Recommendations**

    What ZopNight finds in the cloud you are already running.

**Scopes**

    The RBAC model behind the policies above.

**MCP server**

    Manage policies and read scans from your AI assistant.
