terraform plan -out=plan.out
terraform show -json plan.out > plan.json
Terraform plan visualiser
Open the JSON of a Terraform or OpenTofu plan and see what it will create, change, replace and destroy — with why each replacement is forced, warnings for destroyed data stores, sensitive values masked, the attribute diffs as terraform plan prints them, and the dependency graph from the configuration's references.
Added
How to use it
A second pair of eyes for terraform apply. Give it the JSON form of a saved plan and it sorts out
what actually happens, flags the changes that lose data or cause downtime, and lets you read every
attribute change — in the browser, without uploading the plan anywhere.
How to use it
-
Save a plan and turn it into JSON:
OpenTofu writes the same format (
tofu show -json plan.out). A state file (terraform show -jsonwithout a plan) is recognised and refused with that hint. -
Plan: drop
plan.jsonon the page, open it, or paste it; or pick one of the sample plans. The plan is held in memory only — it is not saved in the browser and is gone when you close the tab. -
Summary: the line
terraform planends with ("Plan: 1 to import, 5 to add, 3 to change, 3 to destroy."), a tile per action (tap one to filter), and Before you apply:-
every destroy, with the reason Terraform gives (
action_reason: removed from the configuration, count shrank, a for_each key went away, the module was removed); -
every replacement, with what forces it — the attributes in
replace_pathsthe provider cannot change in place, a taint,-replace, orreplace_triggered_by— and the order:-/` destroys first (downtime), `/-iscreate_before_destroy; -
destroys and replacements of resource types that usually hold data (databases, buckets, volumes, keys, tables) marked as dangerous — a name-based rule of thumb, not a guarantee;
-
sensitive values that change, data sources that can only be read during apply, objects forgotten with a
removedblock, failed checks, an errored or incomplete plan, and drift found while refreshing.
-
-
Changes: one card per resource with its attribute diff in
terraform plan’s layout: `+added,-removed,~changed,# forces replacementon the attribute that causes a replacement,(known after apply)fromafter_unknown, and(sensitive value)whereverbefore_sensitive/after_sensitivemark a value — at any depth. Unchanged attributes are folded into# (n unchanged attributes hidden)except the identifyingid,nameandtags; tick show unchanged attributes for all of them. Output changes and drift have their own cards. -
Graph: one node per resource instance, coloured by action, with an arrow from each resource to what it depends on (dependencies above). Edges come from the configuration’s
referencesanddepends_on, followed through module variables and outputs, and narrowed to one instance when the reference names it (aws_subnet.public[0]). Tap a node to highlight its neighbours and read its diff; resources with no dependencies either way sit in rows at the bottom. Full screen (F) gives the graph the whole screen. -
Text: the whole plan as text, to copy or download, and Download redacted JSON: the plan with every value Terraform marks sensitive replaced by
(sensitive value)— resource changes, drift, outputs, planned values and prior state — plus the variables the configuration marks sensitive or whose names look like credentials.
The filter (address, type or provider, and action chips: create, update, replace, delete,
read, forget, no-op, import, moved) is shared by the Summary, Changes and Graph tabs.
Why the JSON? The text of terraform plan is made for people and changes between versions; the
JSON is the documented, stable interface (format version 1.x), and it is what policy tools such
as OPA/conftest, Sentinel and Infracost read too.
Limits
-
Sensitive values are in the plan JSON in clear text (masking is only in Terraform’s display). This page never shows them — every view and the redacted download go through the masks — but the file itself is a secret: keep it out of chats, tickets and CI logs.
-
The text view is rebuilt from the JSON without the provider schemas, so it differs from Terraform’s own rendering in places: nested blocks and maps are told apart by shape, multi-line strings are not printed as heredocs, JSON strings are not shown as
jsonencode(…), sets are shown as lists, and objects inside lists are compared by position. -
Graph edges through
localvalues are not visible (the JSON configuration does not include locals), and references to a whole module connect to everything its outputs refer to. -
The "holds data" warning is a rule of thumb from resource type names; check the provider docs.
-
Deferred actions (Terraform 1.10+ experiments) and ephemeral resources are shown as their raw actions.
Sources: Terraform’s JSON output
format (format version 1.2), the terraform plan command reference and the rendering in
internal/command/jsonformat of the Terraform source (headline wording, symbols, plan line and
hidden-attribute rules); OpenTofu’s identical format.
Related: DevOps YAML (Compose and Kubernetes checks), Kubernetes manifests, AWS architecture builder, JSON workbench, Diff and server config builders.
Linking to an example
/tools/terraform-plan/?<parameters>#<tab> where the tab is plan, summary, changes,
graph or text. Links carry the filter and view settings and a sample plan name — never your own
plan, which stays in your browser.
| Parameter | Meaning | Example |
|---|---|---|
|
a sample plan (list below) |
|
|
filter by address, type or provider |
|
|
action chips, comma separated: |
|
|
also list unchanged resources ( |
|
|
show unchanged attributes in diffs ( |
|
example= |
Tab | Shows |
|---|---|---|
|
summary |
A VPC with a forced instance replacement, a create_before_destroy launch template, a sensitive database password, a removed bucket, an import, a move, a forget, a module and drift (open) |
|
graph |
The same plan’s dependency graph, through the dns module’s variables (open) |
|
text |
The same plan as |
|
summary |
|
|
changes |
Replacements for a taint, |
|
text |
No changes, but drift found while refreshing (open) |
For example, ?example=web&act=replace
lists only what is replaced or destroyed.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.