rootform check evaluates Policies against one stage of a plan or state Form,
or by default against both sides of a comparison Form, and exits with the
verdict. Below, you write a one-Policy Pack and run it against the
analysis.json saved in the quickstart.
Checking is a separate step from rootform run, which analyzes and never
evaluates Policies: an analysis that exits 0 says nothing about compliance.
A Policy Pack is a directory with one manifest and one or more Policies in
.rf.hcl files. Create policies/ beside analysis.json:
policy_pack "review" { version = "0.1.0"}policy "database-network-context" { target { concept = rf.concept.managed-database }
assert = ( exists(contexts(rf.context.network, rf.concept.virtual-network)) || exists(contexts(rf.context.network, rf.concept.subnet)) )
message = "Managed databases must belong to a network context."}The target selects every instance a Dialect interpreted as a managed database, whatever the provider. The assertion asks whether each one has an established network Context toward a virtual network or a subnet. A Terraform reference alone cannot satisfy it; only a fact a Rule established can. Write a Policy Pack covers the language.
rootform check analysis.json --policy-pack ./policies -o results.json -o results.mdPolicy check completedOrigin Plan (saved Form)Stage PlannedPolicies 1 selectedEvaluations 1Passed 1Verdict PASSED--policy-pack selects the Pack for this command only. The plan holds one
managed database, a PostgreSQL flexible server, whose network Context to a
subnet came from the saved plan, so the single evaluation passes and the
command exits 0.
Read Policies and Evaluations before trusting the verdict. A Policy
that finds no target makes no decision, and a check that selects no Policy
proves nothing. The summary says so explicitly and exits 3 in both cases.
Statuses 2 and 4 report incorrect use and an unreadable or unwritable
file. Policy check exit status
is the reference.
results.json is the Policy result: it names the Form by digest, the evaluated
stage, the selected Policies, and every evaluation with its evidence.
results.md is the same result as a review document, and -o results.sarif
writes it for a code-scanning tool. Rootform writes them whatever the verdict,
so a violation still produces its report, and none holds sensitive plan values.
To see why an evaluation passed or failed, explain it from the result:
rootform explain policy review.policy.database-network-context \ --result results.json --input analysis.json --detailsPolicy explainedOutcome PASSED azurerm_postgresql_flexible_server.prodcheck also accepts a plan JSON and compiles it first. Pair the saved plan
so that references unknown until apply can still be settled:
rootform check plan.json --plan-file plan.tfplan --policy-pack ./policiesWithout the saved plan, the same Policy can return INDETERMINATE on the same
resources: the subnet ID is unknown until apply and Rootform refuses to guess.
Status 3 keeps that uncertainty out of an approval.
Understand Policy outcomes shows a pass, a
violation, an indeterminate result, and a Policy without target on small plans
you produce yourself.
An override is right for trying a Pack. For repeatable checks, record it:
rootform add policy-packs ./policiesrootform check analysis.json --lockedadd writes the Pack's exact identity to rootform.lock; commit the lock
with the project. --locked refuses overrides, so a CI job evaluates the
Packs the lock records and fails when the lock is missing. The lock records
the selection; it does not protect it. A pull request can still change the
lock or the Pack, so review both like code, and evaluate a pull request with
the Pack from the base revision when the Policies must not be relaxed by the
change they judge, as Review a pull request
does. Other CI/CD turns this into a gate, and
Policies and Policy Packs explains what a result
proves.