Skip to content

feat(terraform): add apply --forbid-resource-changes guard#194

Merged
sam-at-luther merged 1 commit into
mainfrom
fix/forbid-resource-changes-guard
Jun 9, 2026
Merged

feat(terraform): add apply --forbid-resource-changes guard#194
sam-at-luther merged 1 commit into
mainfrom
fix/forbid-resource-changes-guard

Conversation

@sam-at-luther

Copy link
Copy Markdown
Member

Generic terraform safety guard, added for the InsideOut #2048 two-step destroy but not coupled to it.

What

mars <env> apply --forbid-resource-changes:

  1. Produces (or reuses --plan) a saved plan.
  2. Inspects it via terraform show -json (parsed with hashicorp/terraform-json).
  3. Fails if the plan would create, update, or delete (incl. replace) any managed resource.
  4. Allows state-only actions — no-op, read, and forget (a removed { lifecycle { destroy = false } }).
  5. Applies the exact vetted plan (no TOCTOU; a saved plan needs no -auto-approve).

Why

Lets a caller safely apply state-only changes — e.g. forgetting adopted / reverse-imported resources from state before a destroy — while failing loudly if the same apply would also touch real infrastructure. It's a generic guard (no consumer-specific logic), usable by any pipeline that wants "apply, but only if nothing real would change."

First consumer: sandbox-infrastructure-template's two-step tfDestroy (apply the removed{} forgets, then destroy) — reliable #2048.

Tests

  • assertNoResourceChanges truth table: forget/no-op/read allowed; create/update/delete/replace refused, naming the offenders; forget+delete still refused (names the delete).
  • Fake-runner flow: guarded apply refuses (runs no terraform apply) when a resource would change; proceeds to apply <savedplan> for a state-only forget.
  • go build, go vet, gofmt, full internal/terraform suite green.

terraform-json promoted from indirect to direct dep.

Generic safety flag: with --forbid-resource-changes, `mars <env> apply`
produces (or reuses) a saved plan, inspects it via `terraform show -json`,
and FAILS if the plan would create, update, or delete (incl. replace) any
managed resource. State-only actions — no-op, read, and forget (a
`removed { lifecycle { destroy = false } }` block) — are allowed; the vetted
plan is then applied exactly (no TOCTOU, no -auto-approve needed for a saved
plan).

Use case: safely apply state-only changes — e.g. forgetting adopted /
reverse-imported resources from state before a destroy — while failing loudly
if the same apply would also touch real infrastructure. Not tied to any
specific consumer; it's a generic terraform guard.

Parses with hashicorp/terraform-json (promoted from indirect to direct dep).

Tests: assertNoResourceChanges classification truth table (forget/no-op/read
allowed; create/update/delete/replace refused, naming the offenders) + a
Fake-runner flow test asserting the guarded apply refuses (no `terraform
apply`) on a resource change and proceeds to `apply <savedplan>` on a
state-only forget.
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​wrapt@​2.1.2100100100100100

View full report

@sam-at-luther sam-at-luther merged commit 05f0464 into main Jun 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant