AWS CloudFormation Guard Policies Complete Guide
Write and deploy CloudFormation Guard rules for infrastructure validation, security enforcement, and compliance checking in your AWS deployments.
๐ Prerequisites
- Strong experience with Infrastructure as Code (CloudFormation or Terraform).
- Proficiency with YAML syntax and structure.
- Solid understanding of CI/CD concepts and tooling (e.g., GitHub Actions, Jenkins).
- Familiarity with the command line and installing open-source tools.
๐ก From Detective to Preventive: The "Shift-Left" Revolution
Traditional compliance tools often act as detective controls, finding issues after they've been deployed. CloudFormation Guard enables preventive controls by validating your IaC templates before deployment. This "shift-left" approach embeds policy enforcement directly into your development workflow, catching violations early and preventing non-compliant infrastructure from ever being created.
What You'll Learn
๐ท๏ธ Topics Covered
The Power of Preventive Controls
Understanding the difference between preventive and detective controls is key to building a mature cloud governance strategy. While both are essential, preventive controls offer significant advantages in speed and security.
Detective Controls (e.g., AWS Config)
- Scans deployed resources.
- Finds non-compliance post-deployment.
- Requires remediation actions to fix issues.
- The security gap exists until remediation occurs.
- Answers: "Is my deployed environment compliant?"
Preventive Controls (e.g., CFN Guard)
- Scans Infrastructure as Code templates.
- Blocks non-compliance pre-deployment.
- Prevents issues from ever being created.
- No security gap; the build/deployment fails.
- Answers: "Is my planned environment compliant?"
Writing Advanced Guard Rules
CloudFormation Guard uses a declarative, domain-specific language that is easy to read but powerful enough to express complex compliance logic. Let's explore some advanced rule patterns.
Example 1: Comprehensive S3 Bucket Security
This rule ensures that all S3 buckets follow security best practices: encryption, blocked public access, and versioning must all be enabled.
๐ก๏ธ Guard: S3 Security Best Practices Rule
#
# Rule: S3_BUCKET_SECURE_CONFIGURATION
#
let s3_buckets = Resources.*[ Type == 'AWS::S3::Bucket' ]
rule S3_BUCKET_SECURE_CONFIGURATION when %s3_buckets !empty {
%s3_buckets.Properties {
# Check 1: Server-Side Encryption must be enabled
BucketEncryption.ServerSideEncryptionConfiguration[*] {
ServerSideEncryptionByDefault.SSEAlgorithm == "AES256" or
ServerSideEncryptionByDefault.SSEAlgorithm == "aws:kms"
} << Must have AES256 or KMS encryption enabled.
# Check 2: Block Public Access configuration must be set
PublicAccessBlockConfiguration {
BlockPublicAcls == true
BlockPublicPolicy == true
IgnorePublicAcls == true
RestrictPublicBuckets == true
} << Public Access must be blocked on all four settings.
# Check 3: Versioning must be enabled
VersioningConfiguration.Status == "Enabled" << Versioning must be enabled for data protection.
}
} Example 2: Enforce Strict Security Group Ingress Rules
A common enterprise requirement is to deny unrestricted ingress (`0.0.0.0/0`) and ensure that SSH (port 22) is only open to a specific bastion host IP. This rule uses variables (`let`) and complex queries.
๐ Guard: Secure Security Group Ingress Rule
let security_groups = Resources.*[ Type == 'AWS::EC2::SecurityGroup' ]
# Define an allowed CIDR for SSH access
let allowed_ssh_cidr = '10.10.0.5/32' # Bastion Host IP
rule SECURE_INGRESS_RULES when %security_groups !empty {
%security_groups.Properties.SecurityGroupIngress[*] {
# Check 1: Deny unrestricted ingress for any protocol
CidrIp == "0.0.0.0/0" << Unrestricted ingress (0.0.0.0/0) is not allowed.
# Check 2: Handle specific rules for port 22 (SSH)
when ToPort == 22 and FromPort == 22 {
# SSH can only be from the allowed bastion host CIDR
CidrIp == %allowed_ssh_cidr << SSH access is only permitted from the bastion host.
}
}
} Test-Driven Development (TDD) for Rules
How do you know your rules work correctly without deploying infrastructure? CFN Guard includes a powerful test framework. You write test cases with sample IaC snippets and expected outcomes (`PASS` or `FAIL`).
Example: Testing the Security Group Rule
This test file validates our `SECURE_INGRESS_RULES` rule with both a compliant and a non-compliant Security Group template.
๐งช YAML: CFN Guard Test Definition
rule-file: ./rules/security_group_rules.guard
tests:
- name: Test_Compliant_SG
input:
Resources:
MyWebAppSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow SSH from bastion and HTTPS"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.10.0.5/32 # Compliant SSH
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 10.20.0.0/16 # Compliant HTTPS
expectations:
rules:
SECURE_INGRESS_RULES: PASS
- name: Test_Non_Compliant_SG_Unrestricted_Ingress
input:
Resources:
BadSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Non-compliant unrestricted ingress"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0 # VIOLATION: Unrestricted ingress
expectations:
rules:
SECURE_INGRESS_RULES: FAIL
- name: Test_Non_Compliant_SG_Unrestricted_SSH
input:
Resources:
AnotherBadSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Non-compliant unrestricted SSH"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 172.16.0.0/16 # VIOLATION: SSH from wrong CIDR
expectations:
rules:
SECURE_INGRESS_RULES: FAIL To run these tests, you simply execute: `cfn-guard test --test-data my_test_file.yaml`
Integrating Guard into a CI/CD Pipeline
The ultimate goal of preventive controls is to fail a build or deployment *before* insecure infrastructure is created. This is accomplished by adding a CFN Guard validation step to your CI/CD pipeline.
๐ YAML: GitHub Actions CI/CD Pipeline with CFN Guard
name: IaC Validation Pipeline
on:
pull_request:
branches: [ main ]
paths:
- 'templates/**'
- 'rules/**'
jobs:
validate-iac:
name: Validate Infrastructure as Code
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install AWS CloudFormation Guard
run: |
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh
echo "/home/runner/.guard/bin" >> $GITHUB_PATH
- name: Run CFN Guard Validation
# This step will fail the pipeline if any rule check fails.
# -d points to the data templates, -r to the rule files.
run: |
cfn-guard validate --data templates/ --rules rules/ --show-summary fail
Managing Rule Sets at Enterprise Scale
For a large organization, managing rules requires a structured approach. A single, monolithic rule file is not scalable or maintainable.
๐ฆ Central Git Repository
Store all Guard rules in a dedicated, version-controlled repository, making it the single source of truth for policies.
๐ Logical Grouping
Organize rules into directories by compliance domain (e.g., `/security`, `/cost`, `/tagging`, `/networking`).
๐ Rule Layering
Apply a base set of mandatory rules to all pipelines, and allow teams to layer on additional, more specific rules for their applications.
๐ท๏ธ Versioning
Use Git tags or branches to version your rule sets. CI/CD pipelines can then pull specific, stable versions of the rules for validation.
๐ CFN Guard Advanced Best Practices
- Integrate as Early as Possible: Add CFN Guard to pre-commit hooks and Pull Request checks to give developers the fastest feedback.
- Write Granular Rules: Avoid creating large, monolithic rules. Break down complex policies into smaller, single-purpose rules for better clarity and easier testing.
- Test Your Rules, Not Just Your Templates: Use `cfn-guard test` extensively to validate that your rules behave exactly as you expect before enforcing them.
- Provide Meaningful Error Messages: Use custom messages ('<< ... >>') in your rules to give developers clear, actionable feedback on why their template failed validation.
- Start with High-Impact Rules: Begin by implementing rules that address your most critical security risks, such as public access and encryption.
- Version Control Everything: Treat your rules as code. They should be versioned, reviewed, and managed in a central repository just like your application code.