Writing Your First Policy
A hands-on tutorial to create, test, and deploy your first infrastructure policy
What You'll Learn
๐ท๏ธ Topics Covered
Step by Step Policy Tutorial: Real-World Implementation
๐ญ Real-World Problem
Your security team has mandated that all S3 buckets must have encryption enabled. However, developers sometimes forget this setting when creating new buckets. You need an automated way to catch this before resources are deployed.
Policy Requirements
โ Core Requirements
- All S3 buckets must have server-side encryption enabled
- Policy should work with Terraform deployments
- Clear error messages for violations
- Easy to test and maintain
๐ฏ Problem Impact
Unencrypted S3 buckets pose significant security risks and compliance violations.
๐ก๏ธ Solution Approach
Automated policy checks prevent non-compliant resources from being deployed.
โก Implementation
Use OPA/Rego to write declarative policies that integrate with existing workflows.
Policy Development Tutorial: Writing Your First Rule
Let's start by creating our first policy file. Create a new file called s3-encryption.rego:
s3-encryption.rego
# s3-encryption.rego
# S3 Bucket Encryption Policy
package aws.s3.encryption
# Import future keywords for better syntax
import future.keywords.in
import future.keywords.if
# Default deny - all S3 buckets must pass encryption check
default allow := false
# Allow if bucket has encryption configured
allow if {
input.resource_type == "aws_s3_bucket"
has_encryption
}
# Check if bucket has server-side encryption
has_encryption if {
input.configuration.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default.sse_algorithm
}
# Generate violation message
violation[msg] {
input.resource_type == "aws_s3_bucket"
not has_encryption
msg := sprintf("S3 bucket '%s' must have server-side encryption enabled", [input.resource_name])
}Breaking Down the Policy
Package Declaration
Organizes policies into namespaces for better management and prevents naming conflicts across teams.
Default Deny
Starts with a "deny all" approach, so resources must explicitly pass checks to be compliant. This is a security best practice.
Allow Conditions
Defines the specific conditions under which a resource is considered compliant using clear, readable logic.
Violation Messages
Provides clear, dynamic feedback about what needs to be fixed when a resource is non-compliant.
๐ง Advanced Rego Concepts Used
- future.keywords: Modern Rego syntax for better readability
- Comprehensions: The
[_]syntax for array iteration - Helper rules:
has_encryptionbreaks complex logic into readable chunks - sprintf function: Dynamic string formatting for detailed error messages
Create Test Deploy Policy Workflow: Testing Best Practices
Before deploying, let's test our policy with sample data for both a compliant and a non-compliant resource. This is crucial for policy development - always test with both positive and negative cases.
test-data.json
{
"compliant_bucket": {
"resource_type": "aws_s3_bucket",
"resource_name": "my-secure-bucket",
"configuration": {
"bucket": "my-secure-bucket",
"server_side_encryption_configuration": [
{
"rule": [
{
"apply_server_side_encryption_by_default": {
"sse_algorithm": "AES256"
}
}
]
}
]
}
},
"non_compliant_bucket": {
"resource_type": "aws_s3_bucket",
"resource_name": "my-insecure-bucket",
"configuration": {
"bucket": "my-insecure-bucket"
}
}
}Running Tests with OPA CLI
OPA Test Commands
# Test compliant bucket (should return 'true')
$ opa eval -d s3-encryption.rego -i test-data.json "data.aws.s3.encryption.allow with input as input.compliant_bucket"
true
# Test non-compliant bucket for violations (should return a message)
$ opa eval -d s3-encryption.rego -i test-data.json "data.aws.s3.encryption.violation with input as input.non_compliant_bucket"
[
"S3 bucket 'my-insecure-bucket' must have server-side encryption enabled"
]Create Test Data
Build comprehensive test cases covering compliant and non-compliant scenarios
Test Positive Cases
Verify that compliant resources pass the policy checks as expected
Test Negative Cases
Ensure non-compliant resources are properly caught and produce clear violation messages
Validate Messages
Check that error messages are actionable and help developers fix issues quickly
First Infrastructure Policy Example: CI/CD Integration
Now let's integrate this policy into a GitHub Actions workflow to check Terraform plans automatically. This ensures every pull request is validated before merge.
.github/workflows/policy-check.yml
# .github/workflows/policy-check.yml
name: Policy Validation
on:
pull_request:
paths:
- '**.tf'
- '**.rego'
jobs:
policy-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install OPA
run: |
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
chmod +x opa
sudo mv opa /usr/local/bin/
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Generate Terraform Plan
run: |
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
- name: Run Policy Check
run: |
opa eval --fail -d policies/s3-encryption.rego -i tfplan.json "data.aws.s3.encryption.violation"What This Workflow Does
๐ Workflow Steps Breakdown
- Triggers: Runs on pull requests that modify Terraform or Rego files
- Setup: Installs OPA and Terraform in the CI environment
- Plan Generation: Creates a Terraform plan and converts it to JSON format for analysis
- Policy Validation: Runs
opa evalwith--failflag to fail the build if violations are found
โ Without Policy Checks
- Manual security reviews
- Post-deployment fixes
- Inconsistent compliance
- Higher risk of breaches
โ With Automated Policies
- Automatic validation
- Pre-deployment catching
- Consistent enforcement
- Reduced security risks
Policy as Code Getting Started Guide: Production Deployment
Let's enhance our policy with more robust checks, like ensuring only approved encryption algorithms are used and handling edge cases better.
s3-encryption-enhanced.rego
# s3-encryption-enhanced.rego
package aws.s3.encryption.enhanced
import future.keywords.in
allowed_algorithms := {"AES256", "aws:kms"}
violation[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
# Check for missing encryption config entirely
not resource.change.after.server_side_encryption_configuration
msg := sprintf("S3 bucket '%s' is missing server-side encryption.", [resource.address])
}
violation[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
# Check for invalid algorithm
config := resource.change.after.server_side_encryption_configuration[_]
algorithm := config.rule[_].apply_server_side_encryption_by_default.sse_algorithm
not algorithm in allowed_algorithms
msg := sprintf("S3 bucket '%s' uses unapproved algorithm '%s'. Allowed: %v", [resource.address, algorithm, allowed_algorithms])
}Production-Ready Enhancements
Configurable Algorithms
A variable allowed_algorithms makes it easy to update approved encryption types without changing logic.
Better Error Messages
Specific feedback distinguishes between missing configuration vs. invalid algorithm for faster debugging.
Cleaner Logic
Separated violation rules improve readability, maintenance, and make it easier to add new checks.
Terraform Integration
Works directly with Terraform plan JSON format for seamless CI/CD integration.
๐๏ธ Advanced Policy Features
- Constant definitions:
allowed_algorithmsset for easy updates - Multiple violation rules: Each rule handles a specific failure case
- Terraform plan format: Works with
resource_changesstructure - Rich error context: Includes resource address and allowed values
๐ Congratulations!
Achievement Unlocked
You've successfully written and tested your first infrastructure policy! You now know how to:
Write Rego Policies
Create declarative policies for cloud resources with proper syntax and structure.
Test Before Deploy
Validate policies with sample data to ensure they work correctly before deployment.
CI/CD Integration
Integrate policies into automated pipelines for continuous compliance checking.
Clear Error Messages
Provide actionable feedback that helps developers fix issues quickly.