AWS Policy Implementation
Comprehensive guide to implementing policies for AWS resources using CloudFormation Guard and OPA
What You'll Learn
๐ Prerequisites
- AWS account with appropriate permissions
- AWS CLI configured
- Basic understanding of CloudFormation or Terraform
- Familiarity with JSON/YAML
- Read: What is Policy-as-Code?
๐ฏ What You'll Learn
- Setting up CloudFormation Guard for policy validation
- Writing Guard rules for common AWS security patterns
- Using OPA with Conftest for Terraform and CloudFormation
- Integrating policies into CI/CD pipelines
- Runtime compliance monitoring with AWS Config
- Best practices for AWS policy governance
๐ท๏ธ Topics Covered
AWS CloudFormation Policy Validation: Complete Implementation Guide
AWS provides multiple tools and services for implementing policy-as-code across your cloud infrastructure. This guide covers the most effective approaches for ensuring your AWS resources meet security, compliance, and operational standards through automated policy enforcement.
๐ก๏ธ CloudFormation Guard
AWS's native policy validation tool for CloudFormation templates and configuration files
๐ Open Policy Agent (OPA)
Versatile policy engine that works with any AWS configuration format
โ๏ธ AWS Config
Runtime monitoring and compliance assessment of AWS resources
CloudFormation Guard Rules Examples: Security Scanning Tutorial
CloudFormation Guard is AWS's purpose-built tool for validating CloudFormation templates and AWS configuration. It uses a domain-specific language that's designed to be readable and maintainable.
Installation and Setup
Install CloudFormation Guard
# Install via Homebrew (macOS/Linux)
brew install aws/tap/cfn-guard
# Install via Cargo (Rust)
cargo install cfn-guard
# Download binary directly
curl -LO https://github.com/aws-cloudformation/cloudformation-guard/releases/latest/download/cfn-guard-v3-ubuntu-latest.tar.gz
tar xf cfn-guard-v3-ubuntu-latest.tar.gz
# Verify installation
cfn-guard --versionYour First Guard Rule
Let's create a rule that ensures all S3 buckets have encryption enabled:
s3-encryption.guard
# S3 bucket encryption rule
rule s3_bucket_encryption_enabled {
# Select all S3 bucket resources
AWS::S3::Bucket {
# Ensure encryption configuration exists
Properties {
BucketEncryption exists
BucketEncryption {
ServerSideEncryptionConfiguration exists
ServerSideEncryptionConfiguration[*] {
ServerSideEncryptionByDefault exists
ServerSideEncryptionByDefault {
SSEAlgorithm in ["AES256", "aws:kms"]
}
}
}
}
}
}
# Custom violation message
rule s3_bucket_encryption_enabled {
AWS::S3::Bucket {
Properties {
BucketEncryption exists
<<
VIOLATION: S3 bucket must have server-side encryption enabled
FIX: Add BucketEncryption property with ServerSideEncryptionConfiguration
>>
}
}
}Testing Your Rules
Create a sample CloudFormation template to test against:
test-template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Test S3 bucket for Guard validation'
Resources:
# Non-compliant bucket (will fail)
BadBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-test-bucket-bad
# Compliant bucket (will pass)
GoodBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-test-bucket-good
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256Run Guard Validation
# Validate template against rules
cfn-guard validate --rules s3-encryption.guard --data test-template.yaml
# Output will show:
# test-template.yaml Status = FAIL
# FAILED rules
# s3-encryption.guard/s3_bucket_encryption_enabled FAIL
# --
# Rule = s3_bucket_encryption_enabled, Status = FAIL
# Violation: S3 bucket must have server-side encryption enabledAdvanced Guard Patterns
๐ง 1. EC2 Instance Security Groups
ec2-security.guard
# Ensure EC2 instances don't allow SSH from anywhere
rule ec2_no_ssh_from_anywhere {
AWS::EC2::SecurityGroup {
Properties {
SecurityGroupIngress[*] {
when IpProtocol == "tcp" {
when FromPort <= 22 {
when ToPort >= 22 {
CidrIp != "0.0.0.0/0"
CidrIpv6 != "::/0"
}
}
}
}
}
}
}๐ง 2. RDS Database Encryption
rds-encryption.guard
# Ensure RDS instances have encryption enabled
rule rds_encryption_enabled {
AWS::RDS::DBInstance {
Properties {
StorageEncrypted == true
<<
VIOLATION: RDS instance must have storage encryption enabled
FIX: Set StorageEncrypted to true
>>
}
}
}
# Ensure RDS instances are not publicly accessible
rule rds_not_public {
AWS::RDS::DBInstance {
Properties {
PubliclyAccessible == false
<<
VIOLATION: RDS instance must not be publicly accessible
FIX: Set PubliclyAccessible to false
>>
}
}
}๐ง 3. Lambda Function Configuration
lambda-security.guard
# Ensure Lambda functions have proper timeout and memory limits
rule lambda_timeout_limit {
AWS::Lambda::Function {
Properties {
Timeout <= 300
<<
VIOLATION: Lambda timeout must not exceed 300 seconds
FIX: Set Timeout to 300 or less
>>
}
}
}
# Ensure Lambda functions use supported runtime
rule lambda_supported_runtime {
AWS::Lambda::Function {
Properties {
Runtime in [
"python3.9", "python3.10", "python3.11",
"nodejs18.x", "nodejs20.x",
"java11", "java17",
"dotnet6", "dotnet8"
]
<<
VIOLATION: Lambda must use a supported runtime version
FIX: Update Runtime to a supported version
>>
}
}
}AWS Resource Policy Enforcement: OPA Conftest Integration
Open Policy Agent with Conftest provides more flexibility for complex policies and works excellently with Terraform configurations and other infrastructure tools.
Setup Conftest
Install Conftest
# Install via Homebrew
brew install conftest
# Install via Go
go install github.com/open-policy-agent/conftest@latest
# Download binary
curl -L https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_linux_x86_64.tar.gz | tar xz
sudo mv conftest /usr/local/bin
# Verify installation
conftest --versionTerraform AWS Policies with OPA
policy/aws_s3.rego
package aws.s3
import future.keywords.in
# METADATA
# title: S3 Security Policies
# description: Comprehensive S3 bucket security validation
# authors:
# - PolicyAsCode
# Deny S3 buckets without encryption
deny[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_s3_bucket"
not has_encryption(resource)
msg := sprintf("S3 bucket '%s' must have server-side encryption enabled", [resource.name])
}
# Deny S3 buckets with public read access
deny[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_s3_bucket_public_access_block"
values := resource.values
any([
values.block_public_acls == false,
values.block_public_policy == false,
values.ignore_public_acls == false,
values.restrict_public_buckets == false
])
msg := sprintf("S3 bucket '%s' must block all public access", [values.bucket])
}
# Deny S3 buckets without versioning
deny[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_s3_bucket_versioning"
resource.values.versioning_configuration[_].status != "Enabled"
msg := sprintf("S3 bucket versioning must be enabled for bucket '%s'", [resource.values.bucket])
}
# Helper function to check encryption
has_encryption(resource) {
encryption := input.planned_values.root_module.resources[_]
encryption.type == "aws_s3_bucket_server_side_encryption_configuration"
encryption.values.bucket == resource.values.id
}
# Warn about S3 buckets without lifecycle policies
warn[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_s3_bucket"
not has_lifecycle_policy(resource)
msg := sprintf("Consider adding lifecycle policy to S3 bucket '%s' for cost optimization", [resource.name])
}
# Helper function to check lifecycle policy
has_lifecycle_policy(resource) {
lifecycle := input.planned_values.root_module.resources[_]
lifecycle.type == "aws_s3_bucket_lifecycle_configuration"
lifecycle.values.bucket == resource.values.id
}EC2 Security Policies
policy/aws_ec2.rego
package aws.ec2
import future.keywords.in
# Deny EC2 instances without encryption
deny[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_instance"
not all_ebs_encrypted(resource)
msg := sprintf("EC2 instance '%s' must have all EBS volumes encrypted", [resource.name])
}
# Deny security groups with overly permissive ingress
deny[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_security_group"
rule := resource.values.ingress[_]
has_wide_cidr(rule)
has_sensitive_port(rule)
msg := sprintf("Security group '%s' allows %s access from 0.0.0.0/0 on port %d",
[resource.name, rule.protocol, rule.from_port])
}
# Deny instances without IMDSv2
deny[msg] {
resource := input.planned_values.root_module.resources[_]
resource.type == "aws_instance"
metadata := resource.values.metadata_options[_]
metadata.http_tokens != "required"
msg := sprintf("EC2 instance '%s' must require IMDSv2 (set http_tokens to 'required')", [resource.name])
}
# Check if all EBS volumes are encrypted
all_ebs_encrypted(instance) {
# Check root block device
instance.values.root_block_device[_].encrypted == true
# Check additional EBS volumes
all_additional_encrypted(instance)
}
all_additional_encrypted(instance) {
count(instance.values.ebs_block_device) == 0
} {
instance.values.ebs_block_device[_].encrypted == true
}
# Check for wide CIDR blocks
has_wide_cidr(rule) {
rule.cidr_blocks[_] in ["0.0.0.0/0", "::/0"]
}
# Check for sensitive ports
has_sensitive_port(rule) {
sensitive_ports := [22, 3389, 1433, 3306, 5432, 6379, 27017]
rule.from_port <= sensitive_ports[_]
rule.to_port >= sensitive_ports[_]
}Testing Terraform Configurations
Generate and Test Terraform Plan
# Generate Terraform plan in JSON format
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
# Test with Conftest
conftest verify --policy policy/ tfplan.json
# Run specific policy package
conftest verify --policy policy/ --namespace aws.s3 tfplan.json
# Generate detailed report
conftest verify --policy policy/ --output json tfplan.json > policy-results.jsonAWS Config Rules with Policy as Code: Compliance Monitoring
AWS Config provides continuous monitoring of your AWS resources against policy rules, enabling you to detect configuration drift and non-compliance in real-time.
Setting Up AWS Config Rules
config-rules.yaml
# CloudFormation template for AWS Config rules
AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS Config rules for policy compliance'
Resources:
# S3 bucket encryption rule
S3BucketSSLRequestsOnlyRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: s3-bucket-ssl-requests-only
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_SSL_REQUESTS_ONLY
DependsOn: ConfigurationRecorder
# RDS encryption rule
RDSStorageEncryptedRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: rds-storage-encrypted
Source:
Owner: AWS
SourceIdentifier: RDS_STORAGE_ENCRYPTED
DependsOn: ConfigurationRecorder
# EC2 security group rule
EC2SecurityGroupAttachedToENIRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: ec2-security-group-attached-to-eni
Source:
Owner: AWS
SourceIdentifier: EC2_SECURITY_GROUP_ATTACHED_TO_ENI
DependsOn: ConfigurationRecorder
# Custom rule using Guard
CustomS3EncryptionRule:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: custom-s3-encryption-check
Source:
Owner: AWS
SourceIdentifier: CLOUDFORMATION_GUARD_RULE
SourceDetails:
- EventSource: aws.config
MessageType: ConfigurationItemChangeNotification
InputParameters: |
{
"guard-rule": "rule s3_encrypted { AWS::S3::Bucket { Properties { BucketEncryption exists } } }"
}Custom Config Rules with Lambda
custom-config-rule.py
import json
import boto3
from datetime import datetime
def lambda_handler(event, context):
"""
Custom AWS Config rule to check S3 bucket encryption
"""
# Extract configuration item from event
config_item = event['configurationItem']
# Check if this is an S3 bucket
if config_item['resourceType'] != 'AWS::S3::Bucket':
return build_evaluation(
config_item['resourceId'],
'NOT_APPLICABLE',
'Resource is not an S3 bucket'
)
# Check encryption configuration
compliance_type = 'NON_COMPLIANT'
annotation = 'S3 bucket does not have encryption enabled'
if is_bucket_encrypted(config_item):
compliance_type = 'COMPLIANT'
annotation = 'S3 bucket has encryption enabled'
# Build evaluation result
evaluation = build_evaluation(
config_item['resourceId'],
compliance_type,
annotation
)
# Send evaluation to AWS Config
config_client = boto3.client('config')
config_client.put_evaluations(
Evaluations=[evaluation],
ResultToken=event['resultToken']
)
return {
'statusCode': 200,
'body': json.dumps('Config rule evaluation completed')
}
def is_bucket_encrypted(config_item):
"""Check if S3 bucket has encryption enabled"""
configuration = config_item.get('configuration', {})
# Check for bucket encryption configuration
bucket_encryption = configuration.get('bucketEncryption')
if not bucket_encryption:
return False
# Verify encryption algorithm is present
sse_config = bucket_encryption.get('serverSideEncryptionConfiguration', [])
for rule in sse_config:
if rule.get('serverSideEncryptionByDefault', {}).get('sseAlgorithm'):
return True
return False
def build_evaluation(resource_id, compliance_type, annotation):
"""Build Config evaluation result"""
return {
'ComplianceResourceType': 'AWS::S3::Bucket',
'ComplianceResourceId': resource_id,
'ComplianceType': compliance_type,
'Annotation': annotation,
'OrderingTimestamp': datetime.utcnow()
}CloudFormation Security Scanning: GitHub Actions Integration
Integrating AWS policy validation into your CI/CD pipelines ensures that policy violations are caught early, before resources are deployed to your AWS environment.
GitHub Actions Pipeline
.github/workflows/aws-policy-check.yml
name: AWS Policy Validation
on:
pull_request:
paths:
- '**.tf'
- '**.yaml'
- '**.yml'
- 'policy/**'
env:
AWS_REGION: us-east-1
jobs:
policy-validation:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Setup CloudFormation Guard
run: |
curl -LO https://github.com/aws-cloudformation/cloudformation-guard/releases/latest/download/cfn-guard-v3-ubuntu-latest.tar.gz
tar xf cfn-guard-v3-ubuntu-latest.tar.gz
chmod +x cfn-guard
sudo mv cfn-guard /usr/local/bin/
- name: Setup Conftest
run: |
curl -L https://github.com/open-policy-agent/conftest/releases/latest/download/conftest_linux_x86_64.tar.gz | tar xz
chmod +x conftest
sudo mv conftest /usr/local/bin/
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Terraform Init
run: terraform init
- name: Terraform Plan
run: |
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
- name: Validate with Conftest
run: |
echo "๐ Running Conftest validation..."
conftest verify --policy policy/ tfplan.json
- name: Validate CloudFormation templates
run: |
echo "๐ก๏ธ Running CloudFormation Guard validation..."
find . -name "*.yaml" -o -name "*.yml" | grep -E "(cloudformation|cfn)" | while read template; do
echo "Validating $template"
cfn-guard validate --rules guard-rules/ --data "$template"
done
- name: Policy Compliance Report
if: always()
run: |
echo "## ๐ Policy Compliance Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Conftest results
echo "### Conftest Results" >> $GITHUB_STEP_SUMMARY
if conftest verify --policy policy/ tfplan.json --output json > conftest-results.json; then
echo "โ
All Conftest policies passed" >> $GITHUB_STEP_SUMMARY
else
echo "โ Conftest policy violations found" >> $GITHUB_STEP_SUMMARY
cat conftest-results.json >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Resources Validated" >> $GITHUB_STEP_SUMMARY
jq -r '.planned_values.root_module.resources[].type' tfplan.json | sort | uniq -c >> $GITHUB_STEP_SUMMARYAWS Policy Best Practices
Start with High-Impact Rules
Focus on encryption at rest/transit, public access prevention, IAM least privilege, and network security first.
Organize Policies by Domain
Structure policies into security, compliance, cost, and operational domains for better management.
Test Thoroughly
Create positive and negative test cases, use realistic samples, and validate helpful error messages.
Gradual Rollout
Start with warning-only mode, enable in dev first, monitor for false positives before production.
Documentation
Provide clear descriptions, compliant examples, remediation guidance, and exception processes.
Performance Optimization
Use efficient patterns, avoid complex conditions, cache results, and monitor execution time.
Troubleshooting Common Issues
๐ง CloudFormation Guard Issues
โ Rule not matching expected resources
- Check resource type spelling (case-sensitive)
- Verify property path structure
- Use
cfn-guard parse-treeto debug - Test with minimal template first
โ Complex conditions not working
- Break complex rules into smaller parts
- Use intermediate variables
- Check operator precedence
- Test each condition separately
๐ง OPA/Conftest Issues
โ Terraform plan JSON structure changes
- Use defensive programming with
has_key() - Create helper functions for common patterns
- Test against multiple Terraform versions
- Use generic selectors where possible
โ Undefined value errors
- Check existence before accessing values
- Use helper functions for validation
- Implement null-safe operations
- Test with edge cases and missing data
Example: Safe Value Access Pattern
# Bad - will fail if encryption doesn't exist
deny[msg] {
bucket := input.resource_changes[_]
bucket.type == "aws_s3_bucket"
bucket.change.after.server_side_encryption_configuration == null
}
# Good - check existence first
deny[msg] {
bucket := input.resource_changes[_]
bucket.type == "aws_s3_bucket"
not has_encryption(bucket)
}
has_encryption(bucket) {
bucket.change.after.server_side_encryption_configuration
bucket.change.after.server_side_encryption_configuration != null
}๐ Congratulations!
AWS Policy Governance Mastery
You now have the knowledge to implement comprehensive AWS policy governance using:
CloudFormation Guard
Master AWS's native validation tool for CloudFormation templates and configuration files.
OPA/Conftest Integration
Implement flexible policy enforcement for Terraform and complex multi-tool environments.
AWS Config Monitoring
Set up runtime compliance monitoring with continuous drift detection and remediation.
CI/CD Integration
Automate policy checks in your deployment pipelines for early violation detection.
Enterprise Best Practices
Apply proven patterns for enterprise-scale AWS governance and policy management.
Troubleshooting Skills
Debug and resolve common policy issues with confidence and systematic approaches.