---
title: "Policy-as-Code Governance with OPA/Rego"
lang: "en"
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/post/policy-as-code-opa-rego
---

![Blog post image for Policy-as-Code Governance with OPA/Rego - 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.](/_astro/hero.CwAJ64Mi_Z15VuUr.webp)

[Home](/)›[Devtips](/devtips)›[All Categories](/devtips/categories)›[DevOps & DevSecOps](/devtips/categories/devops--devsecops)

Devtips

[Prev in DevOps & DevSecOpsGitHub Actions Secrets and Environment Variables: Handle Config the Right Way](/devtips/post/github-actions-secrets-environment-variables-guide)[Next in DevOps & DevSecOpsSecuring CI/CD with IAM Roles](/devtips/post/securing-cicd-with-iam-roles)

[DevOps & DevSecOps](/devtips/categories/devops--devsecops)

# Policy-as-Code Governance with OPA/Rego

[Mohammad Abu Mattar](/authors/mohammad-abu-mattar)Published: 24 Feb 202603 Mins read04 Mins listen

[Markdown for AI(opens in a new tab)](/post/policy-as-code-opa-rego/index.md "Open the plain-Markdown version of this page, for pasting into an AI tool")

TL;DR

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.

Series

[Infrastructure & Governance](/series/infrastructure--governance)1/1

All posts in this series (1)

DevTips1

1.  [Policy-as-Code Governance with OPA/RegoYou are here](/devtips/post/policy-as-code-opa-rego)

### Policy-as-Code Governance with OPA/Rego

Contents

[Why policy-as-code matters](#why-policy-as-code-matters)[The governance problem](#the-governance-problem)[Common infrastructure issues](#common-infrastructure-issues)[The problem: manual governance](#the-problem-manual-governance)[Why manual reviews fail](#why-manual-reviews-fail)[What it costs](#what-it-costs)[The fix: Open Policy Agent (OPA)](#the-fix-open-policy-agent-opa)[What OPA is](#what-opa-is)[What you get](#what-you-get)[Getting started with OPA and Rego](#getting-started-with-opa-and-rego)[Installing OPA](#installing-opa)[A basic Rego policy](#a-basic-rego-policy)[Integrating OPA with Terraform](#integrating-opa-with-terraform)[Using Conftest for Terraform](#using-conftest-for-terraform)[Policy example: enforce tags](#policy-example-enforce-tags)[Enforcing policies in Kubernetes](#enforcing-policies-in-kubernetes)[Installing OPA Gatekeeper](#installing-opa-gatekeeper)[A Kubernetes ConstraintTemplate](#a-kubernetes-constrainttemplate)[CI/CD pipeline integration](#cicd-pipeline-integration)[GitHub Actions example](#github-actions-example)[Best practices](#best-practices)[1\. Start at the organization level](#1-start-at-the-organization-level)[2\. Fail safely](#2-fail-safely)[3\. Test your policies](#3-test-your-policies)[4\. Keep policies in version control](#4-keep-policies-in-version-control)[Monitoring and auditing](#monitoring-and-auditing)[Log policy violations](#log-policy-violations)[A rollout timeline](#a-rollout-timeline)[Wrapping up](#wrapping-up)[Resources](#resources)

## [Why policy-as-code matters](#why-policy-as-code-matters)

### [The governance problem](#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](#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](#the-problem-manual-governance)

### [Why manual reviews fail](#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](#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)](#the-fix-open-policy-agent-opa)

### [What OPA is](#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](#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](#getting-started-with-opa-and-rego)

### [Installing OPA](#installing-opa)

Terminal window

```
# macOSbrew install opa
# Linuxcurl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_x86_64chmod +x opasudo mv opa /usr/local/bin/
```

### [A basic Rego policy](#a-basic-rego-policy)

restrict-public-buckets.rego

```
1# Deny public S3 buckets2package s33
4deny[msg] {5    input.resource_type == "aws_s3_bucket"6    input.acl == "public-read"7    msg := sprintf("S3 bucket %s cannot be public", [input.name])8}9
10deny[msg] {11    input.resource_type == "aws_s3_bucket"12    input.acl == "public-read-write"13    msg := sprintf("S3 bucket %s cannot be public", [input.name])14}
```

## [Integrating OPA with Terraform](#integrating-opa-with-terraform)

### [Using Conftest for Terraform](#using-conftest-for-terraform)

Terminal window

```
# Install conftestbrew install conftest
# Validate Terraform planterraform plan -json | conftest test -
```

### [Policy example: enforce tags](#policy-example-enforce-tags)

require-tags.rego

```
1package terraform2
3deny[msg] {4    resource := input.resource_changes[_]5    resource.type in ["aws_instance", "aws_rds_cluster"]6    not resource.change.after.tags.Environment7    msg := sprintf("Resource %s must have Environment tag", [resource.address])8}9
10deny[msg] {11    resource := input.resource_changes[_]12    resource.type in ["aws_instance", "aws_rds_cluster"]13    not resource.change.after.tags.CostCenter14    msg := sprintf("Resource %s must have CostCenter tag", [resource.address])15}
```

## [Enforcing policies in Kubernetes](#enforcing-policies-in-kubernetes)

### [Installing OPA Gatekeeper](#installing-opa-gatekeeper)

Terminal window

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

### [A Kubernetes ConstraintTemplate](#a-kubernetes-constrainttemplate)

require-resource-limits.yaml

```
1apiVersion: constraints.gatekeeper.sh/v1beta12kind: K8sRequiredResources3metadata:4  name: require-resource-limits5spec:6  match:7    kinds:8      - apiGroups: ['']9        kinds: ['Pod']10    excludedNamespaces: ['kube-system', 'gatekeeper-system']11---12apiVersion: templates.gatekeeper.sh/v113kind: ConstraintTemplate14metadata:15  name: k8srequiredresources16spec:17  crd:18    spec:19      names:20        kind: K8sRequiredResources21  targets:22    - target: admission.k8s.gatekeeper.sh23      rego: |24        package k8srequiredresources25
26        violation[{"msg": msg}] {27            container := input.review.object.spec.containers[_]28            not container.resources.limits.cpu29            msg := sprintf("Container %s must have CPU limit", [container.name])30        }31
32        violation[{"msg": msg}] {33            container := input.review.object.spec.containers[_]34            not container.resources.limits.memory35            msg := sprintf("Container %s must have memory limit", [container.name])36        }
```

## [CI/CD pipeline integration](#cicd-pipeline-integration)

### [GitHub Actions example](#github-actions-example)

.github/workflows/policy-check.yml

```
1name: Policy Validation2
3on: [pull_request]4
5jobs:6  policy-check:7    runs-on: ubuntu-latest8    steps:9      - uses: actions/checkout@v310
11      - name: Install OPA12        run: |13          curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_x86_6414          chmod +x opa15          sudo mv opa /usr/local/bin/16
17      - name: Run Policy Tests18        run: |19          opa test policies/ -v20
21      - name: Validate Terraform22        if: hashFiles('*.tf') != ''23        run: |24          terraform init25          terraform plan -json | opa eval -d policies/ 'data.terraform.deny' -f pretty
```

## [Best practices](#best-practices)

### [1\. Start at the organization level](#1-start-at-the-organization-level)

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

```
1# Start with clear policy namespaces2package org_policies.infrastructure.aws.security3package org_policies.kubernetes.workload
```

### [2\. Fail safely](#2-fail-safely)

Distinguish between hard denials and warnings:

```
1package my_policies2
3deny[msg] {4    # Hard deny: completely block this5    input.security_critical_violation == true6    msg := "This violates critical security policy"7}8
9warn[msg] {10    # Warning: recommend but allow with approval11    input.non_standard_naming == true12    msg := "Consider following naming standards"13}
```

### [3\. Test your policies](#3-test-your-policies)

Terminal window

```
# Test policy logic before deploymentopa test policies/ -v
```

### [4\. Keep policies in version control](#4-keep-policies-in-version-control)

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

## [Monitoring and auditing](#monitoring-and-auditing)

### [Log policy violations](#log-policy-violations)

Capture and log every policy decision for audit trails:

```
1package audit2
3log_decision[decision] {4    decision := {5        "action": "denied",6        "reason": input.violation_reason,7        "timestamp": input.timestamp,8        "resource": input.resource_id9    }10}
```

## [A rollout timeline](#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](#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](#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)

Was this useful?

## Tags

[#Policy as Code](/devtips/tags/policy-as-code)[#OPA/Rego](/devtips/tags/oparego)[#Compliance](/devtips/tags/compliance)[#Infrastructure](/devtips/tags/infrastructure)[#Governance](/devtips/tags/governance)[#Kubernetes](/devtips/tags/kubernetes)[#Terraform](/devtips/tags/terraform)

## Share

[Facebook](https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego "Share on Facebook")[Twitter](https://twitter.com/intent/tweet/?text=Policy-as-Code%20Governance%20with%20OPA%2FRego&url=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego "Share on Twitter")[LinkedIn](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego&title=Policy-as-Code%20Governance%20with%20OPA%2FRego&summary=How%20Open%20Policy%20Agent%20enforces%20infrastructure%20standards%20automatically%3A%20writing%20policies%20in%20Rego%2C%20wiring%20them%20into%20Terraform%20and%20Kubernetes%2C%20and%20blocking%20non-compliant%20changes%20in%20CI%2FCD%20before%20they%20merge.&source=https://mkabumattar.com "Share on LinkedIn")[WhatsApp](https://wa.me/?text=Policy-as-Code%20Governance%20with%20OPA%2FRego%20https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego "Share on WhatsApp")[Telegram](https://t.me/share/url?url=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego&text=Policy-as-Code%20Governance%20with%20OPA%2FRego "Share on Telegram")[Reddit](https://www.reddit.com/submit?url=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego&title=Policy-as-Code%20Governance%20with%20OPA%2FRego "Share on Reddit")[Hacker News](http://news.ycombinator.com/submitlink?u=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego&t=Policy-as-Code%20Governance%20with%20OPA%2FRego "Share on Hacker News")[Pinterest](https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego&media=&description=How%20Open%20Policy%20Agent%20enforces%20infrastructure%20standards%20automatically%3A%20writing%20policies%20in%20Rego%2C%20wiring%20them%20into%20Terraform%20and%20Kubernetes%2C%20and%20blocking%20non-compliant%20changes%20in%20CI%2FCD%20before%20they%20merge. "Share on Pinterest")[Email](<mailto:?subject=Policy-as-Code%20Governance%20with%20OPA%2FRego&body=Check out this article: https%3A%2F%2Fmkabumattar.com%2Fdevtips%2Fpost%2Fpolicy-as-code-opa-rego>)

## Comments

## You might also enjoy

More posts on similar topics

[![Helm Charts: Templating & Multi-Environment Kubernetes Deployments](/_astro/hero.C-pWunDw_Z143yJc.webp)](/devtips/post/helm-charts-kubernetes-multi-environment)

## [Helm Charts: Templating & Multi-Environment Kubernetes Deployments](/devtips/post/helm-charts-kubernetes-multi-environment)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Kubernetes & DevOps](/devtips/categories/kubernetes--devops)

Why Helm matters The Kubernetes manifest problem Managing Kubernetes manifests at scale becomes a nightmare. You have a deployment for dev, staging and production. Each one is 90% identi

[#Helm](/devtips/tags/helm)[#Kubernetes](/devtips/tags/kubernetes)[#Deployment](/devtips/tags/deployment)+4 tags

[read more](/devtips/post/helm-charts-kubernetes-multi-environment)

[![Container Image Vulnerability Scanning in CI/CD with Trivy](/_astro/hero.yY1orHlw_2oq3jw.webp)](/devtips/post/container-image-vulnerability-scanning-trivy)

## [Container Image Vulnerability Scanning in CI/CD with Trivy](/devtips/post/container-image-vulnerability-scanning-trivy)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [DevOps & DevSecOps](/devtips/categories/devops--devsecops)

Why container security matters Where the vulnerabilities hide A container image is one of the largest pieces of untrusted code you ship. Every image you build carries the base OS layer,

[#Container Security](/devtips/tags/container-security)[#Vulnerability Scanning](/devtips/tags/vulnerability-scanning)[#Trivy](/devtips/tags/trivy)+4 tags

[read more](/devtips/post/container-image-vulnerability-scanning-trivy)

[![7 Reasons Learning the Linux Terminal is Worth It (Even for Beginners)](/_astro/hero.Ci9C_A6W_1AKnQ0.webp)](/devtips/post/7-reasons-learning-linux-terminal-worth-it-beginners)

## [7 Reasons Learning the Linux Terminal is Worth It (Even for Beginners)](/devtips/post/7-reasons-learning-linux-terminal-worth-it-beginners)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [DevOps & DevSecOps](/devtips/categories/devops--devsecops)

Why learn the Linux terminal? Why it still matters Even with the graphical tools and AI assistants available now, the terminal is the most direct way to work with a Linux system. It's a core

[#Linux](/devtips/tags/linux)[#Terminal](/devtips/tags/terminal)[#Command Line](/devtips/tags/command-line)+4 tags

[read more](/devtips/post/7-reasons-learning-linux-terminal-worth-it-beginners)

[![Managing Terraform at Scale with Terragrunt](/_astro/hero.DUZZoi07_ZRPUOh.webp)](/devtips/post/terraform-terragrunt-wrappers)

## [Managing Terraform at Scale with Terragrunt](/devtips/post/terraform-terragrunt-wrappers)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [Cloud & Infrastructure Automation](/devtips/categories/cloud--infrastructure-automation)

The problem with Terraform at scale Duplicated code across environments If you're managing infrastructure with Terraform across several environments or projects, you've probably hit the point

[#Terraform](/devtips/tags/terraform)[#Terragrunt](/devtips/tags/terragrunt)[#Infrastructure as Code](/devtips/tags/infrastructure-as-code)+4 tags

[read more](/devtips/post/terraform-terragrunt-wrappers)

[![Securing CI/CD with IAM Roles](/_astro/hero.Bl9B2DZz_ZDIuXQ.webp)](/devtips/post/securing-cicd-with-iam-roles)

## [Securing CI/CD with IAM Roles](/devtips/post/securing-cicd-with-iam-roles)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [DevOps & DevSecOps](/devtips/categories/devops--devsecops)

Why secure your CI/CD pipeline? Why pipeline security matters Your pipeline holds credentials for every environment you deploy to, which makes it one of the most valuable targets you own. A s

[#CICD Security](/devtips/tags/cicd-security)[#IAM Roles](/devtips/tags/iam-roles)[#Least Privilege](/devtips/tags/least-privilege)+4 tags

[read more](/devtips/post/securing-cicd-with-iam-roles)

[![Understanding Kubernetes Services: ClusterIP vs NodePort vs LoadBalancer](/_astro/hero.DBNjupL__148EQW.webp)](/devtips/post/kubernetes-services-clusterip-nodeport-loadbalancer)

## [Understanding Kubernetes Services: ClusterIP vs NodePort vs LoadBalancer](/devtips/post/kubernetes-services-clusterip-nodeport-loadbalancer)

-   [Mohammad Abu Mattar](/authors/mohammad-abu-mattar)
-   [DevOps & Kubernetes](/devtips/categories/devops--kubernetes)

If you're working with Kubernetes, you've probably noticed that Pods come and go, and their IP addresses keep changing. That's where Services come in. They give you a stable way to keep your apps acce

[#Kubernetes](/devtips/tags/kubernetes)[#K8s Services](/devtips/tags/k8s-services)[#ClusterIP](/devtips/tags/clusterip)+5 tags

[read more](/devtips/post/kubernetes-services-clusterip-nodeport-loadbalancer)

6 related posts
