---
title: "Policy-as-Code Governance with OPA/Rego"
description: "How Open Policy Agent enforces infrastructure standards automatically: writing policies in Rego, wiring them into Terraform and Kubernetes, and blocking non-compliant changes in CI/CD before they merge."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/devtips/post/policy-as-code-opa-rego
---

# Policy-as-Code Governance with OPA/Rego

## Why policy-as-code matters

### The governance problem

**Managing infrastructure at scale gets complicated fast.**

As your infrastructure grows, keeping it consistent and compliant gets harder. Manual reviews don't scale past a few teams, and configuration drift arrives on its own. What you need is enforcement that runs on every change without a human in the loop.

### Common infrastructure issues

- Developers accidentally making resources publicly accessible
- Missing required tags on cloud resources
- Non-compliant security group configurations
- Kubernetes deployments without resource limits
- Terraform modules bypassing organizational standards

## The problem: manual governance

### Why manual reviews fail

Code review and post-deployment checks depend on a tired human noticing one line in a 400-line diff. Standards get missed, security rules get worked around, and the issue surfaces after the apply. Guardrails have to run before the deploy to be worth anything.

### What it costs

Unenforced policy shows up on the invoice and in the incident review:

- **Security breaches** from misconfigured resources
- **Compliance violations** leading to audits and fines
- **Cost overruns** from unoptimized infrastructure
- **Operational chaos** from inconsistent deployments

## The fix: Open Policy Agent (OPA)

### What OPA is

Open Policy Agent is a policy engine that keeps policy logic out of your application and infrastructure code. You write rules in Rego, a declarative query language, and OPA evaluates them against structured input: Terraform plans, Kubernetes manifests, CI/CD configuration, or any other JSON you hand it.

### What you get

- **Unified enforcement** across Terraform, Kubernetes, and custom tools
- **Declarative policies** that are easy to understand and maintain
- **Pre-deployment validation** to catch issues before they reach production
- **Audit trails** for compliance and governance requirements
- **Organization-wide standards** enforced consistently

## Getting started with OPA and Rego

### Installing OPA

```bash
# macOS
brew install opa

# Linux
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_x86_64
chmod +x opa
sudo mv opa /usr/local/bin/
```

### A basic Rego policy

```rego title="restrict-public-buckets.rego"
# Deny public S3 buckets
package s3

deny[msg] {
    input.resource_type == "aws_s3_bucket"
    input.acl == "public-read"
    msg := sprintf("S3 bucket %s cannot be public", [input.name])
}

deny[msg] {
    input.resource_type == "aws_s3_bucket"
    input.acl == "public-read-write"
    msg := sprintf("S3 bucket %s cannot be public", [input.name])
}
```

## Integrating OPA with Terraform

### Using Conftest for Terraform

```bash
# Install conftest
brew install conftest

# Validate Terraform plan
terraform plan -json | conftest test -
```

### Policy example: enforce tags

```rego title="require-tags.rego"
package terraform

deny[msg] {
    resource := input.resource_changes[_]
    resource.type in ["aws_instance", "aws_rds_cluster"]
    not resource.change.after.tags.Environment
    msg := sprintf("Resource %s must have Environment tag", [resource.address])
}

deny[msg] {
    resource := input.resource_changes[_]
    resource.type in ["aws_instance", "aws_rds_cluster"]
    not resource.change.after.tags.CostCenter
    msg := sprintf("Resource %s must have CostCenter tag", [resource.address])
}
```

## Enforcing policies in Kubernetes

### Installing OPA Gatekeeper

```bash
# Deploy Gatekeeper to your cluster
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.14/deploy/gatekeeper.yaml
```

### A Kubernetes ConstraintTemplate

```yaml title="require-resource-limits.yaml"
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredResources
metadata:
  name: require-resource-limits
spec:
  match:
    kinds:
      - apiGroups: ['']
        kinds: ['Pod']
    excludedNamespaces: ['kube-system', 'gatekeeper-system']
---
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredresources
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredResources
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredresources

        violation[{"msg": msg}] {
            container := input.review.object.spec.containers[_]
            not container.resources.limits.cpu
            msg := sprintf("Container %s must have CPU limit", [container.name])
        }

        violation[{"msg": msg}] {
            container := input.review.object.spec.containers[_]
            not container.resources.limits.memory
            msg := sprintf("Container %s must have memory limit", [container.name])
        }
```

## CI/CD pipeline integration

### GitHub Actions example

```yaml title=".github/workflows/policy-check.yml"
name: Policy Validation

on: [pull_request]

jobs:
  policy-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install OPA
        run: |
          curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_x86_64
          chmod +x opa
          sudo mv opa /usr/local/bin/

      - name: Run Policy Tests
        run: |
          opa test policies/ -v

      - name: Validate Terraform
        if: hashFiles('*.tf') != ''
        run: |
          terraform init
          terraform plan -json | opa eval -d policies/ 'data.terraform.deny' -f pretty
```

## Best practices

### 1. Start at the organization level

Define policies at the organization level, not project level. Make them discoverable and documented.

```rego
# Start with clear policy namespaces
package org_policies.infrastructure.aws.security
package org_policies.kubernetes.workload
```

### 2. Fail safely

Distinguish between hard denials and warnings:

```rego
package my_policies

deny[msg] {
    # Hard deny: completely block this
    input.security_critical_violation == true
    msg := "This violates critical security policy"
}

warn[msg] {
    # Warning: recommend but allow with approval
    input.non_standard_naming == true
    msg := "Consider following naming standards"
}
```

### 3. Test your policies

```bash
# Test policy logic before deployment
opa test policies/ -v
```

### 4. Keep policies in version control

Keep policies in the same repo as infrastructure code, with proper code review processes.

## Monitoring and auditing

### Log policy violations

Capture and log every policy decision for audit trails:

```rego
package audit

log_decision[decision] {
    decision := {
        "action": "denied",
        "reason": input.violation_reason,
        "timestamp": input.timestamp,
        "resource": input.resource_id
    }
}
```

## A rollout timeline

| Phase       | Focus                                                  | Timeline |
| ----------- | ------------------------------------------------------ | -------- |
| **Phase 1** | Basic security policies (public access, required tags) | Week 1-2 |
| **Phase 2** | Terraform integration in CI/CD                         | Week 3-4 |
| **Phase 3** | Kubernetes Gatekeeper deployment                       | Week 5-6 |
| **Phase 4** | Full auditing and monitoring rollout                   | Week 7-8 |

## Wrapping up

**Policy-as-code moves the check from after the deploy to before it.**

With OPA and Rego, the same rules apply across Terraform, Kubernetes and whatever else you can feed JSON. Start with the security policies you'd be embarrassed to explain in an incident review, get those passing, then widen the net. Rolling out fifty policies at once mostly teaches your team how to add exceptions.

## Resources

- [OPA Official Documentation](https://www.openpolicyagent.org)
- [Rego Playground](https://play.openpolicyagent.org)
- [Gatekeeper Documentation](https://open-policy-agent.github.io/gatekeeper)
- [Conftest: Testing Framework](https://www.conftest.dev)
