Produce a completed plan with your usual Terraform or OpenTofu workflow.
Rootform reads its JSON export locally. It does not plan, refresh, apply,
contact providers, or fetch missing Dialects. Run rootform run from the
project whose rootform.lock selects the Dialects you want;
--project chooses another project directory explicitly. Evaluate selected
Policy Packs separately with rootform check.
Rootform accepts completed plan and state exports in JSON format 1.x.
This is the export format, not the Terraform/OpenTofu or provider version.
Other major formats are refused. Each JSON or saved Form input is limited
to 128 MiB.
Save the plan and export that same saved plan. OpenTofu users use tofu
where Terraform users use terraform:
Terraform
terraform plan -out=plan.tfplanterraform show -json plan.tfplan > plan.jsonOpenTofu
tofu plan -out=plan.tfplantofu show -json plan.tfplan > plan.jsonUse show -json on the saved plan. plan -json is a planning event
stream, not the completed export Rootform accepts. Preserve the binary plan
for verification if you need expression evidence. Where state already
exists, export it with terraform show -json > state.json, or
tofu show -json > state.json. A new working directory has no state to
export.
Pass the plan JSON export and its saved plan:
rootform run plan.json --plan-file plan.tfplan --require-enrichment --no-serve -o analysis.jsonPlan analyzedInput plan.jsonPlan completeness Complete, as reported in the planEnrichment Saved plan paired with this plan JSON (1 module)Wrote analysis.jsonEnrichment confirms that the saved plan and the JSON export agree on the
recorded tool version, timestamp, and configuration shape. These checks decide
whether the saved plan may enrich this export; they do not validate the plan in
general, and they do not prove one planning operation. The count is the number
of configuration modules read from the saved plan. Input names the export.
Producer appears only when the tool is established, as Terraform or
OpenTofu, without a version. The JSON's shared terraform_version field
and provider registry hosts do not identify the tool; --producer records
your explicit attestation. The Form retains the reported version, hints and
attestations. Plan completeness repeats what the plan reports about itself;
it does not say that the analysis settled every fact.
A paired saved plan lets Rootform inspect direct identity traversals in the
saved configuration, even when an endpoint's evaluated ID is unknown until
apply. It does not execute the configuration. The Form records
enrichment.snapshot.status as verified when the pair passes these checks,
refused, or absent.
If the pair is mismatched or unreadable, Rootform reports the refusal on
standard error. Without --require-enrichment, analysis continues from JSON
alone and records refused; the missing traversal evidence can leave
closures indeterminate. With --require-enrichment, refusal exits 3.
Export the JSON from the saved plan you pass, rather than trying to pair a
new plan with an earlier export.
A plan's planned stage shows proposed instances. refreshed describes the
state the plan starts from; the plan does not record whether or how far refresh
ran. recorded is reconstructed from drift records and can be partial.
The same plan may report three comparisons: Reported drift
(recorded to refreshed), Planned changes (refreshed to planned),
and Net change (recorded to planned). A state export has only
recorded.
In accepted plan format 1.x, Terraform and OpenTofu omit prior_state exactly
when the state before the plan has no resource and no root output. Rootform
records an empty Refreshed stage and reconstructs Recorded from drift records.
Recorded can contain a resource deleted during refresh even when Refreshed is
empty. Terraform 1.12.2 and OpenTofu 1.10.7 exports are qualified; other 1.x
exports are accepted by shape.
The Recorded to Refreshed comparison is Reported drift. The separate drift report lists the producer's drift records and their architectural consequences. What does this plan change? shows where the Explorer lists these views.
-refresh=false prevents Terraform or OpenTofu from checking live objects.
“No drift reported in this plan” means the export contains no drift records;
it does not prove that infrastructure is unchanged.
-target or -exclude can leave instances outside the plan's scope. Terraform
may report complete: false for such plans, while OpenTofu may omit a completeness
field. Rootform preserves that uncertainty. A missing planned instance is
not automatically a deletion or proof of zero instances. An instance present
before the plan that it neither changes nor deletes remains in Planned with
status carried; its population is unverified and the report says the plan did
not evaluate it. When a fact cannot be settled on both sides, the comparison
records it as indeterminate rather than inventing an addition, removal, or no
change. See
stages and facts and
comparisons and drift.
--plan-complete=attested records your explicit claim that the plan covers
its intended scope when the export does not establish completeness. It is
not inferred from absent entries. --producer terraform or
--producer opentofu records the tool you used; the shared
terraform_version JSON field alone does not establish that identity.
--provider-map observed=binding makes an explicit provider registry
mapping when a Dialect binding needs it. These options record your claims,
not facts independently verified by Rootform. Do not use them to hide a targeted
or partial plan.
A planned value can be known, unknown until apply, sensitive, or unavailable. A verified direct traversal can identify an endpoint despite an unknown ID; a transformed expression or dependency list alone cannot. Sensitive values are discarded before Form output, reports, SARIF, and the Explorer. Dialect-declared external identities may still be disclosed at their declared tier. Read limitations before relying on a missing fact as a negative conclusion.
To avoid an intermediate JSON file, pipe the saved-plan export into Rootform. The binary saved plan still needs protection:
terraform show -json plan.tfplan | rootform run - --plan-file plan.tfplan --no-serveFor OpenTofu, replace terraform with tofu. The saved plan remains
local. Rootform's outputs still describe infrastructure names, structure,
and relationships, so apply your internal sharing rules.
To compare two plans, or a state Form with a later plan, follow Compare two Forms. For plans from two Git revisions, Review a pull request adds isolated checkouts and cleanup. To review a completed plan in automation, see Other CI/CD or GitHub.