What is CrossGuard?
Why Policy as Code?
The Problem: Security and compliance reviews are manual, slow, and inconsistent. Teams discover policy violations after deployment when the damage is already done.
The Solution: Pulumi CrossGuard lets you define policies in code that automatically validate every resource before it is created, updated, or deleted.
Real Impact: Organizations using CrossGuard catch 99% of policy violations before deployment, reducing security incidents by 85%.
Real-World Analogy
Think of CrossGuard as a building code inspector:
- Policy Packs = The building code book with all regulations
- Resource Validation = Inspecting each room individually
- Stack Validation = Inspecting the entire building as a whole
- Advisory = A warning note that does not block construction
- Mandatory = A stop-work order that blocks construction until fixed
CrossGuard Key Features
Resource Policies
Validate individual resources against rules. Check tags, encryption, network exposure, and instance sizes.
Stack Policies
Validate the entire stack holistically. Ensure cost budgets, resource counts, and cross-resource relationships.
Configurable Rules
Make policies configurable so different teams can adjust thresholds while maintaining the same base rules.
Publish & Share
Publish policy packs to the Pulumi Cloud and enforce them across all stacks in your organization.
Writing Policy Packs
import * as policy from "@pulumi/policy";
new policy.PolicyPack("aws-security", {
policies: [
{
name: "s3-no-public-access",
description: "S3 buckets must not be publicly accessible",
enforcementLevel: "mandatory",
validateResource: policy.validateResourceOfType(
"aws:s3/bucketV2:BucketV2",
(bucket, args, reportViolation) => {
// Policy logic here
}
),
},
{
name: "require-encryption",
description: "All storage resources must use encryption",
enforcementLevel: "mandatory",
validateResource: (args, reportViolation) => {
if (args.type === "aws:ebs/volume:Volume") {
if (!args.props.encrypted) {
reportViolation("EBS volumes must be encrypted");
}
}
},
},
{
name: "cost-control",
description: "EC2 instances must use approved sizes",
enforcementLevel: "advisory",
validateResource: (args, reportViolation) => {
const allowed = ["t3.micro", "t3.small", "t3.medium"];
if (args.type === "aws:ec2/instance:Instance") {
if (!allowed.includes(args.props.instanceType)) {
reportViolation(
`Instance type ${args.props.instanceType} not in approved list`
);
}
}
},
},
],
});
Resource Validation
// Validate tags on all taggable resources
{
name: "require-tags",
description: "All resources must have required tags",
enforcementLevel: "mandatory",
validateResource: (args, reportViolation) => {
const requiredTags = ["Environment", "Team", "CostCenter"];
if (args.props.tags) {
for (const tag of requiredTags) {
if (!args.props.tags[tag]) {
reportViolation(`Missing required tag: ${tag}`);
}
}
}
},
},
// Prevent overly permissive security groups
{
name: "no-open-ssh",
description: "Security groups must not allow SSH from 0.0.0.0/0",
enforcementLevel: "mandatory",
validateResource: (args, reportViolation) => {
if (args.type === "aws:ec2/securityGroup:SecurityGroup") {
const ingress = args.props.ingress || [];
for (const rule of ingress) {
if (rule.fromPort <= 22 && rule.toPort >= 22) {
if ((rule.cidrBlocks || []).includes("0.0.0.0/0")) {
reportViolation("SSH must not be open to the world");
}
}
}
}
},
}
Stack Validation
// Validate the entire stack
{
name: "max-resource-count",
description: "Stack must not exceed 100 resources",
enforcementLevel: "advisory",
validateStack: (args, reportViolation) => {
if (args.resources.length > 100) {
reportViolation(
`Stack has ${args.resources.length} resources (max 100). Consider splitting.`
);
}
},
},
// Ensure every public-facing resource has a WAF
{
name: "require-waf-for-public",
description: "Public ALBs must have WAF protection",
enforcementLevel: "mandatory",
validateStack: (args, reportViolation) => {
const publicAlbs = args.resources.filter(
r => r.type === "aws:lb/loadBalancer:LoadBalancer" && !r.props.internal
);
const wafAssociations = args.resources.filter(
r => r.type === "aws:wafv2/webAclAssociation:WebAclAssociation"
);
if (publicAlbs.length > wafAssociations.length) {
reportViolation("All public ALBs must have WAF associations");
}
},
}
Enforcement Levels
| Level | Behavior | Use Case |
|---|---|---|
| advisory | Warns but allows deployment | New policies, cost optimization hints |
| mandatory | Blocks deployment on violation | Security, compliance, critical rules |
| disabled | Policy is skipped entirely | Temporarily disabling a policy |
Running Policies
# Run preview with a local policy pack
pulumi preview --policy-pack ./policy-pack
# Deploy with policy enforcement
pulumi up --policy-pack ./policy-pack
# Publish a policy pack to Pulumi Cloud
cd policy-pack
pulumi policy publish my-org
# Enable a published policy on a stack
pulumi policy enable my-org/aws-security latest
Quick Reference
CrossGuard Best Practices
- Start with advisory policies and promote to mandatory after validation
- Use resource validation for individual compliance checks
- Use stack validation for cross-resource relationship checks
- Publish policy packs to enforce organization-wide standards
- Version policy packs and test them against sample stacks before publishing
- Make policies configurable to allow team-specific thresholds