Infrastructure Compliance as a Runtime Constraint
During a recent red-team engagement, we identified a critical misconfiguration where a Terraform-managed AWS Security Group allowed ingress on port 22 from 0.0.0.0/0 in a production environment. To mitigate such exposures, many teams are moving toward a browser based SSH client that eliminates the need for public-facing ports. The infrastructure had passed standard code reviews because the reviewer missed the single line in a 500-line VPC module. This is a systemic failure of manual oversight. By implementing Policy as Code (PaC), we transition from "hoping" engineers follow security documents to "enforcing" those rules at the CI/CD gate.
Infrastructure as Code (IaC) provides the speed to deploy, but without automated guardrails, it provides the speed to fail at scale. We use Terraform Policy as Code to intercept the execution lifecycle between the plan and apply phases. This allows us to inspect the intended state of the cloud environment before a single API call is made to the provider.
What is Policy as Code (PaC) in Infrastructure?
Policy as Code is the practice of managing and enforcing rules governing your infrastructure using a high-level functional or declarative language. In the context of Terraform, it means treating your compliance requirements—such as "All S3 buckets must be encrypted" or "No public IPs in the DB subnet"—as software. These policies are version-controlled, tested, and executed automatically.
We define these rules using specialized languages like Rego (for Open Policy Agent) or Sentinel (for HashiCorp). Unlike manual checklists, PaC provides an unambiguous "Pass/Fail" result. If a Terraform plan violates a policy, the deployment is blocked. This creates a hard boundary for security and compliance that doesn't rely on human memory or manual spot-checks.
The Importance of Policy as Code Using Terraform
We have observed that as organizations scale their cloud footprint in regions like AWS Mumbai (ap-south-1), the complexity of maintaining compliance with local regulations like the Digital Personal Data Protection (DPDP) Act 2023 becomes unmanageable. Manual audits are reactive and often occur weeks after a violation has been introduced. Terraform Policy as Code makes compliance proactive.
By integrating policy checks directly into the Terraform workflow, we mitigate risks like "State File Exfiltration." Terraform state files often contain plaintext secrets. We use OPA to enforce "No-Local-State" policies, ensuring that state is always stored in encrypted remote backends like S3 or Terraform Cloud, reducing the surface area for credential theft.
Key Benefits: Security, Compliance, and Governance
- Deterministic Enforcement: Policies are executed the same way every time, eliminating the variance inherent in human reviews.
- Regulatory Alignment: We can map specific Rego rules to RBI (Reserve Bank of India) guidelines for data localization, ensuring no resources are provisioned outside Indian borders.
- Reduced Mean Time to Remediation (MTTR): Developers receive immediate feedback in their PRs, allowing them to fix security issues before they reach production.
- Auditability: Every policy check is logged, providing a clear trail for auditors to see that every deployment met the required security standards.
Terraform Sentinel Policy as Code: The Native HashiCorp Solution
Sentinel is the proprietary policy-as-code framework integrated into HCP Terraform and Terraform Enterprise. It uses a high-level functional language designed specifically for the HashiCorp ecosystem. We find Sentinel particularly effective for fine-grained control because it has deep access to the Terraform plan, state, and configuration.
Sentinel operates on three enforcement levels: advisory (logs a warning), soft-mandatory (requires an override), and hard-mandatory (stops the run). This flexibility allows us to introduce new security rules gradually without breaking existing developer workflows immediately.
Terraform Policy as Code with OPA (Open Policy Agent)
Open Policy Agent (OPA) is a CNCF graduated project that uses the Rego language. It is vendor-neutral, meaning we can use the same policy engine for Terraform, Kubernetes, and application-level authorization. In our testing, OPA's primary advantage is its portability and the massive community-driven library of policies. For those getting started, we recommend our guide on implementing automated IaC compliance using Rego.
To use OPA with Terraform, we must convert the binary plan file into a JSON format. This extra step allows the OPA engine to parse the planned changes and compare them against our Rego rules.
# Standard workflow for OPA evaluation
terraform plan -out=tfplan.binary terraform show -json tfplan.binary > tfplan.json opa eval --data policy.rego --input tfplan.json "data.terraform.analysis.deny"
Comparing Sentinel vs. OPA for Infrastructure Governance
| Feature | HashiCorp Sentinel | Open Policy Agent (OPA) |
|---|---|---|
| License | Proprietary (HCP/Enterprise) | Open Source (Apache 2.0) |
| Language | Sentinel (Functional) | Rego (Declarative) |
| Ecosystem | HashiCorp specific | Cloud-native (K8s, Envoy, etc.) |
| Integration | Native in Terraform Cloud | Requires CI/CD integration (Conftest) |
We generally recommend OPA for teams looking for a multi-cloud, multi-tool policy engine. However, for organizations heavily invested in the HashiCorp stack, Sentinel offers a more seamless, "batteries-included" experience with less glue code.
Terraform Policy as Code for AWS: Best Practices
When deploying to AWS, we focus policies on identity management, network perimeter, and data encryption. For Indian organizations, enforcing data residency is a top priority. We write policies that inspect the provider configuration and individual resource locations to ensure they are restricted to ap-south-1 (Mumbai) or ap-south-2 (Hyderabad).
We also use PaC to prevent the creation of unencrypted S3 buckets or RDS instances. This is a direct requirement for DPDP Act compliance. Below is a Rego snippet we use to block any AWS Security Group that allows global SSH access, which is a common entry point for ransomware. This risk is well-documented in OpenSSH Security advisories.
package terraform.analysis
import input as tfplan
Deny if AWS Security Group allows global SSH access
deny[msg] { resource := tfplan.resource_changes[_] resource.type == "aws_security_group" ingress := resource.change.after.ingress[_]
ingress.from_port <= 22 ingress.to_port >= 22
some i ingress.cidr_blocks[i] == "0.0.0.0/0"
msg = sprintf("Security Risk: Resource '%v' allows SSH (Port 22) from 0.0.0.0/0. Policy violation: INFRA-SEC-01.", [resource.address]) }
Terraform Policy as Code for Azure: Securing the Cloud
In Azure environments, we leverage the azurerm provider's specific attributes. A critical check we implement is ensuring that all Storage Accounts have public_network_access_enabled set to false and min_tls_version set to TLS1_2.
Azure-specific policies also frequently target the allow_blob_public_access attribute. We have seen multiple instances where developers accidentally enable public access to blobs containing sensitive PII, which would lead to significant fines under the DPDP Act 2023 (up to ₹250 crore).
Deep Dive: Terraform Azurerm Policy as Code Implementation
When writing policies for Azure, you must account for the nested nature of Azure resources. For example, a Virtual Network might contain subnets which in turn contain Network Security Groups (NSGs). We use OPA's recursion capabilities to traverse these relationships.
We also enforce tagging policies. In many Indian enterprises, cost center allocation is mandatory. We write policies that deny the creation of any Azure resource that doesn't include a CostCenter and Environment tag. This ensures that the finance team can track cloud spend in INR accurately across different business units.
Configuring Terraform Cloud Policy as Code Workflows
HCP Terraform (formerly Terraform Cloud) simplifies policy enforcement through "Policy Sets." We connect a Git repository containing our Sentinel or OPA policies to the HCP Terraform organization. Every time a terraform plan is triggered in any workspace, HCP Terraform automatically runs the policy checks.
We configure "Run Tasks" to integrate third-party security scanners as well. This creates a multi-layered defense where the infrastructure is checked for both policy violations (Sentinel/OPA) and known vulnerabilities (using tools like Bridgecrew or Snyk).
Scaling Governance with HCP Terraform Policy as Code
To scale governance across hundreds of workspaces, we use "Global Policy Sets." These are policies that apply to every single workspace in the organization regardless of the team or project. This is where we place our non-negotiable security rules, such as mandatory encryption and restricted regions.
For project-specific rules, we use scoped policy sets. For example, a development environment might have a policy that limits instance sizes to t3.micro to save costs, while the production environment allows larger m5.large instances but enforces multi-AZ deployment.
Enforcing Organizational Standards across Workspaces
One of the most effective patterns we've used is the "Policy Library" approach. Instead of writing monolithic policy files, we break them down into modules:
- compute.rego: Rules for EC2, Lambda, and Autoscaling.
- network.rego: Rules for VPCs, Subnets, and Security Groups.
- iam.rego: Rules for Roles, Policies, and Users.
- compliance_india.rego: Specific rules for DPDP Act and RBI guidelines.
This modularity allows different teams to contribute to the policy codebase. The security team maintains iam.rego, while the platform team maintains compute.rego.
How Terraform Policy as a Code Checks Work in the Lifecycle
The Terraform lifecycle consists of init, plan, and apply. Policy checks occur immediately after the plan is generated. This is the "Pre-apply" phase. The policy engine receives the plan data, which contains the diff between the current state and the desired state.
If the policy engine returns a "Deny" result, the lifecycle is halted. The apply phase is never reached. This is critical because it prevents the creation of insecure resources. If we were to run checks after the apply (Post-apply), the vulnerable resource would already exist in the cloud, even if only for a few minutes.
Automating Policy Enforcement in CI/CD Pipelines
For teams not using HCP Terraform, we automate policy enforcement using conftest, a utility built on top of OPA. We integrate this into GitHub Actions or GitLab CI. This allows us to fail the build if a developer submits a PR that violates our security standards.
# GitHub Action snippet for OPA enforcement
jobs: terraform-policy-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Terraform Plan run: | terraform init terraform plan -out=tfplan.binary terraform show -json tfplan.binary > tfplan.json - name: Run Conftest run: | conftest test tfplan.json --policy ./policies
This pipeline ensures that every single change is validated. We also recommend running these checks on a schedule (e.g., every 24 hours) to detect "configuration drift" where someone might have manually changed a setting in the AWS Console, bypassing the CI/CD pipeline.
Handling Policy Violations and Overrides
Not every violation should stop production. We implement an override mechanism for exceptional cases. In Sentinel, this is handled by the soft-mandatory level. In OPA/Conftest, we use a "break-glass" approach where a senior security engineer can add a specific tag to the PR or a bypass flag in the CI configuration.
Every override must be documented with a business justification. We use a metadata block in our Rego policies to track these exceptions. This ensures that when the auditors ask why a specific security group was allowed to be open, we have a digital paper trail linked to a Jira ticket or a management approval.
Modularizing Your Policy Codebase
As your policy library grows to hundreds of rules, maintainability becomes a challenge. We treat policy code exactly like application code. This means using functions to reduce duplication. For example, we write a helper function in Rego to check if a resource has a specific tag, rather than rewriting the logic for every resource type.
# Helper function for tag enforcement
package lib.tags
has_tag(resource, tag_key) { resource.change.after.tags[tag_key] }
is_environment_valid(resource) { valid_envs := {"prod", "stage", "dev"} val := resource.change.after.tags["Environment"] valid_envs[val] }
Testing Terraform Policies Before Deployment
We never deploy a policy without testing it. OPA provides a built-in test runner. We create "mock" Terraform plan JSON files—some that should pass and some that should fail—and run our policies against them. This prevents "False Positives" that frustrate developers and "False Negatives" that let security holes through.
# Running OPA unit tests
$ opa test ./policies -v data.terraform.analysis.test_deny_ssh_global: PASS (1.2ms) data.terraform.analysis.test_allow_ssh_private: PASS (0.8ms) -------------------------------------------------------------------------------- PASS: 2/2
Testing is especially important when dealing with complex CVEs. For instance, to mitigate CVE-2022-23652 (SSRF in OPA), we write tests to ensure our policies do not use the http.send function with unsanitized user input from the Terraform plan.
Monitoring and Auditing Policy Compliance
Finally, we treat policy violations as security events. When a policy check fails in the CI/CD pipeline, we don't just stop the build; we send the violation data to a centralized logging system like an ELK stack or a SIEM. This allows us to identify patterns—such as a specific team consistently failing encryption checks—which might indicate a need for more training or better base templates.
In the Indian context, maintaining these logs is mandatory for compliance with the CERT-In directions of April 2022, which require service providers to maintain logs for a period of 180 days. By exporting PaC results, we create a robust record of our "Security by Design" efforts.
# Next Command: Check your plan against the compliance suite
conftest test tfplan.json --policy ./compliance/india-dpdp-2023.rego
