skip to content

Custom Rule Authoring

A built-in catalog stops at the generic misconfiguration: your tagging standard and your approved regions exist only once somebody writes them, and where you write them decides who maintains them.

on this pageshow

questions

12

In a Sentinel policy for HCP Terraform, what do the tfplan, tfstate and tfconfig imports each give you?

level: juniorimportance: must knowfreq 68%

answer

  1. not one document, several
  2. what changes vs what exists
  3. the written HCL is its own input
  4. run metadata is a fourth import
  5. cost estimate lives outside the plan

basics

~20 s

tfplan gives the changes this run proposes, tfstate gives the resources the workspace already has, and tfconfig gives the configuration as written. They are three separate documents, so a rule reads whichever one answers its question.

solid answer

~40 s

A policy check in a managed run is not handed "the Terraform code" as one blob. `tfplan/v2` carries this run's proposed changes — `resource_changes` with each address's `actions`, `before` and `after`, plus `planned_values` describing the intended end state. `tfstate/v2` carries what the workspace already contains, which is the only way to reason about resources this run does not touch. `tfconfig/v2` carries the configuration as written — resource and provider blocks, module calls, raw expressions — for rules about how the code is authored rather than what it resolves to. There is a fourth, `tfrun`, with workspace and organization metadata and, when cost estimation is enabled, the run's prior, proposed and delta monthly cost. Choosing the wrong document is the usual reason a rule silently checks nothing.

go deeper

for a junior

Be ready to name the four inputs and say in one sentence what each is for. Interviewers mostly want to hear that the plan, the state and the configuration are separate documents rather than one bundle.

for a middle

Expect to be asked which import answers a specific rule, and why the wrong choice makes a rule pass vacuously. Know the shape of resource_changes: address, actions, before, after.

for a senior

Show that you pick the input deliberately and check the rule actually matched something. Be able to explain why a workspace-wide inventory rule needs state while a change-scoped rule does not.

for a principal

Own the consequence: rules that depend on state or run metadata can only execute where those inputs exist, which constrains where your guardrails can be evaluated and who has to operate that decision point.

## The shape of a policy check inside a managed run In HCP Terraform and Terraform Enterprise a run moves through stages: plan, then cost estimation if it is enabled, then the policy check, then apply. The policy check sits between plan and apply, which means the engine is reasoning about a change that has been fully resolved but not yet executed. What matters for authoring is that the policy is not given a single document. Sentinel exposes the run's data through several **imports**, each a distinct view. ### `tfplan/v2` — what this run proposes The plan import is the workhorse. Its `resource_changes` collection is keyed by resource address (`aws_s3_bucket.data`, `module.net.aws_subnet.a[0]`) and each entry carries the resource's `type`, `name`, `mode` and `provider_name`, plus a `change` object holding: - `actions` — a list such as `["create"]`, `["update"]`, `["delete"]`, `["no-op"]`, or a delete and a create together when the resource is being replaced; - `before` — the attribute values recorded before this run; - `after` — the attribute values the run intends to produce. Alongside it, `planned_values` describes the whole intended end state of the workspace, and `variables` and `output_changes` cover inputs and outputs. Most rules of the form "nothing being created may sit outside our approved regions" are written against `resource_changes`. ### `tfstate/v2` — what already exists The state import exposes the resources currently recorded in the workspace, and the workspace outputs. This is the input you cannot substitute. `resource_changes` lists addresses the run is changing; a resource nobody is touching this run simply is not there. If the rule is "this workspace must not contain any resource outside the approved regions", including the ones created last year, only state can answer it. ### `tfconfig/v2` — what was written The configuration import exposes the source as authored: resource blocks and their raw expressions, provider blocks, variable and output declarations, module calls. Use it when the rule is about the *code*, not the resolved result — a provider block that hardcodes a region, a required variable someone inlined as a literal, a module call whose source is not what the platform team publishes. A plan cannot answer these because by plan time the expressions have been evaluated away. ### `tfrun` — the run itself `tfrun` carries metadata the platform owns rather than anything Terraform computed: the workspace and organization, and — where cost estimation is on — the run's `cost_estimate` with prior, proposed and delta monthly cost. A budget guardrail is expressible only because this import exists. ### Why the separation is the point The same English question gets different answers depending on which document you ask. "Is the region approved?" against `tfconfig` asks how the code is written; against `tfplan` it asks what this change will produce; against `tfstate` it asks what is standing right now. A rule that reads `planned_values` and concludes "no violations" may simply have been looking at a document where the offending resource never appears. Two boundaries are worth stating plainly, because new authors trip on both: 1. **The policy sees only these documents.** It cannot call a cloud API to look something up, cannot read another workspace, and cannot consult a previous run. 2. **These are Sentinel's names.** HCP Terraform can also evaluate OPA policy sets over the same run data; the split between plan, configuration, state and run metadata is a property of the platform, and the import names are a property of the framework. The practical habit to build is to decide, before writing a line, which document actually contains the fact your rule depends on — and then to check whether that document exists wherever you intend the rule to run.

  • What does the tfrun import add that a plan file on its own does not carry?
    Data the platform produced rather than Terraform: the workspace and organization the run belongs to, and — when cost estimation is enabled — the run's prior, proposed and delta monthly cost. A rule that caps the cost increase of a single change, or that applies only to production workspaces, depends on that import existing.
  • Why can't a rule about resources nobody is changing be answered from tfplan alone?
    Because `resource_changes` only lists addresses this run touches. A resource created six months ago and untouched today has no entry there, and `planned_values` will describe it only insofar as the plan reproduces the intended end state. Questions about the workspace's existing inventory belong to the state import.
  • When would you read tfconfig instead of tfplan?
    When the rule is about how the code is written rather than what it evaluates to — a hardcoded credential-shaped literal, a provider block missing a required setting, a module call whose source is not an approved one. By plan time those expressions have been resolved, so the evidence you want is only in the configuration import.

The plan is the diff, the state is the file as it stands on disk, and the configuration is the source you wrote. A reviewer who is handed only one of the three will confidently miss things.

saying these in an interview costs you the question

  • Assumes the plan already contains everything in state
  • Treats the configuration and the plan as the same document
  • Thinks a policy can call the cloud API to look values up
  • Reads planned values and misses resources being destroyed
  • Cannot say where a cost figure would come from

context

open as a page

Why does one conftest rule banning plaintext secret env values need per-format logic?

level: middleimportance: must knowfreq 50%

basics

~20 s

Because conftest policies read the parsed document, not the file text, and each parser produces a different shape. A Dockerfile becomes an array of instruction objects, a Kubernetes container env is a list of name/value objects, and a pipeline env is a plain map - three different paths for one logical field.

open as a page

What file formats can conftest test, and how does a CI job learn it failed?

level: juniorimportance: should knowfreq 58%

basics

~20 s

conftest parses structured configuration - YAML, JSON, HCL, Dockerfiles, TOML, INI and more - into one JSON-like document and evaluates policies against it. Failures print to the console and the process exits non-zero, which is the signal CI acts on.

open as a page

In Checkov, what are the two ways to write a custom check, and when do you need the Python form?

level: juniorimportance: should knowfreq 48%

basics

~20 s

Checkov custom checks come in two forms: a declarative YAML policy made of attribute or connection conditions, and a Python class implementing a scan method. YAML covers straightforward attribute comparisons; Python is for logic YAML operators cannot express.

open as a page

Your Sentinel rule blocks destroying resources tagged data-class=production — why does reading only the planned values miss them?

level: middleimportance: should knowfreq 54%

basics

~20 s

A resource being destroyed has no after values, and it is absent from the plan's planned end state entirely. Its tags live in the change's before values, taken from prior state — so the rule must read before, not after.

open as a page

In a Sentinel policy, which rule decides the verdict, and why might a rule you wrote never run?

level: middleimportance: should knowfreq 45%

basics

~20 s

The rule named main decides it: the policy passes only when main evaluates to true. Rules are evaluated lazily, so a rule that main never reaches is never evaluated at all and silently enforces nothing.

open as a page

Why does a custom Checkov check comparing conf["ami"] to a string fail every instance?

level: middleimportance: should knowfreq 38%

basics

~20 s

Checkov hands scan_resource_conf its own parsed representation of the block, where every attribute value is wrapped in a list. conf["ami"] is ["ami-0aaa1111"], never the bare string, so the comparison is always false and the check fails everything.

open as a page

Your conftest gate has been green for months, but the rule never ran. How?

level: seniorimportance: should knowfreq 42%

basics

~20 s

A conftest run with nothing to evaluate exits zero. The usual causes are a namespace never selected on the command line, a renamed policy package, a glob that missed the files, or an extension with no parser attached - all of which look identical to a clean pass.

open as a page

Your org already runs an IaC scanner — when do you extend it with custom checks rather than adopt a second policy engine?

level: principalimportance: should knowfreq 34%

basics

~20 s

Extend the scanner while your rules fit its model and its authors are the people already on the rota: you inherit its parsing, reporting, ids and pipeline placement for free. Adopt a separate engine when rules must span artifacts the scanner cannot parse, or when several gates should share one rule language.

open as a page

What does conftest's --combine flag change about the input a rule sees?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Without it, each file is evaluated on its own and the input is that file's document. With --combine, all files are merged into one evaluation where the input is an array of elements carrying a path and the file's contents, so rules must iterate rather than address fields directly.

open as a page

It is 02:00 and every apply in HCP Terraform is failing at the policy check — how do you diagnose it?

level: seniorimportance: nice to knowfreq 33%

basics

~20 s

Read the policy check output first: a failure across every workspace at once points at the policy set or its inputs, not at anyone's change. Then reproduce offline by downloading a failed run's mock data and evaluating the policy locally.

open as a page

Your custom Rego rule for Trivy or KICS reports nothing — how do you diagnose it?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Check three layers in order: the engine never loaded the file (wrong custom-rule path, or a package namespace the scan does not include), the required metadata is missing so the rule is not registered or graded, and the Rego body is simply undefined — in Rego undefined yields no result, not a failure.

open as a page