Policy as Code with OPA and Conftest

Khimananda Oli 8 min read Virtualization
Policy as Code with OPA and Conftest

By Khimananda Oli | Last reviewed: August 2026

Manual compliance reviews cannot keep pace with modern infrastructure velocity, creating bottlenecks or dangerous gaps in security governance. Implementing Policy as Code with OPA and Conftest shifts these critical checks left into your development workflow, ensuring every change meets organizational standards before it ever reaches production. This approach transforms subjective audit checklists into deterministic, automated tests that integrate directly with your existing Infrastructure as Code with Terraform pipelines.

What is Policy as Code with OPA and Conftest and why does it matter?

Policy as Code with OPA and Conftest represents a fundamental shift from reactive auditing to proactive prevention. Open Policy Agent (OPA) serves as the general-purpose policy engine that evaluates arbitrary structured data against rules written in Rego, a declarative query language designed specifically for policy logic. Conftest acts as the developer-facing interface, wrapping OPA to validate configuration files like Terraform plans, Kubernetes manifests, Dockerfiles, and Helm charts without requiring you to manage raw JSON inputs manually.

In practice, this separation of concerns matters because infrastructure teams often struggle to bridge the gap between compliance requirements and engineering implementation. Security teams write PDFs; engineers write YAML. OPA translates those PDF requirements into executable code that lives alongside your application source. When you treat policies as first-class artifacts stored in Git, you gain version history, peer review workflows, and automated testing for your governance rules themselves. This alignment is essential for maintaining SOC 2 or ISO 27001 compliance at scale, where evidence collection must be continuous rather than point-in-time.

DeveloperConftest CLI(Local / CI)OPA Engine(Rego Eval)Policy BundleConfig DataPassFail
High-level architecture of Policy as Code with OPA and Conftest showing evaluation flow from developer input through policy bundle to pass/fail enforcement

How do you write effective Rego policies for infrastructure validation?

Rego has a learning curve because it operates on set theory and logic programming rather than imperative scripting. A common mistake is trying to write Rego like Python or Bash. Instead, think in terms of queries: you are asking questions about your data, and violations occur when certain conditions evaluate to true. Start simple and build complexity incrementally.

Basic deny rule structure

Every useful policy starts with a clear violation definition. The following example enforces that all AWS S3 buckets must have versioning enabled, a baseline requirement for most backup and disaster recovery strategies:

package terraform.s3

import rego.v1

deny contains msg if {
    some resource in input.resource_changes
    resource.type == "aws_s3_bucket"
    resource.change.after.versioning[0].enabled != true
    msg := sprintf("S3 bucket '%s' must have versioning enabled", [resource.address])
}

This pattern uses the contains keyword to collect multiple violations into a set, which is crucial when validating large Terraform plans with hundreds of resources. The some ... in construct iterates safely over arrays, avoiding index errors when resources don't exist. Always use sprintf for messages to include specific resource identifiers—generic error messages waste debugging time during CI/CD pipeline execution.

Testing policies before deployment

Never deploy untested Rego. Create test fixtures representing both compliant and non-compliant configurations, then write assertions using Conftest's built-in test framework or OPA's native test runner. Store tests adjacent to your policies in version control. If you cannot articulate what constitutes a pass versus fail case in a unit test, your policy is too vague for production enforcement.

How do you integrate Conftest into CI/CD pipelines for automated enforcement?

Integration timing determines whether Policy as Code with OPA and Conftest acts as a helpful guardrail or an annoying blocker. Validate as early as possible while maintaining sufficient context for meaningful evaluation. For Terraform specifically, always validate the plan output rather than raw HCL files—the plan contains resolved values, computed attributes, and actual state changes that static analysis cannot see.

  1. Generate the Terraform plan in JSON format using terraform plan -out=tfplan && terraform show -json tfplan > tfplan.json
  2. Run Conftest against the plan file with your policy directory: conftest test tfplan.json --policy ./policies
  3. Configure your CI system to fail the job on any deny results, treating warnings as informational only
  4. Archive the plan artifact and policy version used for audit trail reproducibility
  5. Optionally push passing plans to an artifact store for subsequent apply stages

In GitLab CI or GitHub Actions, this typically adds 10–30 seconds to pipeline duration—a negligible cost compared to remediating a misconfigured production database. The key insight from years of running these systems: consistency matters more than strictness. A policy that catches 80% of issues reliably beats one that theoretically covers everything but produces false positives that engineers learn to ignore.

Source RepoCI RunnerConftestTerraformCloudPushPlanJSONEvaluatePassApply
Sequence diagram illustrating Policy as Code with OPA and Conftest positioned between Terraform plan generation and cloud apply within CI/CD

When should you use OPA versus Conftest versus cloud-native policy tools?

Choosing the right tool depends on your existing ecosystem, team expertise, and compliance requirements. While Policy as Code with OPA and Conftest provides maximum flexibility, cloud vendors now offer managed alternatives that reduce operational overhead at the cost of portability.

CriteriaOPA + ConftestAWS SCP / Azure PolicyKyverno / Gatekeeper
LanguageRego (declarative)JSON / Portal UIYAML CRDs / Rego
ScopeAny structured dataSingle cloud API callsKubernetes admission only
Shift-left capabilityExcellent (CLI + CI)Poor (post-deploy enforcement)Moderate (dry-run mode)
Multi-cloud supportNativeNoneLimited
Learning curveSteep initiallyLowModerate
Audit evidence exportCustom (full control)Built-in reportsK8s events / logs
Best forComplex cross-domain policiesCloud account guardrailsK8s runtime enforcement

For organizations pursuing multi-cloud strategies or needing to validate non-infrastructure artifacts (API schemas, application configs, IAM policies), OPA remains unmatched. Cloud-native tools excel at preventing API-level misconfigurations within their respective platforms but cannot replace pre-deployment validation. Many mature teams run both: OPA/Conftest in CI for shift-left prevention, plus cloud-native policies as defense-in-depth runtime guards.

How do you manage policy lifecycle and avoid technical debt in Rego?

Policies age poorly when treated as afterthoughts. Without deliberate lifecycle management, your Rego codebase becomes a tangled mess of exceptions, outdated rules, and undocumented assumptions that block legitimate work. Apply the same software engineering discipline to policies that you apply to application code.

Version and tag policy bundles

Never reference mutable policy sources in production pipelines. Use OPA Bundle API or Git tags to pin exact policy versions consumed by each deployment. When updating policies, maintain backward compatibility or coordinate breaking changes with dependent teams. Document the rationale behind each rule in comments—six months later, nobody will remember why that specific CIDR range was whitelisted.

Implement exception handling explicitly

Real-world infrastructure requires exceptions. Build a structured exception mechanism into your policy library rather than scattering hardcoded overrides throughout rules. A centralized exception registry with expiration dates, approval references, and ownership metadata keeps temporary waivers from becoming permanent security holes. This discipline proves invaluable during AWS IAM best practices audits where reviewers demand justification for every deviation.

Monitor policy effectiveness metrics

Track violation rates, false positive frequency, and mean-time-to-resolution for policy failures. Policies that generate constant noise without catching real risks should be refined or retired. Conversely, rules that never trigger may indicate dead code or insufficient test coverage. Treat policy health as an observable system metric, not a set-and-forget artifact.

Ad-Hoc ScriptsManual • FragileNo Version ControlLinters + PluginsTool-SpecificLimited ScopeOPA + ConftestUnified • TestableShift-Left ReadyGoverned PaCMetrics • LifecycleAudit EvidenceMaturity Progression →
Policy maturity model comparing ad-hoc approaches to governed Policy as Code with OPA and Conftest implementations

Start enforcing Policy as Code with OPA and Conftest today

Begin with a single high-value policy targeting your most frequent misconfiguration—public S3 buckets, missing encryption, overly permissive security groups—and integrate Conftest validation into one pilot pipeline. Measure the catch rate, gather developer feedback, refine the messaging, then expand scope incrementally. Resist the urge to boil the ocean with comprehensive policy libraries before proving the workflow works for your team.

If your organization needs guidance implementing Policy as Code with OPA and Conftest across complex multi-cloud environments or preparing for compliance audits, reach out to discuss your specific infrastructure challenges. Getting the foundation right early prevents costly rework and ensures your governance scales with your engineering velocity.

Frequently Asked Questions

It is a practice using Open Policy Agent for evaluation and Conftest for testing infrastructure configurations against Rego policies before deployment.

Run brew install conftest on macOS or download the latest binary from GitHub releases. Verify installation by running conftest --version to confirm the current stable release is active.

Yes, OPA natively parses YAML and JSON inputs. Use conftest test -p policy/ manifest.yaml to validate Kubernetes resources against Rego rules without additional parsing libraries or custom scripts.

OPA is the general-purpose policy engine evaluating Rego. Conftest is a CLI wrapper optimized for testing static configuration files and CI pipelines using OPA as its backend evaluator.

Use the --trace flag with conftest test to see rule evaluation steps. Add print statements in Rego for variable inspection during development to identify logic errors quickly.

Yes, convert terraform plan output to JSON using -json flag. Test this structured data with Conftest to enforce compliance checks on proposed infrastructure changes before apply runs.

Large Rego bundles may slow evaluation. Use partial evaluation and bundle compilation to optimize. Keep policies modular and avoid deep recursion to maintain sub-second feedback loops in CI.

Add the conftest-action step in your workflow YAML. Specify policy directory and target files. Fail the job on policy violations to block non-compliant merges automatically.

Yes, Rego is tool-agnostic. Write generic policies checking standard fields like metadata.labels. Adapt input documents via adapters rather than rewriting core logic for each platform.

Yes, OPA and Conftest are Apache 2.0 licensed. No licensing fees exist for commercial use, though enterprise support and managed services from vendors like Styra incur separate costs.

Define allow rules that override deny rules based on specific annotations or metadata tags. Document exception criteria clearly in comments to maintain auditability and prevent accidental security bypasses.

Conftest supports JSON, YAML, TOML, HCL, Dockerfile, and Kubernetes manifests. It auto-detects format by extension, enabling unified policy testing across diverse infrastructure artifacts without conversion.

Store Rego files alongside infrastructure code in Git. Tag policy releases semantically. Use OPA bundles for distribution to ensure deployed environments run tested, immutable policy versions matching specific commits.

Yes, pass --output junit to conftest test. Redirect stdout to a file and upload as a test artifact. This integrates policy results into standard CI reporting tools like Jenkins or GitLab.

Conftest simplifies CLI syntax, handles multiple file formats automatically, and provides structured output. It reduces boilerplate in CI scripts compared to manually constructing opa eval commands with raw JSON inputs.