beginner20 min readgetting-startedUpdated: 2024-06-20

Writing Your First Policy

A hands-on tutorial to create, test, and deploy your first infrastructure policy

๐Ÿท๏ธ Topics Covered

how to write your first policy as codestep by step policy tutorial for beginnerspolicy as code getting started guidefirst infrastructure policy examplepolicy development tutorialcreate test deploy policy workflow

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_encryption breaks 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"
]
1

Create Test Data

Build comprehensive test cases covering compliant and non-compliant scenarios

2

Test Positive Cases

Verify that compliant resources pass the policy checks as expected

3

Test Negative Cases

Ensure non-compliant resources are properly caught and produce clear violation messages

4

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

  1. Triggers: Runs on pull requests that modify Terraform or Rego files
  2. Setup: Installs OPA and Terraform in the CI environment
  3. Plan Generation: Creates a Terraform plan and converts it to JSON format for analysis
  4. Policy Validation: Runs opa eval with --fail flag 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_algorithms set for easy updates
  • Multiple violation rules: Each rule handles a specific failure case
  • Terraform plan format: Works with resource_changes structure
  • 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.

Next Steps