// Project Walkthrough · 05
Rego Compliance Policy Library
Three OPA Rego policies that run as a pre-apply gate against terraform plan -json output, validating GCP resources against NIST 800-53 SC-28, AC-3, and CM-6 before anything is deployed. Each deny message returns the resource address and control ID so developers fix the problem without filing a GRC ticket.
opa
rego
nist 800-53
terraform · gcp · policy-as-code
Most compliance validation happens after infrastructure is deployed. A resource gets created, someone runs a check, a finding gets filed, and the team spends a sprint remediating something that should never have been deployed in the first place. The loop is slow, reactive, and produces GRC tickets as a side effect of normal engineering work.
This library moves the validation upstream. Policies run against the Terraform plan before apply — no cloud access required, no resources created, instant feedback. A developer who tries to deploy an unencrypted bucket or a resource missing compliance labels sees the denial, the resource address, and the NIST control ID in the same terminal window where they ran the plan.
OPA evaluates Rego policies against the JSON output of terraform plan -json. The plan contains every resource Terraform intends to create or modify, including all attribute values. Policies inspect those values and emit deny messages for any resource that fails a control check.
// pre-apply gate
Shift Left, Not Post-Deploy
Validation runs at plan time — before any API call reaches GCP. A failing policy blocks the apply entirely, not after resources are half-created.
// rego v1 semantics
OPA import rego.v1
Strict Rego v1 syntax with explicit import rego.v1 for forward compatibility. Partial set rules with contains to recurse into child_modules for module-wrapped resources.
// actionable denials
Resource Address + Control ID
Every deny message returns the full resource address and the NIST control ID. Developers know exactly what to fix and why without a GRC ticket or Slack thread.
// in-memory tests
No Cloud Access Needed
The test suite runs entirely against in-memory fixtures — no apply, no GCP credentials, instant CI feedback. Eight tests across three policies.
Three policies, each enforcing one NIST 800-53 control. Each runs independently so teams can adopt them incrementally.
SC-28 · encryption.rego
Encryption at Rest via CMEK
Validates that every google_storage_bucket resource has a customer-managed encryption key configured. A bucket using default Google-managed encryption fails the policy — SC-28 requires customer control of the key material. Deny message returns the bucket resource address and SC-28.
AC-3 · public_access.rego
Public Access Lockdown
Checks both google_storage_bucket resources (uniform_bucket_level_access must be true, public_access_prevention must be enforced) and google_compute_firewall rules (no ingress rules allowing 0.0.0.0/0 on sensitive ports). Any resource that exposes access beyond the account boundary fails. Deny returns the resource address and AC-3.
CM-6 · labels.rego
Required Compliance Labels
Iterates all taggable resource types and validates that required compliance labels are present. Uses set subtraction — required_labels minus provided_labels — to identify exactly which labels are missing. Deny message returns the resource address, the missing label keys, and CM-6. No false positives on resources that do not support labels.
| Decision | Reasoning |
| import rego.v1 | Strict Rego v1 semantics for forward compatibility — avoids deprecated syntax that will break in future OPA releases |
| Partial set rules + contains | Recurses into child_modules in the plan JSON so policies catch resources wrapped inside Terraform modules, not just root-level resources |
| Set subtraction for labels | required_labels - provided_labels surfaces exactly which labels are missing per resource — cleaner than a loop and easier to extend |
| No METADATA blocks | Simple control coverage comments instead of YAML METADATA blocks avoids OPA parser issues and keeps policies readable without tooling |
| In-memory test fixtures | Tests run against hardcoded plan JSON fixtures — no cloud credentials, no apply, instant feedback in CI pipelines |
// run policies against a terraform plan
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > plan.json
opa eval -d policies/ -i plan.json "data.compliance.deny"
opa test policies/ tests/ -v
tools/rego-policies/
policies/
encryption.rego
public_access.rego
labels.rego
tests/
encryption_test.rego
public_access_test.rego
labels_test.rego
fixtures/
README.md