Chris Collins

Tools · infra

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

  1. Save a plan and turn it into JSON:

    terraform plan -out=plan.out
    terraform show -json plan.out > plan.json

    OpenTofu writes the same format (tofu show -json plan.out). A state file (terraform show -json without a plan) is recognised and refused with that hint.

  2. Plan: drop plan.json on 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.

  3. Summary: the line terraform plan ends 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_paths the provider cannot change in place, a taint, -replace, or replace_triggered_by — and the order: -/` destroys first (downtime), `/- is create_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 removed block, failed checks, an errored or incomplete plan, and drift found while refreshing.

  4. Changes: one card per resource with its attribute diff in terraform plan’s layout: `+ added, - removed, ~ changed, # forces replacement on the attribute that causes a replacement, (known after apply) from after_unknown, and (sensitive value) wherever before_sensitive/after_sensitive mark a value — at any depth. Unchanged attributes are folded into # (n unchanged attributes hidden) except the identifying id, name and tags; tick show unchanged attributes for all of them. Output changes and drift have their own cards.

  5. 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 references and depends_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.

  6. 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 local values 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.

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

example

a sample plan (list below)

replace

q

filter by address, type or provider

aws_instance

act

action chips, comma separated: create, update, replace, delete, read, forget, no-op, import, moved

replace,delete

noop

also list unchanged resources (1)

1

all

show unchanged attributes in diffs (1)

1

example= Tab Shows

web

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

graph

The same plan’s dependency graph, through the dns module’s variables (open)

text

text

The same plan as terraform plan prints it (open)

destroy

summary

terraform plan -destroy, including a database and a bucket (open)

replace

changes

Replacements for a taint, -replace and replace_triggered_by; destroys from shrinking count and for_each (open)

nochange

text

No changes, but drift found while refreshing (open)

For example, ?example=web&act=replace lists only what is replaced or destroyed.

Runs entirely in your browser; nothing you type leaves this page.