Winch Labs

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.