Modern QA2026OPA/Rego: The Universal Policy Engine — tiles
Log inJoin
13 / 75 · 08 Infrastructure as Code Testing · Policy as Code← prev⊞ allnext →☰ Read as one page

3.3OPA/Rego: The Universal Policy Engine

Open Policy Agent (OPA) evaluates structured data against Rego policies. This is the most flexible approach because it works with any infrastructure format -- Terraform plans, Kubernetes manifests, Docker Compose files, or any JSON/YAML configuration.

Writing Rego Policies

# policy/terraform/s3.rego
package terraform.s3

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

has_encryption(resource) {
    resource.change.after.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm
}

# Deny public ACLs
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_s3_bucket"
    acl := resource.change.after.acl
    acl != "private"
    msg := sprintf("S3 bucket '%s' has ACL '%s' -- must be 'private'", [resource.address, acl])
}

# Deny buckets without versioning
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_s3_bucket"
    not has_versioning(resource)
    msg := sprintf("S3 bucket '%s' must have versioning enabled", [resource.address])
}

has_versioning(resource) {
    resource.change.after.versioning[_].enabled == true
}

Running OPA with Conftest

Conftest is the CLI tool that runs OPA policies against configuration files:

# Run policy against a Terraform plan
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
conftest test tfplan.json --policy policy/terraform/

# Run against Kubernetes manifests
conftest test k8s/deployment.yaml --policy policy/kubernetes/

# Run against Dockerfiles
conftest test Dockerfile --policy policy/docker/

# Run with multiple policy directories
conftest test tfplan.json \
  --policy policy/security/ \
  --policy policy/compliance/ \
  --policy policy/cost/

Organizing Rego Policies by Concern

A well-organized policy repository separates concerns:

policy/
  terraform/
    s3.rego          # S3-specific rules
    rds.rego         # RDS-specific rules
    iam.rego         # IAM policy rules
    networking.rego  # VPC, security group rules
    tagging.rego     # Resource tagging requirements
  kubernetes/
    security.rego    # Pod security, RBAC
    resources.rego   # CPU/memory limits
    networking.rego  # Network policies
  docker/
    best_practices.rego  # Dockerfile linting
  common/
    helpers.rego     # Shared helper functions

Advanced Rego Patterns

# policy/terraform/iam.rego
package terraform.iam

# Deny IAM policies with wildcard actions
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_iam_policy"
    policy_doc := json.unmarshal(resource.change.after.policy)
    statement := policy_doc.Statement[_]
    statement.Effect == "Allow"
    statement.Action[_] == "*"
    msg := sprintf("IAM policy '%s' grants wildcard actions (*). Use specific actions.",
        [resource.address])
}

# Deny IAM policies with wildcard resources on mutating actions
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_iam_policy"
    policy_doc := json.unmarshal(resource.change.after.policy)
    statement := policy_doc.Statement[_]
    statement.Effect == "Allow"
    statement.Resource[_] == "*"
    action := statement.Action[_]
    not is_readonly_action(action)
    msg := sprintf("IAM policy '%s' uses wildcard resource with mutating action '%s'",
        [resource.address, action])
}

is_readonly_action(action) {
    endswith(action, ":Get*")
}
is_readonly_action(action) {
    endswith(action, ":List*")
}
is_readonly_action(action) {
    endswith(action, ":Describe*")
}