Enforce Cloud Rules with Pulumi Policy‑as‑Code: A Hands‑On Guide
Learn how Pulumi’s Policy‑as‑Code lets teams enforce infrastructure rules with OPA, see a step‑by‑step example, weigh trade‑offs, and decide when to adopt it.
22 Jul 2026, 02:57 UTC

Concrete Problem: Uncontrolled Cloud Resources
In many teams, infrastructure code is pushed quickly, and a single mis‑configured bucket can expose data or incur extra cost. A common rule is “every S3 bucket must have versioning enabled.” Without a hard check, developers may forget or override this setting, leading to audit failures.
Thesis: Policy‑as‑Code Lets You Codify Rules with OPA
Pulumi’s Policy‑as‑Code feature integrates Open Policy Agent (OPA) into the deployment pipeline. Policies are written in the same language as your stack (TypeScript, Python, Go, or C#), attached to a stack or organization, and evaluated during the preview phase. If a rule fails, the preview aborts, preventing a bad change from ever hitting the cloud.
How Pulumi Policy‑as‑Code Works
- Policy language: OPA uses a declarative rule language. Pulumi wraps these rules in a small TypeScript/Go wrapper that exposes the stack’s resource graph.
- Evaluation timing: Policies run during
pulumi preview, before any provider calls are made. This keeps the cost local and ensures no drift. - Result aggregation: All attached policies are executed; failures are collected into a single error message that aborts
pulumi up. - Version control: Policies live alongside infrastructure code, enabling code review, history, and auditability.
Step‑by‑Step Example: Enforce S3 Bucket Versioning
Assume you have a Pulumi project in TypeScript that creates an S3 bucket. Follow these steps to add a policy that blocks any bucket lacking versioning.
- Create the policy
# From the stack directory pulumi policy new --name bucket-versioningThis generates a
policy.tsfile and apolicy.yamlthat registers the policy with the stack. - Edit the policy
import * as pulumi from "@pulumi/pulumi"; import * as aws from "@pulumi/aws"; export function check(ctx: pulumi.Context) { ctx.registerPostProcessor((resource) => { if (resource.type === "aws:s3/bucket:Bucket") { const bucket = resource as aws.s3.Bucket; if (!bucket.versioning?.enabled) { return { severity: "error", message: `Bucket ${bucket.bucket} must have versioning enabled.`, }; } } return undefined; }); }Replace the placeholder logic with the actual property check. The
registerPostProcessorhook inspects each resource after it’s created but before the preview completes. - Attach the policy
# Ensure the policy is listed for the stack pulumi policy listThe output should include
bucket-versioningunder the stack’s policies. - Run a preview with a non‑versioned bucket
# In your stack code, add a bucket without versioning const bucket = new aws.s3.Bucket("test", {}); # Preview pulumi previewDuring preview, Pulumi will invoke the policy. The expected output contains a section like:
Policy failures: - Bucket test must have versioning enabled. Deployment aborted.Because the policy failed,
pulumi upwill not proceed. - Fix the bucket and redeploy
# Enable versioning const bucket = new aws.s3.Bucket("test", { versioning: { enabled: true }, }); # Preview again pulumi previewNow the preview passes the policy, and
pulumi upwill succeed.
Trade‑Offs & Limitations
- Preview‑only enforcement: Policies run only during preview. A mis‑configured rule that always returns
truewill silently allow a forbidden change until the next preview. Always test your policy against a known bad stack. - Performance impact: For large stacks (hundreds of resources), each policy adds a few milliseconds to preview time. In most cases the overhead is negligible, but monitor
pulumi preview --logtostderrto ensure it stays within acceptable limits. - Strictness vs. flexibility: A policy that is too strict can block legitimate changes. Use
--skip-policyonly for emergencies and consider having a “soft” policy that logs warnings instead of errors. - Learning curve: OPA rules can be complex. New contributors may inadvertently write rules that fail all resources. Provide clear documentation and example policies to mitigate this risk.
Actionable Next Steps
- Review your organization’s compliance requirements and identify high‑impact rules (e.g., encryption, tagging, versioning).
- Generate a policy scaffold with
pulumi policy newfor each rule. - Write the rule in the language you’re comfortable with, and test it against a small stack.
- Attach the policy to the stack or organization using
policy.yamlor the Pulumi Console. - Automate policy checks in CI by running
pulumi preview --policy-dir=policiesbefore merging.
By integrating Policy‑as‑Code into your workflow, you gain a first‑class audit trail, enforce governance, and reduce the risk of accidental mis‑configurations—all without incurring extra cloud costs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.