oso-default-opengraph

Securing Terraform with OPA

If you're working with infrastructure as code, especially with Terraform, you know how quickly things can get out of hand without proper governance. This is where policy as code comes in handy. Today we’ll discuss Open Policy Agent. OPA is one of the best tools to help you maintain security while using Terraform.

What Exactly is Policy as Code?

Policy as code (PaC) is the practice of defining your governance, compliance, and security rules using code. Think of it as version-controlling your rules, making them auditable, and automatically enforcing them. For Terraform users, this means you can evaluate infrastructure changes before they're deployed and roll them back quickly if something unexpected happens after they're deployed. This significantly reduces the risk of misconfigurations and non-compliance. By integrating PaC into your CI/CD pipelines, you gain consistency, traceability, and faster remediation across your infrastructure workflows.

How Open Policy Agent Works with Terraform

OPA is a general-purpose policy engine that evaluates policies written in a language called Rego against JSON-based data inputs. When it comes to Terraform, OPA can evaluate your policies against the JSON output of a Terraform plan. Tools like Conftest or OPA’s HTTP API can then be used to test whether your proposed infrastructure changes meet your defined policy requirements. For example, you can use OPA to validate that resources are only deployed in approved regions or that all required tags are present. The results of these evaluations can then be used to block, warn, or simply log non-compliant infrastructure changes in your automated workflows.

OPA vs. Sentinel: A Quick Comparison

If you've been in the Terraform ecosystem for a while, you might be familiar with Sentinel. So, what's the difference between OPA and Sentinel? OPA is a more flexible, open-source policy engine that works across a much broader cloud-native stack – Terraform, Kubernetes, APIs, and more. Sentinel, on the other hand, is developed by HashiCorp and is tightly coupled with Terraform and the HashiCorp ecosystem. While Sentinel is great for what it does within its scope, OPA provides broader integration opportunities, a larger community, and greater extensibility. If you're a team looking to standardize policy enforcement across diverse environments and tools, OPA is definitely the way to go. For an introduction on how to use OPA for Kubernetes, checkout our guide here.

The Benefits of Integrating OPA with Terraform

Integrating OPA with Terraform offers several compelling benefits:

  • Preventing Non-Compliant Resources: You can stop non-compliant resources from ever being provisioned.
  • Enforcing Standards: Easily enforce tagging, naming, or security requirements across your infrastructure.
  • Early Feedback: Get immediate feedback in your CI/CD pipelines, catching issues before they become problems.

Writing an OPA Policy with Terraform – An Example

Let's walk through a simple example of how you might write an OPA policy to ensure that all AWS S3 buckets are encrypted. First, you'll need a Terraform plan output in JSON format. You can generate this using terraform plan -out=tfplan.binary and then terraform show -json tfplan.binary > tfplan.json.

Now, let's write a Rego policy (e.g., s3_encryption.rego):

package terraform.aws.s3

default allow = false

allow {
 input.resource_changes[_].type == "aws_s3_bucket"
 input.resource_changes[_].change.after.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm == "AES256"
}

# Deny if any S3 bucket is created or updated without AES256 encryption
deny[msg] {
 some i
 resource := input.resource_changes[i]
 resource.type == "aws_s3_bucket"
 resource.change.actions[_] == "create"
 not resource.change.after.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm == "AES256"
 msg := sprintf("S3 bucket '%s' must have AES256 encryption enabled.", [resource.address])
}

deny[msg] {
 some i
 resource := input.resource_changes[i]
 resource.type == "aws_s3_bucket"
 resource.change.actions[_] == "update"
 not resource.change.after.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm == "AES256"
 msg := sprintf("S3 bucket '%s' must have AES256 encryption enabled.", [resource.address])
}

To test this policy, you can use the opa eval command:

opa eval -i tfplan.json -d s3_encryption.rego "data.terraform.aws.s3.deny"

If your Terraform plan includes an S3 bucket without AES256 encryption, the opa eval command will return a denial message, indicating a policy violation.

How does OPA differ from Oso?

OPA is a general-purpose policy engine used to evaluate rules against JSON-based data inputs. It’s commonly applied to infrastructure and admission control policies like with Terraform and Kubernetes. Oso on the other hand focuses on application-level access control. It can help with with authorization challenges within your application such as determining if a user is allowed to take a certain action or view a certain document. They are complementary tools specializing in authorization for different parts of your stack.

As an example, engineering teams can centralize infrastructure policy using OPA (e.g., deny insecure configurations) and centralize application authorization logic using Oso (e.g., only grant edit access to users with ‘admin’ or ‘editor’ status). Each tool serves a purpose, and together they support a broader policy-as-code initiative.

When to Choose Oso or OPA

This is a common question, and it's important to understand their distinct roles:

  • Use Oso when implementing user and object level authorization logic inside an app (e.g., feature gating, permissions, role hierarchies).
  • Use OPA when you need to validate resource configuration  across environments, especially in CI/CD or with infrastructure-as-code tools.

OPA shines in its ability to integrate with your infrastucture like Kubernetes and Terraform natively. Oso’s strength is in its ability to easily integrate into your application, simplifiying and centralizing authorization logic.

Conclusion

Adopting policy as code with OPA is great for managing your infrastructure. It brings the rigor and benefits of software development practices to your infrastructure, making it more secure, compliant, and manageable. While OPA handles the infrastructure side, remember that Oso is there to provide robust, fine-grained authorization within your applications. Used together, they create a powerful, layered approach to policy enforcement across your entire stack.

About the author

Mathew Pregasen

Technical Writer

Mathew Pregasen is a technical writer and developer based out of New York City. After founding a sales technology company, Battlecard, Mathew focused his attention on technical literature, covering topics spanning security, databases, infrastructure, and authorization. Mathew is an alumnus of Columbia University and YCombinator.

Level up your authorization knowledge

Learn the basics

A list of FAQs related to application authorization.

Read Authorization Academy

A series of technical guides for building application authorization.

Explore more about Oso

Enterprise-grade authorization without redoing your application architecture.