
A terraform plan can be syntactically correct yet propose dangerous
changes: a forgotten m5.24xlarge instance, a database without encryption, a
deployment in an unauthorised region. Policy as code automatically evaluates
the plan before the apply to block violations.
What you will learn
- What policy as code is and why it exists
- The difference between Sentinel and OPA (Open Policy Agent)
- The distinction between policy checks and policy evaluations
- How policy sets and enforcement levels work
- How to create and apply a first policy
- The free tier's limits for policy as code
What policy as code is
Policy as code in HCP Terraform fits into a run's lifecycle, between the plan and the apply:
| Step | What happens |
|---|---|
| Plan | Terraform computes the changes |
| Policy checks / evaluations | The policies evaluate the plan |
| Apply | The changes are applied (if the policies pass) |
If a policy fails, the run is blocked and no change is applied.
Sentinel vs OPA
HCP Terraform supports two policy as code frameworks:
| Criterion | Sentinel | OPA (Rego) |
|---|---|---|
| Developed by | HashiCorp (proprietary) | CNCF (open source) |
| Language | Sentinel (dedicated DSL) | Rego (declarative language) |
| Ecosystem | HashiCorp-specific | Multi-purpose (Kubernetes, CI/CD, APIs) |
| Learning curve | Low (simple syntax) | Medium (Rego is peculiar) |
| Data available | Plan, state, run, organisation | Plan JSON through input |
| Examples and libraries | terraform-sentinel-policies (HashiCorp) | conftest, Rego Playground |
| Available for free | Yes (limited) | Yes (limited) |
HashiCorp also documents a third framework, Terraform policy, whose rules
are written in HCL. It has three levels: advisory, mandatory overridable (blocking, but an authorised user can continue) and mandatory.
This lesson sticks to Sentinel and OPA, and the end-of-page lab has you compare
the three frameworks.
When to choose Sentinel
Sentinel fits when your organisation already lives in the HashiCorp ecosystem and wants a readable syntax from the first policy:
- You are in a 100% HashiCorp ecosystem
- You want a simple, readable syntax without a learning curve
- You need access to data specific to HCP Terraform (run metadata, organisation info)
When to choose OPA
OPA fits when the same policy language must serve several tools, from the Kubernetes cluster to the CI pipeline:
- You already use OPA for Kubernetes (Gatekeeper) or your APIs
- You want portable policies across tools
- Your team already masters Rego
For learning labs, Sentinel is the simplest choice.
Policy checks vs policy evaluations
This is the most important architectural distinction to understand in HCP Terraform:
| Type | Supported frameworks | Available enforcement |
|---|---|---|
| Policy checks | Sentinel only | advisory, soft-mandatory, hard-mandatory |
| Policy evaluations | Sentinel and OPA | advisory, mandatory (the soft-mandatory and hard-mandatory levels are converted to mandatory) |
In practice:
- Sentinel policies can run as policy checks or as policy evaluations, depending on the policy set type chosen.
- OPA policies run only as policy evaluations.
This distinction affects the available enforcement levels, the execution order within the run and the override possibilities.
Enforcement levels
The available enforcement levels depend on the evaluation type. The two tables below give, for each level, what happens to the run when the policy fails, and when to use it.
Sentinel policy checks
| Level | Behaviour on failure | Use case |
|---|---|---|
| Advisory | The run continues, a warning is shown | Recommendations, non-blocking good practices |
| Soft-mandatory | The run is blocked, but an authorised user can override | Important rules with a possible exception |
| Hard-mandatory | The run is blocked, with no override unless the policy set allows it | Unavoidable compliance rules (security, regulation) |
Policy evaluations (Sentinel and OPA)
In policy evaluations mode, the soft-mandatory and hard-mandatory levels
are converted to mandatory:
| Level | Behaviour on failure |
|---|---|
| Advisory | The run continues, a warning is shown |
| Mandatory | The run is blocked, users with the Manage Policy Overrides permission can continue |
Policy sets
A policy set groups policies and applies them to workspaces or projects.
Structure of a policy set
A policy set groups several policies and decides where they apply and with what severity. Read the table as the four questions to settle before creating one: which rules, on which workspaces, at which enforcement level, and from which source.
| Component | What it does |
|---|---|
| Policies | The individual rules (.sentinel or .rego files) |
| Scope | The targeted workspaces or projects |
| Enforcement | The default level for the set's policies |
| Source | Policies managed in the UI, VCS repository or API / tfe provider |
Creating a policy set
-
Prepare your policy files
Sentinel example, requiring tags on every AWS resource:
enforce-tags.sentinel import "tfplan/v2" as tfplanmandatory_tags = ["environment", "team", "cost-center"]main = rule {all tfplan.resource_changes as _, rc {rc.type is "aws_instance" impliesall mandatory_tags as tag {rc.change.after.tags contains tag}}}OPA (Rego) example, limiting instance size:
restrict-instance-size.rego package terraform.policiesdeny contains msg if {some rc in input.resource_changesrc.type == "aws_instance"rc.change.after.instance_type == "m5.24xlarge"msg := "m5.24xlarge instances are not allowed"}This rule follows the Rego 1.0 syntax, mandatory since OPA 1.0: the
containsandifkeywords are required in it. Measured with OPA 1.10.1, the olddeny[msg] {form fails at parse time with`contains` keyword is required for partial set rules, and the rule above returns[]on a compliant plan and the message on a plan requesting anm5.24xlarge. Examples written before 2025 still carry the old syntax.For an OPA policy managed in the UI, HCP Terraform asks for a query pointing to the Rego rule to evaluate, here
terraform.policies.deny. The result of this query must be an array: the policy passes when the array is empty (no violation), and fails when it holds messages. -
Create the policy set configuration file
For Sentinel, create a
sentinel.hcl:policy "enforce-tags" {source = "./enforce-tags.sentinel"enforcement_level = "soft-mandatory"}For OPA, create a
policies.hclfile or use the web interface. -
Create the policy set in HCP Terraform
In the web interface: Organization, then Settings, Policy Sets, Connect a new policy set.
Field Value Source Policies managed in the UI (for a test) or VCS / API (for production) Framework Sentinel or OPA Scope Specific workspaces / Specific projects / All workspaces -
Test the policy
Run a
terraform planin a targeted workspace. If the plan violates the policy:- In advisory: the run continues with a warning.
- In soft-mandatory (Sentinel checks): the run is blocked, an authorised user can override.
- In hard-mandatory (Sentinel checks): the run is blocked, and only the policy set's override option allows going past it.
- In mandatory (policy evaluations): the run is blocked, a user with the Manage Policy Overrides permission can continue.
Common policies
Here are examples of policies frequently put in place. The last column gives the enforcement level to start from, which you then tighten as confidence in the policy grows:
| Policy | What it checks | Recommended enforcement |
|---|---|---|
| Mandatory tags | Every resource has the required tags | Soft-mandatory |
| Allowed regions | No deployment outside eu-west-1, eu-central-1 | Hard-mandatory |
| Encryption enabled | EBS volumes, S3 buckets and RDS are encrypted | Hard-mandatory |
| Limited instance size | No *.24xlarge or *.metal instances | Soft-mandatory |
| No public IP addresses | EC2 instances have no public IP | Hard-mandatory |
| Limited estimated cost | The plan's cost estimate stays below a threshold | Advisory |
Policy libraries
HashiCorp provides ready-to-use policy libraries, and the OPA ecosystem offers an online sandbox to try Rego before deploying:
- terraform-sentinel-policies (GitHub): a collection of Sentinel policies covering AWS, Azure, GCP and VMware.
- Rego Playground: test your OPA policies online before deploying them.
Governance workflow in production
In production, a policy's lifecycle follows a structured workflow, which moves from observation to blocking only once false positives are dealt with:
-
Develop the policy locally or in the Rego Playground.
-
Test the policy in advisory mode on a staging workspace.
-
Observe the results for a few days, identify false positives.
-
Fix the policy and move to soft-mandatory (Sentinel checks) or mandatory (policy evaluations).
-
Deploy to production in hard-mandatory (Sentinel checks only) once confidence is established.
Troubleshooting
Most blocks come from the enforcement level or the scope of the policy set, rarely from the rule itself. Find the exact message of the policy check in the run first, then follow the matching row of the table.
| Symptom | Likely cause | Solution |
|---|---|---|
Policy check: soft-mandatory failed | The plan violates a Sentinel policy (checks) | Fix the configuration or ask an authorised user for an override |
Policy check: hard-mandatory failed | The plan violates a blocking policy (Sentinel checks) | Fix the configuration; an override only exists if the policy set allows it |
Policy evaluation: mandatory failed | The plan violates a policy evaluation (Sentinel or OPA) | Fix the configuration or ask for an override (Manage Policy Overrides permission) |
| The policy check does not appear | No policy set is attached to the workspace | Check the policy set's scope |
Error: invalid policy on upload | Syntax error in the .sentinel or .rego file | Validate the syntax locally before uploading |
| The VCS policy set does not update | VCS connection not configured (free tier) | Upload by hand or move to the Standard plan |
| False positive on a policy | The policy is too strict | Refine the conditions or add an exception |
Put it into practice
A hard-mandatory is not unbeatable, contrary to what its name promises: the policy set setting decides, combined with the user's permission. You qualify seven runs, establish the levels of the three frameworks, and write a rule that refuses a non-compliant plan.
Key points
- Policy as code automatically evaluates the plan between the plan and the apply. It is your last safeguard before a non-compliant change.
- Sentinel (HashiCorp, simple syntax) and OPA (CNCF, Rego, portable) are the two historical frameworks, joined by Terraform policy (HCL). Sentinel is simpler to start with.
- Sentinel can run as policy checks (3 levels:
advisory,soft-mandatory,hard-mandatory) or as policy evaluations (2 levels:advisory,mandatory). OPA only supports policy evaluations. - Overriding a policy depends on the level, the user's permission and the
policy set's override option: when ticked, it makes even a
hard-mandatoryoverridable. - On the free edition: 1 policy set, 5 policies at most, no VCS. Enough to test and learn.
- Roll out gradually: advisory, then mandatory / soft-mandatory, then hard-mandatory (Sentinel checks) once confidence is established.