Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Sentinel Policy as Code for Terraform

By Kokil Thapa | Last reviewed: August 2026

Sentinel Policy as Code for Terraform allows engineering teams to embed compliance, security, and cost controls directly into their infrastructure provisioning workflow. Rather than relying on manual code reviews or post-deployment audits, Sentinel evaluates Terraform plans against defined rules before any changes reach production. For teams managing critical infrastructure—whether legal-tech portals handling sensitive client data or high-traffic eCommerce platforms—this automated governance layer prevents configuration drift and enforces organizational standards consistently.

How does Sentinel Policy as Code for Terraform integrate with the plan lifecycle?

Understanding where Sentinel fits in your CI/CD pipeline is critical because it operates differently than traditional linting tools. Sentinel does not scan static HCL files; instead, it inspects the structured JSON output of a terraform plan. This distinction matters because many compliance violations only become visible after interpolation, module expansion, and provider API resolution.

terraform planJSON OutputSentinel EnginePolicy EvaluationImport tfplanRule LogicPASSProceed to ApplyFAILBlock + ReportSentinel Evaluation Flow
Sentinel Policy as Code for Terraform evaluates the plan JSON between plan and apply stages, returning pass or fail decisions that gate deployment.

In practice, the evaluation happens in three distinct phases within Terraform Enterprise (TFE) or Terraform Cloud (TFC):

  1. Plan Generation: Terraform produces a binary plan file and its JSON representation. Sentinel never sees raw HCL; it only sees the resolved state graph.
  2. Policy Evaluation: Sentinel imports the tfplan/v2 module, traverses resource changes, and executes policy logic. Each policy returns true (pass) or false (fail).
  3. Enforcement Decision: Based on the aggregate result and each policy’s enforcement level (hard-mandatory, soft-mandatory, or advisory), TFE/TFC either allows the run to proceed to apply or halts it immediately.

A common mistake I’ve seen on real client projects is assuming Sentinel can validate secrets or runtime behavior. It cannot. Sentinel only sees what the plan contains. If a value is marked sensitive in the plan JSON, Sentinel receives a redacted placeholder, not the actual value. Design your policies around observable attributes: tags, instance types, CIDR blocks, IAM policy documents, and resource metadata.

What are the essential Sentinel policies every Terraform team should implement first?

When introducing Sentinel Policy as Code for Terraform to an organization, start with high-value, low-friction policies that catch expensive or dangerous mistakes without generating excessive noise. Based on production experience across multiple infrastructure stacks, these four categories deliver immediate ROI:

Mandatory Tagging Enforcement

Untagged resources create cost allocation nightmares and audit failures. This policy ensures every taggable resource includes required keys like Environment, Owner, and Project:

import "tfplan/v2" as tfplan

required_tags = ["Environment", "Owner", "Project"]

taggable_resources = filter tfplan.resource_changes as _, rc {
    rc.mode == "managed" and
    rc.change.actions contains "create" or rc.change.actions contains "update"
}

violations = filter taggable_resources as _, rc {
    tags = rc.change.after.tags else {}
    missing = filter required_tags as tag {
        not tags contains tag
    }
    length(missing) > 0
}

main = rule {
    length(violations) is 0
}

Prohibited Instance Types

Prevent developers from accidentally provisioning expensive GPU instances or deprecated machine types in non-production environments. This is especially relevant for Nepal-based teams managing multi-region deployments where cost sensitivity is high:

import "tfplan/v2" as tfplan

prohibited_types = ["p3.2xlarge", "p4d.24xlarge", "g5.48xlarge"]

all_instances = filter tfplan.resource_changes as _, rc {
    rc.type == "aws_instance" and
    (rc.change.actions contains "create" or rc.change.actions contains "update")
}

violations = filter all_instances as _, rc {
    rc.change.after.instance_type in prohibited_types
}

main = rule {
    length(violations) is 0
}

Public S3 Bucket Prevention

Data exposure through misconfigured storage is a persistent risk. This policy blocks any S3 bucket creation or modification that enables public ACLs or bucket policies:

import "tfplan/v2" as tfplan

public_acls = ["public-read", "public-read-write"]

bucket_changes = filter tfplan.resource_changes as _, rc {
    rc.type == "aws_s3_bucket" and
    (rc.change.actions contains "create" or rc.change.actions contains "update")
}

acl_violations = filter bucket_changes as _, rc {
    acl = rc.change.after.acl else ""
    acl in public_acls
}

main = rule {
    length(acl_violations) is 0
}

Network Security Group Restrictions

Open ingress rules (0.0.0.0/0 on sensitive ports) are a frequent source of breaches. Restrict SSH, RDP, and database ports to known CIDR ranges:

import "tfplan/v2" as tfplan

restricted_ports = [22, 3389, 3306, 5432]
allowed_cidrs = ["10.0.0.0/8", "172.16.0.0/12"]

sg_ingress = filter tfplan.resource_changes as _, rc {
    rc.type == "aws_security_group_rule" and
    rc.change.after.type == "ingress" and
    (rc.change.actions contains "create" or rc.change.actions contains "update")
}

violations = filter sg_ingress as _, rc {
    port = rc.change.after.from_port
    cidr = rc.change.after.cidr_blocks else []
    (port in restricted_ports) and
    any cidr as c { c == "0.0.0.0/0" }
}

main = rule {
    length(violations) is 0
}

For teams also managing application-level security alongside infrastructure, understanding server hardening practices complements these Sentinel policies by covering runtime configurations that IaC cannot address.

How do you structure and test Sentinel policies locally before pushing to production?

Writing Sentinel policies without local testing is like deploying untested application code. The Sentinel CLI provides a complete local development loop that mirrors TFE/TFC behavior. Here is the workflow I use on every engagement:

Write Policypolicy.sentinelGenerate Mock Planterraform plan -out=tfplansentinel testRun Unit TestsTest Casespass.json / fail.json mocksRefine PolicyFix False PositivesLocal Development Loop
Sentinel Policy as Code for Terraform local testing workflow: write policy, generate mock plans, run sentinel test, iterate on failures before committing to VCS.

Step 1: Install the Sentinel CLI

Download the latest Sentinel CLI (v0.26.x as of 2026) from HashiCorp’s releases page. Verify installation:

sentinel version
# Sentinel v0.26.3 (2026)

Step 2: Create Test Fixtures

Never test against live infrastructure. Generate sanitized plan JSON from representative Terraform configurations:

# Generate binary plan
terraform plan -out=tfplan.binary

# Convert to JSON for Sentinel consumption
terraform show -json tfplan.binary > mock-pass.json

# Manually edit mock-pass.json to create failure cases
cp mock-pass.json mock-fail.json
# Edit mock-fail.json: change instance_type to p3.2xlarge, remove tags, etc.

Step 3: Write Sentinel Test Configuration

Create test/policy_test.hcl to define expected outcomes for each fixture:

test "enforce_instance_types_pass" {
    source = "../enforce-instance-types.sentinel"
    config = {
        prohibited_types = ["p3.2xlarge", "p4d.24xlarge"]
    }
    mock = {
        "tfplan/v2" = "./mock-pass.json"
    }
    want_result = true
}

test "enforce_instance_types_fail" {
    source = "../enforce-instance-types.sentinel"
    config = {
        prohibited_types = ["p3.2xlarge", "p4d.24xlarge"]
    }
    mock = {
        "tfplan/v2" = "./mock-fail.json"
    }
    want_result = false
}

Step 4: Run Tests and Iterate

sentinel test ./test/
# PASS: enforce_instance_types_pass
# FAIL: enforce_instance_types_fail (expected false, got true)
# → Adjust policy logic, re-run until green

This local loop eliminates the slow feedback cycle of pushing to TFE and waiting for runs. Commit both policies and test fixtures to version control. Treat policy tests with the same rigor as application unit tests—they are your compliance regression suite.

How does Sentinel compare to OPA/Rego and native Terraform validation?

Choosing the right policy engine depends on your team’s existing skills, infrastructure platform, and compliance requirements. Here is a practical comparison based on real-world usage across multiple client engagements:

CriteriaSentinelOPA / RegoTerraform Validation Blocks
Integration DepthNative TFE/TFC integration; automatic plan gatingRequires external runner or TFC agent; manual wiringBuilt into Terraform core; no external tooling
Language ComplexityPurpose-built; simpler syntax for IaC patternsGeneral-purpose Datalog; steeper learning curveHCL expressions only; limited expressiveness
Cross-Provider PoliciesYes; import modules for AWS, Azure, GCP, K8sYes; provider-agnostic but requires custom data shapingNo; single-resource scope only
Enforcement Levelshard-mandatory, soft-mandatory, advisoryBinary allow/deny; advisory requires wrapper logicError on validation failure; no warning mode
LicensingBSL 1.1; free for personal/non-prod; paid for TFE/TFCApache 2.0; fully open sourceMPL 2.0; included in Terraform OSS
Best ForTeams already on TFE/TFC needing tight plan integrationMulti-platform policy (K8s, CI, APIs) beyond TerraformSimple input validation within individual modules

If your organization uses Terraform Enterprise or Cloud exclusively and wants the path of least resistance for infrastructure governance, Sentinel is the pragmatic choice. If you need a unified policy layer across Kubernetes admission control, CI pipelines, and Terraform—and have engineers willing to invest in Rego—OPA offers broader applicability. Native validation blocks handle simple variable constraints but cannot replace external policy engines for cross-resource or organizational compliance.

For teams evaluating whether to invest in specialized DevOps tooling versus generalist development, this DevOps automation guide covers when dedicated infrastructure roles make sense versus when full-stack developers can manage policy-as-code effectively.

What are the common pitfalls when adopting Sentinel Policy as Code for Terraform in production?

After implementing Sentinel across multiple production environments, several recurring issues emerge that documentation rarely addresses:

Pitfall: Overly Broad PoliciesBlocks legitimate edge cases→ Use exceptions + scopingFix: Scoped EnforcementFilter by workspace, env, team→ Reduce false positivesPitfall: Ignoring DriftPolicies only run on plan/apply→ Manual changes bypass checksFix: Scheduled Drift DetectionTFE periodic plans + Sentinel→ Catch out-of-band changesKey PrincipleStart AdvisoryMonitor ViolationsPromote to MandatoryAdoption Pitfalls and Mitigations
Sentinel Policy as Code for Terraform adoption pitfalls: overly broad policies cause friction, drift detection gaps leave blind spots, and premature hard-mandatory enforcement blocks legitimate work.

Premature Hard-Mandatory Enforcement

Deploying new policies as hard-mandatory immediately breaks existing workflows and erodes trust. Always introduce policies as advisory first. Monitor violation frequency for 2–4 weeks. Refine rules based on real false positives. Only promote to soft-mandatory (allows override with justification) or hard-mandatory after the team understands the policy’s impact and edge cases are handled.

Neglecting Workspace and Environment Scoping

A policy that makes sense for production may be inappropriate for development sandboxes. Use TFE/TFC workspace metadata and Sentinel’s tfrun import to scope policies:

import "tfrun/v1" as tfrun

is_production = tfrun.workspace.name matches "^prod-" or
                tfrun.environment == "production"

main = rule {
    not is_production or length(violations) is 0
}

Ignoring State Drift Between Runs

Sentinel only evaluates during explicit plan/apply operations. Manual console changes, auto-scaling events, or failed applies that partially modify resources create drift that policies never see. Enable scheduled speculative plans in TFE/TFC to run Sentinel evaluations periodically against current state, catching out-of-band modifications.

Insufficient Error Messaging

When a policy fails, the default output tells users that something violated a rule but not how to fix it. Always include descriptive error messages:

main = rule {
    length(violations) is 0 else
        "Violation: Resources ${violations[*].address} missing required tags. " +
        "Add Environment, Owner, and Project tags per infrastructure standards."
}

Implementing Sustainable Governance with Sentinel Policy as Code for Terraform

Sentinel Policy as Code for Terraform transforms infrastructure compliance from a reactive audit exercise into a proactive engineering practice embedded in daily workflows. Start with high-impact policies (tagging, instance types, public access), establish a rigorous local testing discipline, introduce enforcement levels gradually, and scope rules to avoid unnecessary friction. The goal is not perfect policy coverage on day one—it is building organizational muscle memory where compliance becomes a natural part of writing Terraform, not a gatekeeper bolted on afterward.

If your team needs help designing, testing, or operationalizing Sentinel policies—or integrating them into an existing Terraform workflow alongside application development and API-driven infrastructure automation—reach out through my contact page to discuss your specific environment and compliance requirements.

Frequently Asked Questions

Sentinel is a HashiCorp-native policy-as-code language that enforces compliance rules during Terraform plan and apply phases. It evaluates infrastructure changes against defined policies before resources are provisioned, preventing non-compliant deployments in HCP Terraform or Enterprise environments.

Sentinel requires HCP Terraform Plus (starting ~USD 45/user/month, ~NPR 6,000) or Terraform Enterprise licensing. There is no standalone free tier for production use, though HashiCorp provides a free playground for learning and testing policy logic without infrastructure costs.

No. Sentinel integration requires HCP Terraform or Terraform Enterprise. Open-source CLI users should consider Open Policy Agent with Rego as a free alternative for local policy enforcement, though it lacks native Terraform Cloud integration and requires separate tooling setup.

Use the Sentinel CLI simulator with mock Terraform plans exported via terraform show -json. Write unit tests in .sentinel files using test blocks to validate policy logic against fixtures. The HCP Terraform UI also provides a policy evaluation preview during plan review before enforcement activates.

Sentinel is purpose-built for HashiCorp tools with native Terraform state access and simpler syntax for infrastructure teams. OPA Rego is more general-purpose, works across ecosystems, but requires adapters for Terraform data. Choose Sentinel for HCP Terraform shops; choose OPA for multi-tool governance or open-source workflows.

Import the tfplan/v2 module to access planned resource changes, prior state, and configuration values. Use tfplan.resource_changes to iterate over modified resources and evaluate attributes like instance_type, tags, or encryption settings. This module is automatically available in HCP Terraform policy evaluations without manual setup.

Common causes include incorrect attribute paths in tfplan imports, missing null checks on optional fields, or soft-mandatory enforcement level allowing overrides. Enable verbose logging in policy sets, verify your test fixtures match actual plan structure, and confirm the policy set is attached to the correct workspace or organization scope.

Yes. Create an organizational policy set with mandatory enforcement that iterates through tfplan.resource_changes and validates required tags exist with correct formats. Attach at the organization level so all current and future workspaces inherit the rule automatically, eliminating per-workspace configuration drift and ensuring consistent metadata governance.

Use advisory enforcement level for known violations during migration periods, or implement allowlist logic checking resource addresses against approved exception lists stored in external data sources. Track exceptions in version-controlled files with expiration dates. Never disable mandatory policies entirely; scope exceptions narrowly and audit regularly.

For mandatory policies, the apply halts immediately and returns the violation details. Soft-mandatory policies allow authorized team members to override via UI or API with justification. Advisory policies log warnings but permit execution. Failed runs generate audit trails in HCP Terraform showing which policy blocked deployment and who attempted the override.

Separate concerns into modules: one for AWS-specific rules, another for Azure, shared utilities for common validations. Use parameterized functions accepting resource objects rather than hardcoding attribute paths. Store reusable logic in policy set libraries. Version control everything with semantic tagging so policy changes align with infrastructure evolution and team handoffs remain manageable.

Limited. Sentinel primarily evaluates planned state after terraform plan executes, not raw input variables. For pre-plan validation, use Terraform variable validation blocks or custom preprocessing scripts. Sentinel excels at evaluating computed values, resource relationships, and cross-resource constraints that only exist in the planned state graph after interpolation resolves.

Export the failing run's plan JSON from HCP Terraform, then run sentinel test -verbose locally with matching fixtures. Check import statements resolve correctly, verify attribute existence with safe navigation operators, and inspect intermediate values using print statements in test mode. The HCP Terraform policy evaluation log also shows which specific rule clause triggered failure with line numbers.

Yes, through policy set parameters configured in HCP Terraform UI or API. Define parameter placeholders in Sentinel code using param() function calls, then supply values per environment or workspace at runtime. This enables single policy definitions enforcing different thresholds across dev, staging, and production without duplicating logic or triggering unnecessary redeployments when tuning limits.

Nested loops over large resource collections cause exponential slowdowns. Flatten iterations using filter and map functions instead of nested for-each blocks. Cache repeated lookups in variables. Avoid importing unused modules like tfstate unless necessary. Profile slow policies using sentinel test timing output, and split monolithic rules into focused, composable functions that evaluate independently and fail fast on first violation.

Share this article

Quick Contact Options
Choose how you want to connect me: