Menu

Topic 7 of 8

Policy As Code Basics

Learn Policy As Code Basics for free with explanations, exercises, and a quick test (for Platform Engineer).

Published: January 23, 2026 | Updated: January 23, 2026

Why this matters

Policy as Code (PaC) lets you express security, compliance, and platform standards in code and enforce them automatically on Infrastructure as Code (IaC) changes. As a Platform Engineer you will:

  • Block risky resources (e.g., public storage buckets, open security groups) before they reach cloud.
  • Enforce consistency (tags, regions, naming, encryption) across teams and environments.
  • Shift-left compliance by running policies in local dev, pull requests, and CI/CD.
  • Provide auditable, reviewable policy changes using version control.

Concept explained simply

Policy as Code means you write rules (in a policy language) that inspect IaC plans or manifests and return violations. Pipelines treat violations like test failures and stop the deployment.

Mental model

Think of PaC as a spellchecker for infrastructure. It reads your IaC file, compares it with company rules, and highlights what breaks the rules. No highlights = good to go.

  • Inputs: IaC manifests or plans (Terraform plan JSON, Kubernetes manifests, cloud templates).
  • Policy runtime: A policy engine (commonly OPA/Rego) evaluates rules against the input.
  • Outputs: Messages describing violations. If none, the change passes.
Key terms in practice
  • Policy: A rule, e.g., “All buckets must be private.”
  • Decision: Allow/deny or a list of violation messages.
  • Enforcement point: Where you run policies (pre-commit, PR checks, CI/CD, admission controller).
  • Source of truth: Policies stored in version control with code review.

Worked examples

Example 1: Deny public storage buckets

Goal: Reject any S3-like bucket configured with a public ACL.

Simple Rego sketch:

package policies.buckets

deny[msg] {
  input.kind == "aws_s3_bucket"
  input.acl == "public-read"  # or public-read-write
  msg := sprintf("Bucket %s must not be public", [input.name])
}

Behavior: If a bucket is public, a message appears and the pipeline fails.

Example 2: Require mandatory tags

Goal: Every resource must have owner, cost_center tags.

package policies.tags

required := {"owner", "cost_center"}

missing[tag] {
  tag := required[_]
  not input.tags[tag]
}

deny[msg] {
  count(missing) > 0
  msg := sprintf("Missing required tags: %v", [missing])
}

Behavior: If any tag is missing, it prints which ones.

Example 3: Allowlist regions

Goal: Only specific regions can be used.

package policies.region

allowed := {"us-east-1", "eu-west-1"}

deny[msg] {
  input.region
  not allowed[input.region]
  msg := sprintf("Region %s is not allowed", [input.region])
}

Behavior: Blocks resources using non-approved regions.

Who this is for

  • Platform Engineers designing guardrails for multi-team IaC.
  • DevOps/SREs adding automated checks to CI/CD.
  • Security engineers who want policy automation without slowing delivery.

Prerequisites

  • Basic understanding of Terraform or Kubernetes manifests.
  • Comfort with JSON/YAML and reading structured data.
  • Familiarity with CI pipelines (e.g., PR checks).

Learning path

  1. Understand what inputs your policies will read (Terraform plan JSON or manifest files).
  2. Learn a policy language pattern (deny rules that produce messages).
  3. Write 2–3 baseline guardrails (public exposure, tags, regions).
  4. Run policies locally, then add to PR checks.
  5. Iterate with teams: small, testable policies; fast feedback.
Time estimate
  • Concepts & mental model: 30–45 minutes
  • Examples & exercises: 45–60 minutes
  • Integrating into a demo pipeline: 60–90 minutes

Exercises

Do these locally with any policy runner you prefer, or just reason through the inputs/outputs.

Exercise 1 — Write a deny policy for public buckets

Input (conceptual JSON):

{
  "kind": "aws_s3_bucket",
  "name": "app-assets",
  "acl": "public-read",
  "tags": {"owner": "web", "cost_center": "1001"}
}

Task: Write a policy that denies when acl is public-read or public-read-write. Include a helpful message with the bucket name.

Hint
  • Create a deny rule that fires when acl matches a public value.
  • Return a message string including the resource name.
Expected output

A violation message like: "Bucket app-assets must not be public" and a non-zero exit in CI.

Show solution
package learn.ex1

public_acls := {"public-read", "public-read-write"}

deny[msg] {
  input.kind == "aws_s3_bucket"
  public_acls[input.acl]
  msg := sprintf("Bucket %s must not be public", [input.name])
}

Exercise 2 — Require standard tags

Input (conceptual JSON):

{
  "kind": "any_resource",
  "name": "api",
  "tags": {"owner": "platform"}
}

Task: Deny if tags are missing either owner or cost_center. Output which tags are missing.

Hint
  • Define a set of required keys.
  • Collect missing ones and use them in the message.
Expected output

Message such as: "Missing required tags: {cost_center}" and a failure status.

Show solution
package learn.ex2

required := {"owner", "cost_center"}

missing[tag] {
  tag := required[_]
  not input.tags[tag]
}

deny[msg] {
  count(missing) > 0
  msg := sprintf("Missing required tags: %v", [missing])
}

Self-check checklist

  • I can write a deny rule that returns clear messages.
  • I can read input fields and compare against allowed values.
  • I can collect missing fields and report them in one message.
  • I can reason about policy behavior without running it.

Common mistakes and how to self-check

  • Inverted conditions: You deny compliant resources by mistake. Self-check: Create a passing input and ensure no messages appear.
  • Overly broad rules: Deny everything. Self-check: Add a known-good example and verify it passes.
  • Silent policies: No message means confused teams. Self-check: Always include actionable messages and examples.
  • Enforcement too late: Only in production. Self-check: Run policies in pre-commit and PR checks.
  • No version control: Policies drift between repos. Self-check: Store policies centrally and reuse via modules or shared folders.

Practical projects

Project 1 — Local guardrail pack

  1. Create a policy folder with three rules: public exposure, required tags, allowed regions.
  2. Add sample inputs and run locally to see pass/fail.
  3. Document expected messages in a README snippet.

Project 2 — PR check integration

  1. Add a policy step to your CI that evaluates the IaC plan or manifest files.
  2. Fail the job if any deny messages exist; print them in logs.
  3. Share one screenshot or log excerpt with your team for feedback.

Project 3 — Policy starter kit for teams

  1. Package common rules in a reusable folder or template.
  2. Provide examples and a simple make task to run policies.
  3. Collect 2 team requests and convert them into small, testable policies.

Quick Test

Anyone can take the test for free. Only logged-in users get saved progress.

When you finish, review explanations and revisit exercises if needed.

Mini challenge

Design a single policy that prevents any resource from being created without encryption at rest. It should:

  • Check an encryption flag or algorithm field.
  • Return a message naming the resource and the missing field.
  • Pass if the flag is true and algorithm is non-empty.
Hint

Use deny[msg] with two conditions: not input.encrypted and (not input.kms_key or input.algorithm == "").

Next steps

  • Expand policies to cover network rules (no 0.0.0.0/0 on sensitive ports).
  • Add policy tests with known-good and known-bad inputs.
  • Roll out enforcement gradually: warn in first PRs, then block once stable.

Practice Exercises

2 exercises to complete

Instructions

Given input:

{
  "kind": "aws_s3_bucket",
  "name": "app-assets",
  "acl": "public-read",
  "tags": {"owner": "web", "cost_center": "1001"}
}

Write a policy that denies when acl is public-read or public-read-write. Include the bucket name in the message.

Expected Output
A violation message like: "Bucket app-assets must not be public" and a failing status.

Policy As Code Basics — Quick Test

Test your knowledge with 8 questions. Pass with 70% or higher.

8 questions70% to pass

Have questions about Policy As Code Basics?

AI Assistant

Ask questions about this tool