Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Infrastructure as Code By James Joyner IV · · 9 min read

Policy-as-Code for Infrastructure: OPA and Conftest in Practice

Stop catching bad infrastructure config in code review. Here's how to enforce IaC guardrails automatically with OPA and Conftest — and let AI write the Rego.

  • #iac
  • #policy-as-code
  • #opa
  • #conftest
  • #rego
  • #security

Every team eventually hits the same wall: someone merges infrastructure that opens a security group to 0.0.0.0/0, or ships a bucket without encryption, or a container running as root. Code review is supposed to catch these. It doesn’t, reliably, because humans are tired and the diff is 600 lines.

Policy-as-code moves those rules out of human attention and into a gate that runs every time. The Open Policy Agent (OPA) and its config-friendly wrapper Conftest are how I do it across Terraform, Kubernetes, Dockerfiles, and plain YAML.

Why a dedicated policy engine

You could write a bash script that greps for 0.0.0.0/0. People do. It works until you have forty rules across six config formats, and then it’s an unmaintainable pile of regex.

OPA gives you a real language — Rego — purpose-built for “given this structured data, is it allowed?” Conftest wraps OPA so it reads your YAML, JSON, HCL, or Dockerfiles, evaluates them against your policies, and exits non-zero when something violates. That non-zero exit is the whole value: it fits in CI like any other test.

A first policy

Conftest evaluates input against deny rules. Any deny that produces a message fails the run. Here’s a policy that blocks privileged Kubernetes containers:

# policy/security.rego
package main

deny[msg] {
  input.kind == "Deployment"
  c := input.spec.template.spec.containers[_]
  c.securityContext.privileged == true
  msg := sprintf("container '%s' must not run privileged", [c.name])
}

Run it:

conftest test deployment.yaml

If any container is privileged, you get a clear message and a failing exit code. Wire that into CI and the rule is now enforced on every change, forever, without anyone remembering to check.

Rego has a learning curve — AI flattens it

Let’s be honest: Rego is the reason a lot of teams bounce off OPA. It’s a declarative logic language and it does not read like the Python or Go you’re used to. The mental model — rules that succeed by finding a matching document — takes a while.

This is exactly where AI pays off. Describe the rule in English and ask for the Rego:

“Write a Conftest deny rule: fail if any Kubernetes container does not set resources.limits.memory. Include the container name in the message.”

You’ll get a working rule in seconds. The catch — and it’s a real one — is that LLMs hallucinate Rego helpers and occasionally write rules that always pass (a deny that never matches is silently useless). So you must test every generated policy against both a known-good and a known-bad fixture. Trust, but verify with fixtures.

I keep a set of IaC policy prompts tuned for exactly this, so the model already knows the Conftest conventions.

Test your policies like code

A policy with no tests is a liability — it can rot into a no-op and you’d never know. Conftest supports unit tests for policies:

# policy/security_test.rego
package main

test_denies_privileged {
  deny[_] with input as {
    "kind": "Deployment",
    "spec": {"template": {"spec": {"containers": [
      {"name": "app", "securityContext": {"privileged": true}}
    ]}}}
  }
}

test_allows_unprivileged {
  count(deny) == 0 with input as {
    "kind": "Deployment",
    "spec": {"template": {"spec": {"containers": [{"name": "app"}]}}}
  }
}
conftest verify

Now your guardrails have guardrails. This pairs naturally with broader IaC testing strategies — policy tests are just one layer.

Organize policies by concern

As policies grow, split them by domain using Rego packages and Conftest namespaces:

policy/
├── security/      # encryption, network exposure, privilege
├── cost/          # instance sizes, untagged resources
├── compliance/    # required tags, naming conventions
└── reliability/   # replica counts, probes, limits

Run a namespace with conftest test --namespace security .... This lets you make security policies hard-fail while cost policies emit warnings, using Rego’s warn rules instead of deny.

Wire it into the pipeline

The pattern that works:

  1. Pre-commit / local — developers run conftest test before pushing. Fast feedback, fewer surprises.
  2. CI gate — the pipeline runs the full policy set on every PR. A deny fails the build. This is the enforcement point.
  3. Pre-apply — for Terraform, run Conftest against the plan JSON (terraform show -json plan.out) so policies evaluate the actual proposed change, not just the source.
# CI step
- name: Policy check
  run: conftest test --all-namespaces manifests/

What to enforce first

Don’t try to boil the ocean. The high-value starter rules, by impact:

  • No public network exposure unless explicitly annotated.
  • Encryption at rest required on storage.
  • Required tags (owner, environment, cost-center) on every resource.
  • No :latest image tags — pin everything.
  • Resource limits set on every container.

Each of these has caused a real outage or a real bill somewhere. Encoding them as policy means it can’t happen to you twice.

The payoff

Policy-as-code changes the conversation. Instead of a senior engineer being the human bottleneck who remembers the rules, the rules live in a repo, run automatically, and are testable. New hires can’t violate a policy they’ve never heard of, because the gate explains itself in the failure message.

Let AI write the first draft of the Rego from your plain-English rules, keep your prompts in a prompt library, and test every policy against fixtures. The engine is deterministic even when the authoring assistant isn’t — which is exactly the safety property you want.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.