A Terraform plan tells you what will change. It does not tell you whether the change is safe.
blastcheck reads terraform show -json and emits an Impact Manifest: a machine-readable change-safety assertion. Free, offline, no credentials, no hosted service.
That file was produced by this tool. It records, for every resource change in a plan, whether the change is reversible, whether anything becomes unrecoverable, whether exposure widens, and — the part that matters most — what could not be determined.
An unknown in that document is not a bug. It means the producer could not verify something, and is saying so rather than guessing. Unproven is not safe, and a consumer must never treat it as such.
pip install blastcheck
terraform plan -out tfplan
terraform show -json tfplan | blastcheck > manifest.json
In a pull request:
- run: terraform show -json tfplan > plan.json
- uses: prococonsulting/blastcheck@v0
with:
plan: plan.json
| Dimension | Example the plan can actually prove |
|---|---|
reversibility | An Azure managed disk grow is one-way. Azure cannot shrink a disk. |
security_posture | An inbound rule opening 0.0.0.0/0 to 22 or 3389; a database turning on publicly_accessible; a bucket set to public-read; encryption or deletion protection switched off. |
data_durability | A delete removes the primary copy of a data-bearing resource, and recoverability is unverified rather than assumed. |
state_confidence | drift_detected when Terraform's own refresh found the resource had moved. Otherwise not_verified — see below. |
Every change in a plan is assessed. Nothing is skipped for being an unfamiliar resource type.
| Layer | Applies to | Confidence |
|---|---|---|
| 0 — structural | any provider ever written: action semantics, action_reason, replace_paths, drift, unreadable fields | high |
| 1 — heuristic | any provider, by resource-type and attribute name and value patterns | low, tagged heuristic |
| 2 — precise | resource types with an exact rule (Azure disks, VMs, NSGs, storage, SQL today) | high |
On a real 55-change AWS plan, with no AWS-specific code at all, the heuristic layer finds a publicly accessible unencrypted database with final snapshots disabled, a public-read bucket, and a security group open to the internet.
Those findings are graded caution, never blocking, and their evidence is tagged source: heuristic. They are leads, not determinations, and saying so is the difference between a tool people read and a tool people mute. For the same reason an open egress rule and a default route are not flagged: both are 0.0.0.0/0 by definition, and firing on them would trip on nearly every plan ever written.
terraform plan refreshes by default: before computing a diff it reads live reality for every managed resource and records anything that moved in a top-level resource_drift array. That is a live-state observation already sitting inside an offline artifact. blastcheck did not perform the read. Terraform did. The fact is no less true for it.
A resource appearing in both resource_drift and resource_changes is the most dangerous shape this tool can find, and it is graded blocking:
azurerm_managed_disk.sql_data severity: blocking
state_confidence: drift_detected (recorded 512 -> live 1024)
verdict: blocked
The plan is internally consistent. It reads as routine. It was computed against a description of that resource which had already stopped being true, and every other verdict for it was derived from the same stale state. That is the outage this exists for.
Two limits, stated rather than papered over. An empty resource_drift is ambiguous — it means either refresh found nothing or refresh did not run (-refresh=false), and the plan does not record which, so absence never earns state_matches_reality. And refresh only sees resources Terraform manages; anything created outside Terraform is not in state, so nothing refreshes it.
safeblastcheck is offline by design. It never queries live cloud state, so it is not entitled to certify that a change is safe — it can only report caution, blocked, or unknown. That is not a limitation being apologised for. A tool that says safe when it means I found no problem converts an absence of information into a green light and puts someone's name on it.
A false safe is catastrophic. A false unknown is annoying. The format is built around that asymmetry.
blastcheck --plan plan.json --sign > manifest.json
blastcheck --verify manifest.json
--sign attaches a digest over the manifest canonicalized with JCS (RFC 8785), so two conformant producers of the same logical document compute the same bytes. Editing a verdict after the fact breaks verification.
Impact Manifest is an open specification, Apache-2.0, with no assumption of any cloud and no coupling to any implementation. blastcheck is a reference producer of it, not its owner. Other tools are expected to implement it, and the format is only worth anything if they do.
The plan was right. The state was wrong. — a Terraform apply that took production down on a change whose plan was three lines long and completely correct, and why every pre-apply tool would have passed it.
Bug reports and feature requests: open an issue. Questions about the format, or about implementing it in another tool: the specification repository, or email kproffitt@prococonsulting.com.
Built and maintained by Kevin Proffitt — prococonsulting.com, LinkedIn.