Pulumi Policy as Code with CrossGuard
Guardrails belong in the deployment path, not in a wiki. Pulumi CrossGuard lets you express organisational rules — encryption, tagging, instance sizing, network exposure — as typed Python that runs on every pulumi preview and blocks non-compliant changes before they reach the cloud. This cluster, part of Pulumi patterns and provider management, covers how policy packs work and links to concrete policies you can adopt.
Problem Framing
Reviews catch some misconfigurations, but humans miss the fifth unencrypted bucket of the day. Policy as code makes the rule executable: it evaluates the resource graph Pulumi is about to apply and returns advisory warnings or hard failures. Because the policies are Python, they live in version control, get unit tested, and evolve with the same workflow as the infrastructure they govern — an extension of the security and compliance basics every team needs.
Prerequisites
- Python 3.9+ with
pulumi>=3.0andpulumi-policyinstalled in a separate policy project - A stack you can preview, and permission to register the policy pack with
--policy-pack - Agreement on which rules are advisory (warn) versus mandatory (fail), so the pack does not block every deploy on day one
# CLI: confirm the policy SDK is importable
python -c "import pulumi_policy; print('policy sdk ok')"
How Policy Packs Work
A policy pack is a small Python program that registers a PolicyPack with one or more ResourceValidationPolicy rules. Each rule receives a resource's type and properties and reports a violation string when the resource breaks the rule.
# __main__.py — a minimal policy pack requiring S3 encryption
# CLI: pulumi preview --policy-pack ./policy
from pulumi_policy import (
PolicyPack, ResourceValidationPolicy, EnforcementLevel, ReportViolation)
def s3_encrypted(args, report: ReportViolation):
if args.resource_type == "aws:s3/bucketV2:BucketV2":
if not args.props.get("serverSideEncryptionConfiguration"):
report("S3 buckets must define server-side encryption.")
PolicyPack(
name="acme-baseline",
enforcement_level=EnforcementLevel.MANDATORY,
policies=[ResourceValidationPolicy(
name="s3-encryption-required",
description="All S3 buckets must be encrypted.",
validate=s3_encrypted)])
Provider note: rules see the same property names Pulumi sends to the provider, so consult the resource schema when matching fields.
Step-by-Step: Enforcing a Baseline
Start advisory, watch what would fail, then promote stable rules to mandatory. Run the pack locally, then wire it into CI so no stack merges without passing.
# CLI: evaluate a stack against the pack, failing on mandatory violations
pulumi preview --policy-pack ./policy --stack prod
Once green locally, register the pack in your pipeline so every preview enforces it. Group related rules (encryption, tagging, public-access) into one pack per baseline so consumers adopt them together.
Verification
Prove the pack both blocks bad resources and passes good ones — a rule that never fails is worthless, and one that always fails is unusable.
# CLI: a compliant stack passes; a deliberately bad one fails
pulumi preview --policy-pack ./policy # expect: no violations
Add unit tests that call each validate function with crafted args and assert the report, exactly as you would unit test a Pulumi program.
Troubleshooting
Rule never triggers — the resource_type string is wrong; print args.resource_type during a preview to get the exact token.
Property is always missing — you are matching the HCL/console name, not Pulumi's camelCase property; check the resource's input schema.
Pack blocks everything — a mandatory rule is too strict or has a bug; drop it to advisory, fix, and re-promote.
FAQ
Is CrossGuard AWS-only?
No. Policies match on resource type, so you can write rules for any Pulumi provider — AWS, GCP, Kubernetes, or a custom dynamic provider.
Can policies auto-remediate?
Resource validation policies report violations; they do not mutate resources. Remediation belongs in the program itself or a separate reconciliation job.
How does this relate to Checkov?
Checkov scans static configuration; CrossGuard evaluates Pulumi's actual planned graph at preview time, so it sees computed values Checkov cannot.
Related
- Writing Pulumi CrossGuard Policies in Python — a hands-on walkthrough of a resource validation policy.
- Enforcing Tagging Policies with Pulumi CrossGuard — a concrete, widely-needed policy you can adopt today.
- Security & Compliance Basics — the broader compliance context policy packs fit into.