TL;DR

  • OPA is your go-to for multi-platform policy enforcement (Kubernetes, Terraform, APIs); Sentinel excels within the HashiCorp ecosystem
  • Policy tests are code — write them before the policy itself using OPA’s built-in test framework
  • The #1 mistake: treating policies as documentation instead of tested, versioned code

Best for: Teams managing infrastructure across multiple clouds or platforms needing unified governance Skip if: You’re 100% in Terraform Cloud/Enterprise and never touch Kubernetes Read time: 11 minutes

Your Terraform plan passed all tests. CI is green. You merge and deploy. Three hours later, someone provisions a publicly accessible S3 bucket with no encryption. Your security team is furious.

The problem isn’t your tests — it’s that you’re testing the wrong layer. Infrastructure tests verify what gets deployed. Policy tests verify what’s allowed to be deployed. In 2026, with AI tools generating Terraform configs faster than ever, this distinction is critical.

The Real Problem

Most teams approach policy as code backwards. They write policies after incidents, store them in a wiki, and hope developers read them. This is policy as documentation, not policy as code.

Real policy as code means:

  • Policies are versioned in Git alongside infrastructure
  • Policies have unit tests that run in CI
  • Policies block deployments automatically, not via code review comments
  • Policy violations produce actionable error messages, not cryptic failures

The shift from “guidelines” to “guardrails” requires a mental model change. You’re not writing rules for humans to follow — you’re writing executable constraints that machines enforce.

OPA vs Sentinel: The 2026 Reality

Open Policy Agent (OPA) v1.9.0 and HashiCorp Sentinel both solve policy enforcement, but their scopes differ dramatically.

AspectOPASentinel
EcosystemPlatform-agnostic (K8s, Terraform, APIs, CI/CD)HashiCorp stack (Terraform, Vault, Nomad, Consul)
LanguageRego (declarative, JSON-native)HSL (HashiCorp Sentinel Language)
TestingBuilt-in opa test commandCLI with sentinel test
Data InputAny JSON (requires terraform show -json)Native Terraform plan imports
GovernanceDIY enforcement via CI gatesBuilt-in advisory/soft/hard enforcement levels
CostOpen source, freeRequires Terraform Cloud/Enterprise

My recommendation: Start with OPA if you work across multiple platforms or value portability. Choose Sentinel if you’re deeply invested in HashiCorp Cloud Platform and want tight integration with zero transformation steps.

Writing Testable OPA Policies

The key insight is that policies should be written test-first. Before coding the policy, define what should pass and what should fail.

package terraform.aws.s3

import rego.v1

# Deny S3 buckets without encryption
deny contains msg if {
    resource := input.planned_values.root_module.resources[_]
    resource.type == "aws_s3_bucket"
    not has_encryption(resource)
    msg := sprintf("S3 bucket '%s' must have server-side encryption enabled", [resource.name])
}

has_encryption(bucket) if {
    bucket.values.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm
}

Notice the import rego.v1 — this is the modern syntax as of OPA v1.x. The contains keyword and if guards are Rego v1 conventions that replace the older implicit set generation.

Testing Your Policies

OPA’s testing framework lets you verify policy behavior before deployment. Create a test file alongside your policy:

package terraform.aws.s3_test

import data.terraform.aws.s3

# Test: Encrypted bucket should be allowed
test_encrypted_bucket_allowed if {
    count(s3.deny) == 0 with input as {
        "planned_values": {
            "root_module": {
                "resources": [{
                    "type": "aws_s3_bucket",
                    "name": "secure_bucket",
                    "values": {
                        "server_side_encryption_configuration": [{
                            "rule": [{
                                "apply_server_side_encryption_by_default": [{
                                    "sse_algorithm": "AES256"
                                }]
                            }]
                        }]
                    }
                }]
            }
        }
    }
}

# Test: Unencrypted bucket should be denied
test_unencrypted_bucket_denied if {
    count(s3.deny) > 0 with input as {
        "planned_values": {
            "root_module": {
                "resources": [{
                    "type": "aws_s3_bucket",
                    "name": "insecure_bucket",
                    "values": {}
                }]
            }
        }
    }
}

Run tests with opa test . -v to see detailed output. The -v flag shows which tests passed and any that failed with reasons.

CI/CD Integration

The workflow for Terraform + OPA in your pipeline:

# 1. Generate plan
terraform plan -out=tfplan.binary

# 2. Convert to JSON for OPA
terraform show -json tfplan.binary > tfplan.json

# 3. Evaluate policies
opa exec --decision terraform/aws/s3/deny --bundle policies/ tfplan.json

# 4. Fail if violations exist
if [ $(opa eval -d policies/ -i tfplan.json 'data.terraform.aws.s3.deny' | jq '.result[0].expressions[0].value | length') -gt 0 ]; then
    echo "Policy violations detected!"
    exit 1
fi

For Conftest (OPA’s dedicated config file testing tool), the syntax is simpler:

conftest test tfplan.json --policy policies/

Conftest automatically looks for deny rules and exits non-zero if any fire.

Sentinel for Terraform Cloud

If you’re using Terraform Cloud or Enterprise, Sentinel offers tighter integration. The equivalent S3 encryption policy:

import "tfplan/v2" as tfplan

s3_buckets = filter tfplan.resource_changes as _, rc {
    rc.type is "aws_s3_bucket" and
    rc.mode is "managed" and
    (rc.change.actions contains "create" or rc.change.actions contains "update")
}

encryption_required = rule {
    all s3_buckets as _, bucket {
        bucket.change.after.server_side_encryption_configuration is not null
    }
}

main = rule {
    encryption_required
}

Sentinel’s tfplan/v2 import gives you native access to Terraform’s plan structure without JSON conversion. The enforcement levels (advisory, soft-mandatory, hard-mandatory) let you roll out policies gradually.

AI-Assisted Approaches

Writing policies in Rego or Sentinel requires learning a new language. In 2026, AI tools significantly accelerate this process.

What AI does well:

  • Generating initial policy drafts from plain English requirements
  • Converting between OPA and Sentinel syntax
  • Creating comprehensive test cases for edge conditions
  • Explaining complex Rego comprehensions

What still needs humans:

  • Deciding which resources require which constraints
  • Setting appropriate severity levels for violations
  • Reviewing AI-generated policies for logical correctness
  • Understanding the business context behind compliance requirements

Useful prompt:

Write an OPA Rego policy that:
1. Denies AWS EC2 instances without the "Environment" tag
2. Allows exceptions for instances with "temporary: true" in metadata
3. Include comprehensive unit tests for both cases
4. Use Rego v1 syntax with explicit imports

When This Breaks Down

Policy as code has limitations:

Complexity explosion: As policies grow, interdependencies become hard to track. A policy allowing t3.micro instances conflicts with a policy requiring encrypted EBS volumes on certain instance types.

False positives: Overly strict policies block legitimate use cases. Teams start asking for exemptions, and you end up with a policy full of exceptions.

Performance at scale: Evaluating thousands of policies against large Terraform plans can slow CI significantly. OPA handles this well with partial evaluation, but you need to optimize.

Drift between policy and reality: Policies enforce what’s planned, not what’s running. A manually-created resource bypasses all policy checks.

Consider complementary tools:

Decision Framework

Choose OPA when:

  • You manage Kubernetes alongside Terraform
  • You need policies for API authorization or CI/CD gates
  • Your infrastructure spans multiple clouds
  • Budget constraints rule out Terraform Enterprise

Choose Sentinel when:

  • You’re all-in on HashiCorp Cloud Platform
  • You want enforcement levels without building custom CI logic
  • Your team already knows Sentinel from Vault or Nomad
  • You need first-class support and SLAs

Use both when:

  • OPA for Kubernetes admission control (Gatekeeper)
  • Sentinel for Terraform-specific governance in TFC

Measuring Success

MetricBeforeAfterHow to Track
Policy violations in prodUnknown0Cloud audit logs
Mean time to violation detectionDays/weeks<5 minutesCI pipeline timestamps
Policy coverage0%80%+ resourcesopa test –coverage
False positive rateN/A<5%Exemption request count

Warning signs it’s not working:

  • Teams bypassing CI to avoid policy checks
  • Growing list of permanent exemptions
  • Policies that never fire (too permissive)
  • Policies blocking every PR (too strict)

What’s Next

Start small. Pick one high-impact policy — maybe S3 encryption or instance tagging — and implement it end-to-end:

  1. Write the policy with tests
  2. Add to CI as a warning (advisory mode)
  3. Monitor for false positives for one sprint
  4. Promote to blocking (mandatory mode)
  5. Expand to the next policy

The goal isn’t 100% policy coverage on day one. It’s building muscle memory for treating policies like production code.


Related articles:

External resources: