TerraForce

TerraForce Documentation

← Back to Home

Introduction

TerraForce is a powerful policy enforcement tool designed for Terraform, enabling teams to maintain consistency and compliance in their infrastructure-as-code practices.

Key Features

  • Complete Lifecycle Coverage: pre-plan, pre-apply, post-apply, and pre-destroy policy checks
  • Flexible Policy Definition: Using Go templates and YAML format
  • CI/CD Integration: Seamless integration with existing workflows
  • Built-in Testing: Comprehensive testing support for TDD

Installation

TerraForce is distributed as a single binary. To install:

# Download the appropriate version for your platform
wget https://terraforce-builds-henrybravo-nl.s3.eu-west-1.amazonaws.com/linux/arm64/terraforce-1.0.0-arm64-linux.zip

# Unpack the archive
unzip -p terraforce-1.0.0-arm64-linux.zip | sudo tee terraforce > /dev/null

# Make the binary executable
chmod +x terraforce

Verify Installation

# Verify the installation
terraforce --version

HCL to JSON Converter

TerraForce's pre-plan stage requires your Terraform configuration files (HCL format) to be converted to JSON. The hcl2json tool simplifies this conversion process.

Installation

# Download hcl2json binary
wget https://hcl2json-builds-henrybravo-nl.s3.eu-west-1.amazonaws.com/hcl2json-1.0.0-arm64-linux.zip

# Unpack and install
unzip -p hcl2json-1.0.0-arm64-linux.zip | sudo tee hcl2json > /dev/null
chmod +x hcl2json

Usage Example

# Convert Terraform files found in current directory

% hcl2json -D .

# Verify conversion
% cat *.json
{
    "resource": {
    "aws_instance": {
        "example": {
        "instance_type": "t4g.medium",
        "tags": {
            "Name": "example-instance"
        }
        }
    }
    }
}

Policy Structure

TerraForce policies are defined in YAML format and organized by lifecycle stages. Each policy consists of rules that are evaluated at specific points in the Terraform workflow.

Basic Structure

# basic-policy.yml
pre_plan:
    - name: "Required Tags"
    description: "Ensure all resources have required tags"
    condition: |
        {{- $valid := true -}}
        {{- range $name, $resource := .resource -}}
        {{- if not (index $resource "tags") -}}
            {{- $valid = false -}}
        {{- end -}}
        {{- end -}}
        {{- $valid -}}
    deny_message: "All resources must have tags defined"

Policy Components

  • Lifecycle Stage: The Terraform lifecycle point where the policy is evaluated (pre_plan, pre_apply, etc.)
  • Name: Unique identifier for the policy rule
  • Description: Clear explanation of what the rule checks
  • Condition: Go template expression that evaluates to true/false
  • Deny Message: Explanation shown when the policy check fails

Advanced Policy Example

# comprehensive-policy.yml
pre_plan:
    - name: "Production Resource Policy"
    description: "Enforce production environment standards"
    condition: |
        {{- /* Initialize variables */ -}}
        {{- $valid := true -}}
        {{- $errors := list -}}
        
        {{- /* Define standards */ -}}
        {{- $required_tags := list "Environment" "Owner" "CostCenter" -}}
        {{- $allowed_instances := list "t3.medium" "t3.large" "t3.xlarge" -}}
        
        {{- /* Check each resource */ -}}
        {{- range $name, $resource := .resource -}}
        {{- /* Verify tags exist */ -}}
        {{- $tags := $resource.tags | default dict -}}
        {{- range $required_tags -}}
            {{- if not (index $tags . | default "") -}}
            {{- $errors = append $errors (printf "Missing required tag: %s" .) -}}
            {{- $valid = false -}}
            {{- end -}}
        {{- end -}}
        
        {{- /* Check environment-specific rules */ -}}
        {{- if eq (index $tags "Environment" | default "") "production" -}}
            {{- /* Verify instance types */ -}}
            {{- if eq $resource.type "aws_instance" -}}
            {{- if not (contains $allowed_instances $resource.instance_type) -}}
                {{- $errors = append $errors "Invalid instance type for production" -}}
                {{- $valid = false -}}
            {{- end -}}
            {{- end -}}
            
            {{- /* Verify encryption */ -}}
            {{- if not $resource.encrypted -}}
            {{- $errors = append $errors "Production resources must be encrypted" -}}
            {{- $valid = false -}}
            {{- end -}}
        {{- end -}}
        {{- end -}}
        {{- $valid -}}
    deny_message: |
        Production environment policy violations:
        {{- range $error := $errors }}
        - {{ $error }}
        {{- end }}

Best Practices for Policy Structure

  • Use Clear Names: Policy names should be descriptive and indicate their purpose
  • Group Related Rules: Keep related checks within the same policy block
  • Detailed Messages: Provide clear deny messages that help users understand and fix violations
  • Structured Variables: Define reusable variables at the start of your condition
  • Error Collection: Collect all errors rather than failing on the first one

Lifecycle Stages

TerraForce operates at four key stages of the Terraform lifecycle, each serving a specific purpose in policy enforcement.

Pre-Plan Stage

# pre-plan policies validate configuration before planning
pre_plan:
    - name: "Resource Naming Convention"
    description: "Enforce naming standards before planning"
    condition: |
        {{- $valid := true -}}
        {{- $pattern := "^[a-z]+(-[a-z0-9]+)*$" -}}
        {{- range $name, $resource := .resource -}}
        {{- if not (regexMatch $pattern $name) -}}
            {{- $valid = false -}}
        {{- end -}}
        {{- end -}}
        {{- $valid -}}
    deny_message: "Resource names must use kebab-case"

Pre-plan checks validate:

  • Resource naming conventions
  • Required configuration attributes
  • Tag standards
  • Basic security configurations

Pre-Apply Stage

# pre-apply policies validate planned changes
pre_apply:
    - name: "Production Security Standards"
    description: "Enforce security controls before applying changes"
    condition: |
        {{- $valid := true -}}
        {{- range $name, $resource := .planned_resources -}}
        {{- if eq $resource.environment "production" -}}
            {{- if not $resource.encrypted -}}
            {{- $valid = false -}}
            {{- end -}}
        {{- end -}}
        {{- end -}}
        {{- $valid -}}
    deny_message: "Production resources must be encrypted"

Pre-apply checks validate:

  • Changes to sensitive resources
  • Security group modifications
  • Network configuration changes
  • Resource deletion safeguards

Post-Apply Stage

# post-apply policies validate the resulting state
post_apply:
    - name: "Resource Compliance Check"
    description: "Verify resource compliance after deployment"
    condition: |
        {{- $valid := true -}}
        {{- range $name, $resource := .state_resources -}}
        {{- /* Verify actual configuration matches expected */ -}}
        {{- if not (eq $resource.actual_config $resource.expected_config) -}}
            {{- $valid = false -}}
        {{- end -}}
        {{- end -}}
        {{- $valid -}}
    deny_message: "Resource configuration drift detected"

Post-apply checks validate:

  • Configuration drift
  • Compliance requirements
  • Resource state verification
  • Integration validation

Pre-Destroy Stage

# pre-destroy policies protect against unauthorized destruction
pre_destroy:
    - name: "Protected Resource Check"
    description: "Prevent destruction of critical resources"
    condition: |
        {{- $valid := true -}}
        {{- range $name, $resource := .resources_to_destroy -}}
        {{- if index $resource.tags "Protected" | default "" | eq "true" -}}
            {{- $valid = false -}}
        {{- end -}}
        {{- end -}}
        {{- $valid -}}
    deny_message: "Cannot destroy protected resources"

Pre-destroy checks protect:

  • Critical production resources
  • Databases and data stores
  • Shared infrastructure components
  • Compliance-required resources

Stage Execution

# Execute stage-specific checks
terraforce pre-plan policy.yml config.json
terraforce pre-apply policy.yml planfile.json
terraforce post-apply policy.yml state.json
terraforce pre-destroy policy.yml state.json

Template Language Reference

TerraForce uses Go's text/template package for policy conditions. Here are the key concepts and features:

Basic Syntax

{{- /* Basic template syntax */ -}}
{{ .Field }}           # Access a field
{{ $var := .Field }}   # Assign to variable
{{ if .Condition }}    # Conditional
{{ range .Items }}     # Iteration
{{ .SubField }}        # Nested field access
{{ end }}

Variables and Scope

  • . (dot) represents the current context
  • $variable creates a new variable
  • $ accesses the root context from any scope

Chain Operations

Chain operations allow you to combine multiple functions using pipes (|) for cleaner, more maintainable templates.

Basic Chaining

condition: |
{{- /* Convert to lowercase, trim, and check pattern */ -}}
{{- $name := .resource_name | toLower | trim -}}
{{- regexMatch "^[a-z]+(-[a-z0-9]+)*$" $name -}}

Complex Transformations

condition: |
{{- /* Process tag values with multiple operations */ -}}
{{- $tags := .tags | default dict -}}
{{- $env := index $tags "Environment" | default "unknown" | toLower | trim -}}
{{- $owner := index $tags "Owner" | default "" | split "@" | first -}}

{{- /* Validate transformed values */ -}}
{{- $valid := and 
    (contains (list "prod" "stage" "dev") $env)
    (ne $owner "")
-}}
{{- $valid -}}

Best Practices

  • Chain related operations for readability
  • Use intermediate variables for complex chains
  • Handle potential nil values early in the chain
  • Break long chains into multiple steps when logic becomes complex

Default Values

Proper handling of optional fields and default values is crucial for robust policy conditions.

Basic Default Handling

condition: |
{{- $tags := .tags | default dict -}}
{{- $env := index $tags "Environment" | default "unknown" -}}
{{- $owner := index $tags "Owner" | default "" -}}

{{- /* Validate with defaults */ -}}
{{- and (ne $env "unknown") (ne $owner "") -}}

Nested Defaults

condition: |
{{- /* Handle deeply nested values */ -}}
{{- $config := .configuration | default dict -}}
{{- $settings := index $config "settings" | default dict -}}
{{- $backup := index $settings "backup" | default dict -}}
{{- $retention := index $backup "retention_days" | default 7 -}}

{{- /* Validate with nested defaults */ -}}
{{- le $retention 30 -}}

Default Value Best Practices

  • Always provide sensible defaults for optional fields
  • Use type-appropriate defaults (empty string, zero, empty dict/list)
  • Chain defaults for nested structures
  • Document default values in policy descriptions

Performance Optimization

Optimize template performance through efficient variable usage and conditional evaluation.

Variable Caching

condition: |
{{- /* Cache frequently accessed values */ -}}
{{- $tags := .tags | default dict -}}
{{- $env := index $tags "Environment" | default "" -}}

{{- /* Use cached values */ -}}
{{- if eq $env "production" -}}
{{- /* Multiple checks using $env */ -}}
{{- end -}}

Efficient Iteration

condition: |
{{- /* Pre-compute reusable lists */ -}}
{{- $allowed_types := list "t3.micro" "t3.small" "t3.medium" -}}

{{- /* Single pass validation */ -}}
{{- range $name, $resource := .resource -}}
{{- /* Multiple checks in single iteration */ -}}
{{- if and
    (contains $allowed_types $resource.type)
    (index $resource.tags "Environment")
    (index $resource.tags "Owner")
-}}
{{- end -}}
{{- end -}}

Optimization Patterns

  • Cache frequently accessed values
  • Minimize nested range operations
  • Pre-compute reusable lists and maps
  • Combine multiple conditions in single passes
  • Use early returns for invalid states

Error Handling Patterns

Implement robust error handling in policy conditions to handle edge cases gracefully.

Defensive Programming

condition: |
{{- /* Initialize error collection */ -}}
{{- $valid := true -}}
{{- $errors := list -}}

{{- /* Safe map access */ -}}
{{- $tags := .tags | default dict -}}
{{- $env := index $tags "Environment" | default "unknown" -}}

{{- /* Type conversion with validation */ -}}
{{- $size := 0 -}}
{{- if $sizeStr := .instance_size -}}
{{- if $converted := toInt $sizeStr | default 0 -}}
    {{- $size = $converted -}}
{{- else -}}
    {{- $errors = append $errors "Invalid size format" -}}
{{- end -}}
{{- end -}}

{{- /* Validate results */ -}}
{{- if and (ne $env "unknown") (gt $size 0) -}}
{{- $valid = true -}}
{{- else -}}
{{- $errors = append $errors "Missing required fields" -}}
{{- end -}}

{{- $valid -}}
deny_message: |
Validation errors:
{{- range $error := $errors }}
- {{ $error }}
{{- end }}

Error Handling Strategies

  • Always initialize variables with safe defaults
  • Use multi-step validation for complex types
  • Collect all errors instead of failing fast
  • Provide detailed error messages
  • Handle nil values explicitly

Complex Conditions

Break down complex policy conditions into manageable, reusable components.

Resource Validation Example

condition: |
{{- /* Initialize state */ -}}
{{- $valid := true -}}
{{- $errors := list -}}

{{- /* Define reusable validation rules */ -}}
{{- $validateTags := dict
    "Environment" (list "prod" "stage" "dev")
    "CostCenter" (list "eng" "ops" "infra")
    "Owner" (list "team-a" "team-b" "team-c")
-}}

{{- /* Extract commonly used values */ -}}
{{- $resource := . -}}
{{- $tags := $resource.tags | default dict -}}
{{- $env := index $tags "Environment" | default "" | toLower -}}

{{- /* Environment-specific rules */ -}}
{{- $envRules := dict
    "prod" (dict
        "min_size" "t3.medium"
        "encryption" true
        "backup" true
    )
    "stage" (dict
        "min_size" "t3.small"
        "encryption" true
        "backup" false
    )
-}}

{{- /* Validation blocks */ -}}
{{- /* 1. Tag validation */ -}}
{{- range $tag, $allowed := $validateTags -}}
{{- $value := index $tags $tag | default "" -}}
{{- if not (contains $allowed $value) -}}
    {{- $errors = append $errors (printf "Invalid %s: %s" $tag $value) -}}
    {{- $valid = false -}}
{{- end -}}
{{- end -}}

{{- /* 2. Environment-specific validation */ -}}
{{- if $rules := index $envRules $env | default dict -}}
{{- /* Size check */ -}}
{{- if lt $resource.instance_type $rules.min_size -}}
    {{- $errors = append $errors (printf "Instance type %s below minimum %s for %s" 
        $resource.instance_type $rules.min_size $env) -}}
    {{- $valid = false -}}
{{- end -}}

{{- /* Security checks */ -}}
{{- if and $rules.encryption (not $resource.encrypted) -}}
    {{- $errors = append $errors "Encryption required" -}}
    {{- $valid = false -}}
{{- end -}}
{{- end -}}

{{- $valid -}}
deny_message: |
Validation failures:
{{- range $error := $errors }}
- {{ $error }}
{{- end }}

Best Practices for Complex Conditions

  • Group related rules in dictionaries
  • Extract reusable validation logic
  • Use meaningful variable names
  • Add comments for complex logic blocks
  • Structured error collection and reporting

Tips for Managing Complexity

  • Break conditions into logical sections
  • Use intermediate variables to store partial results
  • Document assumptions and dependencies
  • Maintain consistent naming conventions
  • Regular refactoring to improve readability

Function Reference

Math Operations

Function Description Arguments Example Result
add Addition (a, b int) {{ add 1 2 }} 3
subtract Subtraction (a, b int) {{ subtract 5 2 }} 3
multiply Multiplication (a, b int) {{ multiply 2 3 }} 6
divide Division (a, b int) {{ divide 6 2 }} 3
mod Modulo (a, b int) {{ mod 7 3 }} 1

String Operations

Function Description Arguments Example Result
toLower Convert to lowercase (s string) {{ toLower "TEST" }} "test"
toUpper Convert to uppercase (s string) {{ toUpper "test" }} "TEST"
trim Remove whitespace (s string) {{ trim " test " }} "test"
hasPrefix Check string prefix (s, prefix string) {{ hasPrefix "test" "te" }} true
hasSuffix Check string suffix (s, suffix string) {{ hasSuffix "test" "st" }} true
contains Check substring (s, substr string) {{ contains "test" "es" }} true
split Split string (s, sep string) {{ split "a,b" "," }} ["a", "b"]
join Join strings (elements []string, sep string) {{ join .tags "," }} "tag1,tag2"
replace Replace n occurrences (s, old, new string, n int) {{ replace "test" "t" "x" 1 }} "xest"
replaceAll Replace all occurrences (s, old, new string) {{ replaceAll "test" "t" "x" }} "xesx"

Type Conversion

Function Description Arguments Example Result
toString Convert to string (v interface{}) {{ toString 123 }} "123"
toInt Convert to integer (v interface{}) {{ toInt "123" }} 123
toBool Convert to boolean (v interface{}) {{ toBool "true" }} true

Collection Operations

Function Description Arguments Example Result
length Get collection length (v interface{}) {{ length .items }} 3
len Get collection length (v interface{}) {{ len .items }} 3
keys Get map keys (sorted) (m map[string]interface{}) {{ keys .tags }} ["env", "name"]
values Get map values (m map[string]interface{}) {{ values .tags }} ["prod", "webserver"]
list Create a list (...interface{}) {{ list "a" "b" "c" }} ["a", "b", "c"]
dict Create a dictionary (interface{}) {{ dict "key" "value" }} {"key": "value"}
index Access map/slice element (collection, key interface{}) {{ index .tags "Environment" }} "prod"
append Append to list (slice []interface{}, v ...interface{}) {{ append .list "new" }} [old, "new"]

Regular Expression Operations

Function Description Arguments Example Result
regexMatch Pattern matching (pattern, s string) {{ regexMatch "^t.*t$" "test" }} true
regexFind Find first match (pattern, s string) {{ regexFind "[0-9]+" "abc123def" }} "123"
regexFindAll Find all matches (pattern, s string, n int) {{ regexFindAll "[0-9]+" "123 456" -1 }} ["123", "456"]
regexReplace Replace matches (pattern, repl, s string) {{ regexReplace "\\d+" "NUM" "abc123" }} "abcNUM"

Comparison Operations

Function Description Arguments Example Result
eq Equal to (a, b interface{}) {{ eq .env "prod" }} true
ne Not equal to (a, b interface{}) {{ ne .env "dev" }} true
lt Less than (a, b interface{}) {{ lt 2 3 }} true
le Less than or equal (a, b interface{}) {{ le 2 2 }} true
gt Greater than (a, b interface{}) {{ gt 3 2 }} true
ge Greater than or equal (a, b interface{}) {{ ge 2 2 }} true

Debug Functions

Function Description Arguments Example Result
debug Get detailed value representation (v interface{}) {{ debug .tags }} map[string]string{"env":"prod"}
typeOf Get type information (v interface{}) {{ typeOf .count }} "int"

Best Practices

Policy Organization

pre_plan:
- name: "Resource Naming Policy"
description: "Enforce consistent resource naming"
condition: |
    {{- $valid := true -}}
    {{- $pattern := "^[a-z]+(-[a-z0-9]+)*$" -}}
    {{- range $name, $resource := .resource -}}
    {{- if not (regexMatch $pattern $name) -}}
        {{- $valid = false -}}
    {{- end -}}
    {{- end -}}
    {{- $valid -}}
deny_message: "Resource names must follow kebab-case pattern"

Policy Linting

TerraForce includes a built-in linter to validate policy files before execution. Although the linter checks may return linting issues, and the issues are related to long conditions, terraforce will still execute using the policy if there are no errors in the policy definition.

Running the Linter

# Lint a single policy file
terraforce lint policy.yml

# Lint all policies in a directory
terraforce lint --dir ./policies

Linting Rules

Rule Description Example Error
Valid YAML Checks for valid YAML syntax "Invalid YAML at line 5: mapping values are not allowed here"
Template Syntax Validates Go template expressions "Unclosed template action in condition"
Required Fields Ensures all required fields are present "Missing required field 'deny_message' in rule 'aws_tags'"
Function Usage Verifies only valid functions are used "Unknown function 'invalidFunc' at line 12"

Troubleshooting

Common issues you might encounter when using TerraForce and how to resolve them.

Common Error Messages

Error Cause Solution
template: pattern matches multiple files Malformed template syntax in policy condition Check for matching {{ and }} brackets
failed to load policy Invalid YAML format or file permissions Validate YAML syntax and file permissions
failed to parse input Invalid JSON format in Terraform files Verify JSON format of plan/state files
function "foo" not defined Using undefined template function Check function reference for available functions

Debug Mode

# Enable debug output
terraforce -d pre-plan policy.yml config.json

# Output includes:
# - Template evaluation details
# - Variable values
# - Resource processing steps
# - Error traces

Template Debugging

pre_plan:
- name: "Debug Example"
condition: |
    {{- /* Debug variable values */ -}}
    {{- $debug := true -}}
    {{- $value := .some_field -}}
    {{- if $debug -}}
    {{- printf "Processing value: %v\n" $value -}}
    {{- end -}}
    
    {{- /* Track execution path */ -}}
    {{- if eq $value "expected" -}}
    {{- if $debug -}}{{- printf "Path A taken\n" -}}{{- end -}}
    true
    {{- else -}}
    {{- if $debug -}}{{- printf "Path B taken\n" -}}{{- end -}}
    false
    {{- end -}}

Common Issues

Policy Not Being Applied

  • Check Stage: Verify the policy is defined for the correct lifecycle stage
  • File Location: Ensure policy file path is correct
  • File Permissions: Verify read permissions on policy and input files
  • JSON Format: Validate Terraform JSON output format

Unexpected Policy Results

  • Variable Scope: Check variable scoping in template conditions
  • Type Conversion: Verify type conversion in comparisons
  • Null Values: Handle null values with default filters
  • Range Context: Verify context inside range blocks

Debugging Example

pre_plan:
- name: "Resource Tag Validator"
    condition: |
    {{- /* Initialize debugging */ -}}
    {{- $valid := true -}}
    {{- $required_tags := list "Environment" "Owner" -}}
    {{- printf "Required tags: %v\n" $required_tags -}}
    
    {{- /* Process resources */ -}}
    {{- range $name, $resource := .resource -}}
        {{- printf "Checking resource: %s\n" $name -}}
        {{- $tags := $resource.tags | default dict -}}
        {{- printf "Resource tags: %v\n" $tags -}}
        
        {{- range $required_tags -}}
        {{- $tag := . -}}
        {{- if not (index $tags $tag | default "") -}}
            {{- printf "Missing required tag: %s\n" $tag -}}
            {{- $valid = false -}}
        {{- end -}}
        {{- end -}}
    {{- end -}}
    
    {{- printf "Final validation result: %v\n" $valid -}}
    {{- $valid -}}
    deny_message: "Resources missing required tags"

Support Resources

  • Documentation: Refer to the function reference and examples
  • Debug Output: Use the -d flag for detailed execution information
  • Common Patterns: Check the best practices section for recommended approaches
  • Contact Support: Email terraforce@henrybravo.nl for additional assistance

Comprehensive Examples

Real-world policy examples demonstrating common use cases and best practices.

AWS Security Standards Policy

# aws-security-policy.yml
pre_plan:
    - name: "AWS Security Baseline"
    description: "Enforce AWS security standards"
    condition: |
        {{- /* Initialize validation */ -}}
        {{- $valid := true -}}
        {{- $errors := list -}}

        {{- /* Security standards */ -}}
        {{- $allowed_instance_types := list "t3.micro" "t3.small" "t3.medium" -}}
        {{- $required_tags := list "Environment" "Owner" "CostCenter" "SecurityLevel" -}}
        {{- $allowed_regions := list "eu-west-1" "eu-central-1" -}}

        {{- /* Check each resource */ -}}
        {{- range $name, $resource := .resource -}}
        {{- if eq $resource.type "aws_instance" -}}
            {{- /* Instance type validation */ -}}
            {{- if not (contains $allowed_instance_types $resource.instance_type) -}}
            {{- $errors = append $errors (printf "Instance %s: unauthorized type %s" $name $resource.instance_type) -}}
            {{- $valid = false -}}
            {{- end -}}

            {{- /* Encryption validation */ -}}
            {{- if not $resource.ebs_block_device.encrypted -}}
            {{- $errors = append $errors (printf "Instance %s: EBS volumes must be encrypted" $name) -}}
            {{- $valid = false -}}
            {{- end -}}

            {{- /* Security group validation */ -}}
            {{- range $resource.security_group_ids -}}
            {{- if eq . "sg-0000000000" -}}
                {{- $errors = append $errors (printf "Instance %s: default security group not allowed" $name) -}}
                {{- $valid = false -}}
            {{- end -}}
            {{- end -}}
        {{- end -}}

        {{- /* Tag validation */ -}}
        {{- $tags := $resource.tags | default dict -}}
        {{- range $required_tags -}}
            {{- if not (index $tags . | default "") -}}
            {{- $errors = append $errors (printf "Resource %s: missing required tag %s" $name .) -}}
            {{- $valid = false -}}
            {{- end -}}
        {{- end -}}
        {{- end -}}

        {{- $valid -}}
    deny_message: |
        Security policy violations:
        {{- range $error := $errors }}
        - {{ $error }}
        {{- end }}

Multi-Environment Network Policy

# network-policy.yml
pre_plan:
    - name: "Network Security Standards"
    description: "Enforce network security across environments"
    condition: |
        {{- /* Initialize */ -}}
        {{- $valid := true -}}
        {{- $errors := list -}}

        {{- /* Network rules by environment */ -}}
        {{- $network_rules := dict -}}
        {{- $_ := set $network_rules "production" (dict 
            "allowed_cidrs" (list "10.0.0.0/8" "172.16.0.0/12")
            "required_ports" (list "80" "443")
            "forbidden_ports" (list "22" "3389")
        ) -}}
        {{- $_ := set $network_rules "staging" (dict 
            "allowed_cidrs" (list "10.0.0.0/8" "192.168.0.0/16")
            "required_ports" (list "80" "443")
            "forbidden_ports" (list "3389")
        ) -}}

        {{- /* Process resources */ -}}
        {{- range $name, $resource := .resource -}}
        {{- if eq $resource.type "aws_security_group" -}}
            {{- $env := index $resource.tags "Environment" | default "unknown" | toLower -}}
            {{- $rules := index $network_rules $env | default dict -}}
            
            {{- /* Check ingress rules */ -}}
            {{- range $rule := $resource.ingress -}}
            {{- /* Check CIDR blocks */ -}}
            {{- range $cidr := $rule.cidr_blocks -}}
                {{- $cidr_valid := false -}}
                {{- range $allowed := $rules.allowed_cidrs -}}
                {{- if hasPrefix $cidr $allowed -}}
                    {{- $cidr_valid = true -}}
                {{- end -}}
                {{- end -}}
                {{- if not $cidr_valid -}}
                {{- $errors = append $errors (printf "SG %s: unauthorized CIDR %s" $name $cidr) -}}
                {{- $valid = false -}}
                {{- end -}}
            {{- end -}}

            {{- /* Check ports */ -}}
            {{- range $port := $rules.forbidden_ports -}}
                {{- if and (le $rule.from_port (int $port)) (ge $rule.to_port (int $port)) -}}
                {{- $errors = append $errors (printf "SG %s: forbidden port %s" $name $port) -}}
                {{- $valid = false -}}
                {{- end -}}
            {{- end -}}
            {{- end -}}
        {{- end -}}
        {{- end -}}

        {{- $valid -}}
    deny_message: |
        Network policy violations:
        {{- range $error := $errors }}
        - {{ $error }}
        {{- end }}

Cost Control Policy

# cost-control-policy.yml
pre_plan:
    - name: "Cost Control Policy"
    description: "Enforce cost controls across resources"
    condition: |
        {{- /* Initialize */ -}}
        {{- $valid := true -}}
        {{- $errors := list -}}

        {{- /* Cost limits by environment */ -}}
        {{- $limits := dict -}}
        {{- $_ := set $limits "development" (dict
            "max_instance_size" "t3.medium"
            "max_volume_size" 100
            "max_retention_days" 7
        ) -}}
        {{- $_ := set $limits "staging" (dict
            "max_instance_size" "t3.large"
            "max_volume_size" 500
            "max_retention_days" 14
        ) -}}
        {{- $_ := set $limits "production" (dict
            "max_instance_size" "t3.2xlarge"
            "max_volume_size" 1000
            "max_retention_days" 30
        ) -}}

        {{- /* Resource size mappings */ -}}
        {{- $instance_sizes := list "t3.micro" "t3.small" "t3.medium" "t3.large" "t3.xlarge" "t3.2xlarge" -}}

        {{- /* Process resources */ -}}
        {{- range $name, $resource := .resource -}}
        {{- $env := index $resource.tags "Environment" | default "development" | toLower -}}
        {{- $env_limits := index $limits $env -}}

        {{- /* EC2 instance checks */ -}}
        {{- if eq $resource.type "aws_instance" -}}
            {{- $size_index := -1 -}}
            {{- $max_size_index := -1 -}}
            {{- range $i, $size := $instance_sizes -}}
            {{- if eq $resource.instance_type $size -}}
                {{- $size_index = $i -}}
            {{- end -}}
            {{- if eq $env_limits.max_instance_size $size -}}
                {{- $max_size_index = $i -}}
            {{- end -}}
            {{- end -}}
            {{- if gt $size_index $max_size_index -}}
            {{- $errors = append $errors (printf "Instance %s: size %s exceeds limit %s for %s" 
                $name $resource.instance_type $env_limits.max_instance_size $env) -}}
            {{- $valid = false -}}
            {{- end -}}
        {{- end -}}

        {{- /* EBS volume checks */ -}}
        {{- if eq $resource.type "aws_ebs_volume" -}}
            {{- if gt $resource.size $env_limits.max_volume_size -}}
            {{- $errors = append $errors (printf "Volume %s: size %dGB exceeds limit %dGB for %s" 
                $name $resource.size $env_limits.max_volume_size $env) -}}
            {{- $valid = false -}}
            {{- end -}}
        {{- end -}}
        {{- end -}}

        {{- $valid -}}
    deny_message: |
        Cost policy violations:
        {{- range $error := $errors }}
        - {{ $error }}
        {{- end }}