ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Policy Automation with Open Policy Agent

Enforce security, compliance, and operational policies as code using Open Policy Agent. Covers Rego language, Kubernetes admission control, Terraform policy validation, CI/CD gate enforcement, and building a policy library that scales across teams.

Policy-as-code replaces manual policy enforcement — approval chains, checklists, review meetings — with automated, testable, version-controlled rules. Open Policy Agent (OPA) is the industry standard for this: a general-purpose policy engine that can evaluate any JSON/YAML input against any policy you define.


How OPA Works

Input (JSON)  →  OPA Engine  →  Decision (allow/deny + reasons)

              Policy (Rego)

OPA takes structured data as input, evaluates it against Rego policies, and returns a decision. The input can be a Kubernetes admission request, a Terraform plan, an HTTP request, or any structured data.

Rego Language Basics

package kubernetes.admission

# Deny pods without resource limits
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    not container.resources.limits
    msg := sprintf("Container '%s' must have resource limits", [container.name])
}

# Deny images from untrusted registries
deny[msg] {
    input.request.kind.kind == "Pod"
    container := input.request.object.spec.containers[_]
    not startswith(container.image, "registry.internal/")
    not startswith(container.image, "gcr.io/company-prod/")
    msg := sprintf("Container '%s' uses untrusted image: %s", 
                   [container.name, container.image])
}

Kubernetes Admission Control

OPA Gatekeeper acts as a Kubernetes admission webhook:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels:
      - key: "team"
      - key: "cost-center"

Every namespace creation is validated: no team or cost-center label → rejected before anything is created.


Terraform Policy Validation

Validate infrastructure changes before terraform apply:

package terraform

# Deny public S3 buckets
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_s3_bucket"
    resource.change.after.acl == "public-read"
    msg := sprintf("S3 bucket '%s' cannot be public", [resource.address])
}

# Require encryption on RDS instances
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_db_instance"
    not resource.change.after.storage_encrypted
    msg := sprintf("RDS instance '%s' must have encryption enabled", [resource.address])
}

# Enforce instance type limits
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_instance"
    not resource.change.after.instance_type in allowed_types
    msg := sprintf("Instance type '%s' not allowed. Use: %v", 
                   [resource.change.after.instance_type, allowed_types])
}

allowed_types := {"t3.micro", "t3.small", "t3.medium", "m5.large", "m5.xlarge"}

CI Pipeline Integration

# In CI/CD pipeline
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
conftest test plan.json --policy policies/

Policy Testing

Policies are code. Test them like code:

package terraform_test

test_deny_public_s3 {
    deny with input as {
        "resource_changes": [{
            "type": "aws_s3_bucket",
            "address": "aws_s3_bucket.bad",
            "change": {"after": {"acl": "public-read"}}
        }]
    }
}

test_allow_private_s3 {
    count(deny) == 0 with input as {
        "resource_changes": [{
            "type": "aws_s3_bucket",
            "address": "aws_s3_bucket.good",
            "change": {"after": {"acl": "private"}}
        }]
    }
}
opa test policies/ -v

Building a Policy Library

Organize policies by domain:

policies/
├── kubernetes/
│   ├── resource-limits.rego
│   ├── image-registry.rego
│   ├── network-policy.rego
│   └── rbac.rego
├── terraform/
│   ├── encryption.rego
│   ├── tagging.rego
│   ├── networking.rego
│   └── cost-limits.rego
├── ci-cd/
│   ├── branch-protection.rego
│   └── test-coverage.rego
└── tests/
    ├── kubernetes_test.rego
    └── terraform_test.rego

Anti-Patterns

Anti-PatternConsequenceFix
Policies without testsFalse positives/negatives in productionTest every policy rule
Deny-only (no explanations)Developers do not know how to fix violationsInclude fix guidance in deny messages
All policies enforced immediatelyTeams blocked, shadow IT increasesAudit mode first, then enforce gradually
No exception processLegitimate needs blockedException mechanism with expiry
Policies in a monorepo not versionedBreaking changes affect everyoneSemantic versioning for policy packages

Policy automation scales governance from “one security engineer reviews everything” to “every change is automatically validated against the entire policy library in seconds.”

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →