Pulumi CrossGuard: Policy as Code for IaC at Scale

Sources

In the era of hyper-scale cloud deployments, the "security review" is a bottleneck. Waiting for a security architect to manually approve a Terraform plan or a CloudFormation template is an operational failure.

Pulumi CrossGuard shifts this paradigm by implementing Policy as Code (PaC). Instead of a PDF of guidelines, you maintain a version-controlled repository of executable rules that intercept every infrastructure change *before* it hits the cloud provider.

The Technical Mechanics of CrossGuard

CrossGuard does not wrap the cloud provider's API; it hooks into the Pulumi Engine. When you run `pulumi preview` or `pulumi up`, the engine generates a resource graph (the "plan"). Before the engine sends these requests to the provider (AWS, GCP, Azure), it invokes the loaded Policy Packs.

The Enforcement Loop


[ Developer ] --( pulumi up )--> [ Pulumi Engine ]
                                        |
                                        v
                          [ Loaded Policy Packs (CrossGuard) ]
                                        |
           +----------------------------+----------------------------+
           |                            |                            |
  [ Resource Validation ]      [ Stack Validation ]        [ Remediations ]
  (Check individual props)     (Check graph relations)     (Auto-fix violations)
           |                            |                            |
           +----------------------------+----------------------------+
                                        |
                                        v
                          [ Enforcement Level Decision ]
                                        |
           +----------------------------+----------------------------+
           |                            |                            |
    ( Advisory )                ( Mandatory )                ( Remediate )
    Warn & Proceed               Block & Fail                Fix & Proceed
                                        |
                                        v
                          [ Cloud Provider API (AWS/Azure/GCP) ]

Resource Validation vs. Stack Validation

One of the most common mistakes in PaC is using the wrong validation scope. CrossGuard provides two distinct modes.

1. Resource Validation

Resource validation policies run for every single resource registered in the program. They are highly efficient and ideal for "low-hanging fruit" security checks.

Use Case: "Every S3 bucket must have `publicAccessBlock` enabled."

2. Stack Validation

Stack validation runs once per update, after the entire resource graph has been constructed but before the apply phase. This allows the policy to examine relationships between resources.

Use Case: "Any RDS instance tagged as `production` must be associated with a subnet that has no route to the Internet Gateway (IGW)."
FeatureResource ValidationStack Validation
Execution FrequencyPer ResourceOnce per Stack
ContextSingle Resource PropertiesFull Resource Graph
PerformanceFast, O(n)Slower, O(graph)
Primary GoalProperty ComplianceArchitectural Integrity
Best ForEncryption, Tagging, SizingNetwork Isolation, Dependency Checks

Production-Grade Implementation

Below is a complete implementation of a security-focused policy pack. This example enforces encryption for RDS and prevents the creation of public S3 buckets.


import * as aws from "@pulumi/aws";
import { PolicyPack, validateResourceOfType } from "@pulumi/policy";

new PolicyPack("platform-monkey-security-baseline", {
    policies: [
        {
            name: "s3-no-public-read",
            description: "Prevents S3 buckets from having public read access.",
            enforcementLevel: "mandatory",
            validateResource: validateResourceOfType(aws.s3.Bucket, (bucket, args, reportViolation) => {
                if (bucket.acl === "public-read" || bucket.acl === "public-read-write") {
                    reportViolation("S3 bucket cannot have public-read or public-read-write ACLs.");
                }
            }),
        },
        {
            name: "rds-storage-encryption",
            description: "Requires RDS instances to have storage encryption enabled.",
            enforcementLevel: "mandatory",
            validateResource: validateResourceOfType(aws.rds.Instance, (instance, args, reportViolation) => {
                if (!instance.storageEncrypted) {
                    reportViolation("RDS instances must have storage encryption enabled for compliance (SOC2/HIPAA).");
                }
            }),
        },
        {
            name: "vpc-no-igw-in-private-subnets",
            description: "Ensures private subnets do not have a direct route to an Internet Gateway.",
            enforcementLevel: "mandatory",
            validateResource: validateResourceOfType(aws.ec2.Route, (route, args, reportViolation) => {
                if (route.gatewayId && route.gatewayId.includes("igw-") && route.destinationCidrBlock === "0.0.0.0/0") {
                    if (args.resourceTags?.["Tier"] === "Private") {
                        reportViolation("Private subnets cannot have a direct route to an Internet Gateway.");
                    }
                }
            }),
        },
    ],
});

Mapping to Regulatory Frameworks

CrossGuard allows you to map code directly to auditor-friendly controls.

SOC2 (Security & Confidentiality)

HIPAA (Technical Safeguards)

PCI DSS (Network Security)

Production Realities: The "Security Theater"

Implementing Policy as Code is not a silver bullet. In high-growth environments, several patterns emerge that undermine the system:

1. The "Advisory" Trap: Teams often set policies to `advisory` to avoid blocking developers. In practice, this becomes "security theater"—warnings are ignored in CI logs, and non-compliant infrastructure is deployed anyway.

2. The Local Bypass: If policy enforcement is only configured locally (`--policy-pack`), a developer can simply omit the flag. True enforcement requires Pulumi Cloud server-side policy groups.

3. The "Unknown" Value Problem: During `pulumi preview`, some values (like an AWS-generated ID) are unknown. Writing policies that depend on these values requires careful handling of `pulumi.Output` and may result in policies that pass in preview but fail in apply.

4. Operational Toil: As the policy pack grows to 100+ rules, `pulumi preview` times can increase significantly. Modularizing policy packs by "domain" (e.g., `network-policies`, `data-policies`) is essential.

Who Should Skip This

Do not implement a full CrossGuard architecture if:

References & Further Reading