Everything you can know about a change, without asking permission
Terraken is a free, open-source command-line tool for Terraform and OpenTofu. Hand it a plan and it tells you what the change actually does, ranked by how much damage it can do. It takes a file and nothing else: no credentials, no network, no apply, and no attribute value in the output.
Install
go install github.com/dbhq-uk/terraken/cmd/terraken@latestterraken 1 finding terraform 1.9.8
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
HOW MUCH OF THIS COULD BE CHECKED ─────────────────────────── 1
1 of 1 change were read in full, and the note below is what
the rest of this plan does not say
this plan records nothing that changed underneath the estate,
and that means one of two things it does not distinguish:
either nothing drifted, or refresh never ran and nobody looked
CRITICAL ──────────────────────────────────────────────────── 1
azurerm_postgresql_flexible_server.main
destroy, then create
├ holds data, so destroying it loses that data
├ an attribute changed that cannot be updated in place
├ forces replacement zone
├ changes 1 attribute
│ zone
└ destroyed before the replacement is created
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
These are the top-level attributes the plan shows as changing,
so a change inside one of them is counted once. The count is not
a measure of how big the change is.
1 criticalReading the report
One section per severity present, most severe first, and none at all for a severity with nothing in it. The report is set to the width of your terminal, between 60 and 100 columns.
The finding above is critical rather than high for one reason: the resource type holds data, so destroying it loses that data. It is the only escalation in the tool, and it applies to destruction alone.
Notice what is not there. The attribute that forced the replacement is named as zone, and neither its old value nor its new one appears, because no value ever does.
What it finds
- The shape of the plan before the findings: what kind of change this is and which modules hold the churn, so you know where to look before reading any of it. Only on a plan large enough to need it
- What this change destroys, ranked, with the ones that lose data first
- What else depends on it: every resource in the plan that a destroy or a replacement reaches, transitively, and how near each one is. Computed from the plan's own dependency graph, so it needs no credentials and no
terraform graph - Why a resource is being replaced, using Terraform's own stated reason
- Which attribute forced the replacement, by name
- Renames that forgot a
movedblock: a destroy and a create that look like the same resource, which is how a refactor quietly destroys a database it meant to keep - Attributes whose before and after are the same value written differently: a reshuffled list, a JSON policy whose keys moved, a re-indented heredoc, a port that came back as a string, a null that became an empty list
- When that is the whole of a resource's change, said out loud, which is the fastest way to clear an update in place that is really nothing
- Credentials sitting in the plan file that Terraform did not mark sensitive: root variables, resource attributes and outputs, named by path and by what they appear to be. Never by value, which is the only form in which this can be said safely
- What cannot be known until apply, so you can see which claims about this change are unverifiable in review
Each of those is a statement of fact rather than a verdict. Order is significant for a container command, whitespace is significant in a script, and a tool that called either one harmless would eventually be confidently wrong about somebody's production change. Terraken says what kind of difference it found and leaves the call to you.
Three things it will not do
It takes a file, and runs nothing
Terraken reads a plan file, or the same JSON piped in on standard input. It never runs terraform, never reads a cloud credential, never makes a network call and never applies anything. The only file it writes is the one you name with --out, created mode 0600 on Unix because the report lists every resource in the plan. Windows has no Unix permission bits, so there the file takes the directory's ACLs instead.
It never prints an attribute's value, in any format
Not masked, not redacted, not truncated. Values are not in the output at all. Masking depends on Terraform having marked a value sensitive, and that marking is best-effort: a live credential was found in a real plan that Terraform had not marked. Paths, counts, levels and Terraken's own sentences are all it will ever show you - including when it is telling you that one of those paths holds a credential.
It is deterministic, with no model in the loop
The same plan always produces the same verdict. Nothing is sent anywhere, there is no model to talk you round, and no ranking that cannot be read straight off the plan. Findings are sorted most severe first with ties broken on the resource address, so two runs of the same plan are byte for byte identical.
Not yet built
Where this is going
Terraken ships one command today and the rest of this page describes it accurately. This is the rest of the plan, and each item links to the issue tracking it. What makes the list worth reading is not its length but its edges: every capability here was picked because it can be built without giving up one of the three contracts above. Roughly a hundred things a Terraform tool could do were considered; about ten survive that test, and six of them have shipped and moved up the page.
Two plans, and what actually resolved(external site)#9
Re-plan after a fix and the question is which findings went away, which are new, and which are exactly as they were. That is a comparison of two files, which is still two files.
One report across many roots(external site)#10
Estates are split across many Terraform roots and a change often touches several. One ranked report over all of them, rather than a terminal window per directory.
Cost delta, from a price sheet on disk(external site)#11
What this change does to the bill, computed against a price file you supply. Every other tool in this space asks for an API key; a price sheet is a file, and a file is inside the contract.
Evidence, for teams that must show their working(external site)#12
Regulated change control needs an artefact saying what was reviewed, when, and what it said. Deterministic output is exactly what makes such an artefact worth anything.
Linting, formatting, security scanning, documentation and orchestration are all deliberately absent. Each is held by a good tool with years of accumulated rules, and aggregating them means inheriting the maintenance without earning the credibility. The aim is not to own your session - terraform already does that. It is to be the thing you hand a plan to when you need to know what it really says.
Install
go install github.com/dbhq-uk/terraken/cmd/terraken@latestOr download a binary from the releases page (external site). The release ships terraken and tken, built from the same package, so use whichever name is free on your machine. Builds are for Linux, macOS, Windows, FreeBSD, OpenBSD and Solaris.
Then run it
terraform plan -out tfplanterraform show -json tfplan > plan.jsonterraken plan.jsonOr skip the file entirely. Plan JSON can hold a credential in the clear, and a file on a CI runner is one more place for it to be left behind:
terraform show -json tfplan | terraken -Four levels, and no medium
A middle bucket is where findings go to be ignored, so there is not one.
| Level | What reaches it |
|---|---|
critical | A destroy or a replacement of a resource type that holds data, so destroying it loses that data. This is the only escalation in the tool, and it applies to destruction alone: updating a database in place does not lose data. |
high | Any other destroy, or any replacement. The existing object is destroyed either way, whatever is or is not inside it. |
low | An update in place, or a resource that is being forgotten from state but left running. |
info | A create, a data source read, an import and a no-op. Nothing existing is being taken away. |
The source, and the tests that hold it
Every line is public and MIT licensed, and the three boundaries above are held by tests in that repository rather than by a promise on this website. It also carries the fixtures the output on this site was produced from, including a plan that exists only to be leaked from.