Skip to content

Terraform_policy_as_code

Policy as Code (PaC) enables you to define, implement, and enforce policies programmatically. This ensures infrastructure complies with organizational standards, security requirements, and best practices before deployment.

  • Prevent misconfigurations: Catch issues before production
  • Enforce standards: Consistent policy enforcement
  • Audit trails: Track policy changes in version control
  • Self-service: Enable safe self-service provisioning
  • Compliance: Meet regulatory requirements (SOC2, PCI-DSS, HIPAA)
┌─────────────────────────────────────────────────────────────────┐
│ Policy as Code Workflow │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Write │───▶│ Test │───▶│ Enforce │ │
│ │ Policies │ │ Policies │ │ in CI/CD │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Version │ │ Approve/ │ │
│ │ Control │ │ Reject │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘

OPA is an open-source policy engine:

Terminal window
# Install OPA
brew install opa
# Check version
opa version
# Example: Deny public S3 buckets
package terraform
deny[msg] {
input.resource_type == "aws_s3_bucket"
input.config.acl == "public-read"
msg = "S3 bucket must not be public"
}
# With conditions
deny[msg] {
input.resource_type == "aws_instance"
not input.config.vpc_security_group_ids
msg = "EC2 instance must be in a VPC with security groups"
}
# Allow with specific conditions
allow[msg] {
input.resource_type == "aws_iam_user"
input.config.name == "admin"
input.provider == "aws"
msg = "IAM user creation is allowed only for admin"
}
package terraform
test_deny_public_s3 {
deny with input as {
"resource_type": "aws_s3_bucket",
"config": {"acl": "public-read"}
}
}
test_allow_private_s3 {
not deny with input as {
"resource_type": "aws_s3_bucket",
"config": {"acl": "private"}
}
}

Run tests:

Terminal window
opa test policy.rego -v

Sentinel is HashiCorp’s policy as code framework:

# Example: Restrict instance types
import "tfplan/v2" as tfplan
# Get all EC2 instances
instances = tfplan.resource_changes[type is "aws_instance"]
# Define allowed instance types
allowed_types = [
"t3.micro",
"t3.small",
"t3.medium",
]
# Validation rule
validate_instance_type = func() {
all instances as i {
i.change.after.instance_type in allowed_types
}
}
# Main rule
main = rule {
validate_instance_type()
}
# Restrict regions
import "tfplan/v2" as tfplan
allowed_regions = [
"us-east-1",
"us-west-2",
"eu-west-1",
]
main = rule {
all tfplan.resource_changes as rc {
all rc.change.after as key, value {
key != "region" or value in allowed_regions
}
}
}
# Require tags
import "tfplan/v2" as tfplan
required_tags = [
"Environment",
"CostCenter",
"Owner",
]
main = rule {
all tfplan.resource_changes as rc {
rc.type in ["aws_instance", "aws_s3_bucket", "aws_rds_instance"] implies
all required_tags as tag {
rc.change.after.tags contains tag
}
}
}

Policy testing for configuration files:

policy/policies.rego
package main
deny[msg] {
input.kind == "Deployment"
not input.spec.replicas
msg = "Deployment must specify replicas"
}
deny[msg] {
input.kind == "Deployment"
input.spec.replicas == 0
msg = "Deployment replicas cannot be 0"
}
Terminal window
# Install conftest
brew install conftest
# Test Terraform plan
conftest test plan.out -p policy/rego/
# Test Kubernetes manifests
conftest test deployment.yaml -p policy/rego/

AWS Guardrails for Terraform:

Terminal window
# Install cfn-guard
pip install cfn-guard
# Create rules
cfn-guard rulegen --template type=terraform > rules.guard
# AWS SCP Example
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["us-east-1", "us-west-2"]
}
}
}
]
}
name: Policy Check
on: [pull_request]
jobs:
policy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run OPA
run: |
opa eval --fail-defined -d policy.rego "data.terraform.deny" \
-f json \
--fail-defined \
terraform_plan.json > result.json
- name: Check Results
run: |
if [ -s result.json ]; then
echo "Policy violations found:"
cat result.json
exit 1
fi
sentinel.hcl
policy_set "aws-policies" {
source = "./policies"
enforce {
mode = "hard-mandatory"
}
}
.pre-commit-config.yaml
repos:
- repo: https://github.com/open-policy-agent/conftest
rev: v0.25.0
hooks:
- id: conftest
args: ["test", "--all", "--data", "tests/"]
# Security: Require encryption
package terraform
deny[msg] {
input.resource_type == "aws_s3_bucket"
not input.config.server_side_encryption_configuration
msg = "S3 bucket must have encryption enabled"
}
deny[msg] {
input.resource_type == "aws_db_instance"
not input.config.storage_encrypted
msg = "RDS instance must be encrypted"
}
# Require secure protocols
deny[msg] {
input.resource_type == "aws_alb"
not input.config.ssl_policy
msg = "ALB must use secure SSL policy"
}
# Cost: Restrict expensive instance types
package terraform
deny[msg] {
input.resource_type == "aws_instance"
input.config.instance_type in ["m5.4xlarge", "r5.4xlarge", "c5.4xlarge"]
msg = "Large instance types require approval"
}
# Cost: Limit storage size
deny[msg] {
input.resource_type == "aws_db_instance"
input.config.allocated_storage > 1000
msg = "Database storage over 1TB requires approval"
}
# Tags: Required tags
package terraform
required_tags = ["Environment", "CostCenter", "Owner"]
deny[msg] {
input.resource_type == "aws_instance"
missing_tags := required_tags - keys(input.config.tags)
count(missing_tags) > 0
msg = sprintf("Missing required tags: %v", [missing_tags])
}
  1. Start with basics: Begin with simple policies
  2. Version control: Store policies in Git
  3. Test thoroughly: Test policies before enforcement
  4. Gradual enforcement: Start with warnings, then hard enforcement
  5. Document policies: Explain why each policy exists
  6. Regular review: Update policies as requirements change
  7. False positives: Allow for exceptions process

Policy as Code is essential for:

  • Security: Enforce security standards automatically
  • Compliance: Meet regulatory requirements
  • Cost control: Prevent expensive resources
  • Governance: Consistent policy enforcement

Key tools:

  • OPA: Open-source policy engine with Rego
  • Sentinel: HashiCorp’s policy framework
  • Conftest: Configuration policy testing
  • Guardrails: Cloud-specific policy enforcement