Reviews
Destructive changes and recreates
Terraform replaces a resource whenever a ForceNew attribute changes. On a stateless resource that is a shrug. On a database it is data loss.
This is the difference between a gate that says “an attribute changed” and one that says “this pull request will recreate your payments database”, and it is the reason this family of rules exists.
The rules
| Rule | Fires when |
|---|---|
destructive.recreate |
A literal ForceNew attribute changes value. Critical on a stateful resource. |
destructive.removed |
A resource is removed from configuration. |
destructive.rename_without_moved |
A rename with no moved block — which destroys and recreates rather than renaming. |
destructive.count_shift |
A count change renumbers instances, recreating resources. |
destructive.no_prevent_destroy |
A stateful resource carries no prevent_destroy lifecycle guard. |
The ForceNew table
Answering “does changing this attribute replace the resource?” needs provider knowledge the parser does not have. Garboard carries a curated, pinned, embedded table for it — no provider binary, no network call, no version drift at review time.
Pinned and offline is a deliberate trade. It means the table can be behind a very new provider release, and it means a review produces the same answer today and in six months regardless of what has been published since.
It needs a base to compare against
destructive.recreate compares a changed value to its previous value, so it needs the base of the pull request. A check run with no base context produces zero destructive findings, and that is correct rather than degraded — there is genuinely nothing to compare. If you are running the gate over a working tree with no base, this family stays quiet by construction.
Plan-aware gating goes further
Given a terraform show -json plan artifact, Garboard reads what the plan will actually do rather than inferring from the source:
terraform plan -out=tf.plan && terraform show -json tf.plan > plan.json
garboard gate . --plan plan.json
That adds plan.stateful_destroy, plan.stateful_replace and plan.blast_radius, and a plan finding wins over a static finding about the same resource — the plan is evidence about the change, where the source is an inference about it.
You produce the plan in your own credentialed CI step. Garboard never runs Terraform and holds no cloud credentials.
