What Exactly is Policy as Code?
How Open Policy Agent Works with Terraform
OPA vs. Sentinel: A Quick Comparison
The Benefits of Integrating OPA with Terraform
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.
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.
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.
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.
Integrating OPA with Terraform offers several compelling benefits:
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.
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.
This is a common question, and it's important to understand their distinct roles:
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.
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.