blastcheck
blastcheck reads a terraform show -json plan and emits an Impact Manifest — a machine-readable change-safety assertion. It is the reference producer of that open format.
terraform plan -out plan.tfplan
terraform show -json plan.tfplan | blastcheck > manifest.json
terraform plan tells you what will change. blastcheck adds the layer the plan can't: for each change, is it reversible, does anything become unrecoverable, does exposure widen, and — honestly — what could it not determine.
What it does, and what it deliberately doesn't
blastcheck is offline: no credentials, no network, no hosted service. It reasons from the plan artifact alone. Wherever a verdict genuinely requires live cloud state — "is this disk attached to something serving traffic?", "does a backup exist?" — it emits unknown / not_verified with a stated reason, rather than guessing.
A direct consequence, and the point of the format: a plan-only run can never emit safe. It never verified live state, so it says caution, blocked, or unknown — never safe. Certifying safe requires the live-state enrichment that a later version (or a paid consumer) layers on. blastcheck is honest about the ceiling of what a plan alone can prove.
What it derives from the plan alone is still substantial:
- Irreversibility — e.g. a managed-disk grow is one-way (Azure can't shrink), visible in the plan diff.
- Widened exposure — an inbound NSG rule opening
0.0.0.0/0to a sensitive port; a storage account turning on public access or lowering TLS. - Data-loss risk — deleting a data-bearing resource flags the primary copy as removed and recoverability as unverified, not safe.
- Cost direction and action semantics.
- Drift — see below. This is the one place blastcheck reaches a real state determination rather than an
unknown.
Drift, without asking you for credentials
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 the offline artifact. blastcheck did not perform the read — Terraform did — but the fact is no less true for it.
A resource appearing in both resource_drift and resource_changes is the most dangerous shape blastcheck can find, and it is graded blocking:
azurerm_managed_disk.sql_data severity: blocking
state_confidence: drift_detected (recorded 512 -> live 1024)
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 that same stale state.
Two limits, stated rather than papered over:
- An empty
resource_driftis ambiguous. It means either "refresh found nothing" or "refresh did not run" (-refresh=false), and the plan does not record which. So absence never earnsstate_matches_reality; it staysnot_verified. - Refresh only sees resources Terraform manages. Anything created outside Terraform is not in state, so nothing refreshes it. Shadow-IT discovery needs a direct cloud query and is out of scope here.
Drift on a resource this plan does not touch is recorded under extensions.drift_outside_this_plan rather than invented into a change.
blastcheck is a producer, not a gate. It emits the manifest and exits 0; turning that into pass/fail is a separate policy layer (a CI gate). Exit codes reflect execution, not the verdict.
Scope (v0.1, intentionally narrow)
Azure: managed disks, virtual machines, network security groups (+ rules), storage accounts, SQL databases. Anything else in the plan is recorded under extensions.skipped — never silently dropped. The narrow surface is a choice: the job of this version is to exercise the Impact Manifest schema against real plans and find its shape errors, not to be a finished product.
Install & use
pip install blastcheck
terraform show -json plan.tfplan | blastcheck # stdin
blastcheck --plan plan.json > manifest.json # from a file
blastcheck --compact # single-line JSON
No hosted service, no cloud credentials, no network, no runtime dependencies — it runs entirely against the plan file.
In CI
- run: terraform show -json tfplan > plan.json
- uses: prococonsulting/blastcheck@v0
with:
plan: plan.json
The manifest is uploaded as a build artifact and the verdict is posted on the pull request. The action does not fail the build — blastcheck is a producer, not a gate, and what a blocked verdict should do to a pipeline is a policy question that belongs to you. To gate on it:
- uses: prococonsulting/blastcheck@v0
id: bc
with:
plan: plan.json
- if: steps.bc.outputs.verdict == 'blocked'
run: exit 1
Note what you cannot write: there is no verdict == 'safe' gate to pass on a plan-only run, because a plan-only run never emits safe. A pipeline that proceeds only on a positive safety claim needs the live-state enrichment. That is the honest ceiling of what a plan by itself can prove.
Tests
The suite's primary job is to prove every emitted manifest validates against the vendored schema (blastcheck/schema/impact-manifest.schema.json) on realistic plan fixtures — that is how a schema shape error surfaces.
pip install -e ".[test]"
pytest
Relationship to the spec
blastcheck implements the Impact Manifest specification and vendors a pinned copy of its schema at blastcheck/schema/, which ships inside the wheel. The format is open and vendor-neutral; blastcheck is a reference implementation of it, not its owner.
Status
v0.1 — draft, narrow, and evolving alongside the spec (which does not freeze at 1.0 until this tool has run against real Terraform plans).
License
Apache-2.0. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file blastcheck-0.2.0.tar.gz.
File metadata
- Download URL: blastcheck-0.2.0.tar.gz
- Upload date:
- Size: 42.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76c564181de6c19f826e0f0e9ff7435926012e0b5db411b950332588616b90c4
|
|
| MD5 |
69748dad6b411c43a56284c9f4c0624b
|
|
| BLAKE2b-256 |
0f57cf8168285b9ac79bf5ace9859eda6a94886b95f10fa8308dbee8c45edc82
|
File details
Details for the file blastcheck-0.2.0-py3-none-any.whl.
File metadata
- Download URL: blastcheck-0.2.0-py3-none-any.whl
- Upload date:
- Size: 32.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b12be99c674fff40c9107fd76af983e26a276d6cbc52c6503f6e0e8e923d66d9
|
|
| MD5 |
d0e78ed5d9f89c8b8c425f14f43b5c5d
|
|
| BLAKE2b-256 |
9277347f416a07027136b8d09c0ab036cea5fd030d739a1baa04c756fbd89cfc
|