terraform-aws-modules/terraform-aws-sqs examples/complete (tag v5.2.2, commit dd73a96c0155bc324dda5256f3e7a9ea2c710195), reduced to four self-contained module calls per the script’s header

Source: https://github.com/terraform-aws-modules/terraform-aws-sqs.git at v5.2.2.

Set: core. Lane: terraform-popular.

Why it is in the core set: a most-downloaded terraform-aws-modules example, pinned by tag; the shape most people deploy

Clear. Every active stage passes.

StageVerdictDetail
Cold deploypass6 resources added by plain terraform (4 queues + redrive_policy + redrive_allow_policy), 0 objects carry tofu-estate before migration
Migratepass4 of 6 eligible (2 untaggable redrive types resolved by provider identity schema), 4 stamped, 0 failed, 2 skipped; tofu-slot=0 written on all 4 queues by the stamp itself (issue #372’s remainder), confirmed by value and by a genuine no-op on the follow-up apply
Replan from nothingpassno resource change proposed, no foreign resources; fifo and default queue tofu-address re-checked against SQS
No-op applypassgenuine no-op (0 added, 0 changed, 0 destroyed); 4 objects before, 4 after, no state file
Drift and reconvergepassone object tampered, exactly 1 object proposed and applied (0 added, 1 changed, 0 destroyed), tag reconverged to “ex-complete”
Renamepassmoved block: module.default_sqs renamed with zero churn (0 add, 1 change, 0 destroy), marker rewritten in place; live-mv: module.unencrypted_sqs renamed with zero churn, marker rewritten in place; stock oracle over the same two-object rename on cold_deploy’s own state also shows zero churn (0 add, 0 change, 0 destroy); both live ids unchanged, read via the AWS CLI
Remove a blockpasschoudoufu: deleting module.unencrypted_sqs_renamed’s block proposed exactly one destroy (0 add, 0 change, 1 destroy), applied cleanly (0 added, 0 changed, 1 destroyed), the object is genuinely gone from the live account (sqs get-queue-url on the old name now returns NonExistentQueue, read via the AWS CLI, not choudoufu’s own report), and the next plan proposes no resource action; stock oracle on cold_deploy’s own state (E-ORACLE) also proposes exactly one destroy for the same object (before any rename ever touched it)
Change count (planned)not run
Replace with create_before_destroypasschoudoufu: changing module.default_sqs_renamed’s ForceNew name argument proposed exactly one replace at the same declared address (1 add, 0 change, 1 destroy; -/+ destroy and then create), applied cleanly; the old object (https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-default) is confirmed gone and the new object (https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-default-v2) carries the marker, both via the AWS CLI; the local record store’s record at the same address now names the new object’s import_id, not the destroyed one (https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-default -> https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-default-v2); the next plan proposes no resource action; stock oracle on cold_deploy’s own state (F-ORACLE) also proposes exactly one replace at the same address (plan only, not applied - it shares floci’s account with $EST); BREAK=replace confirms a manufactured marker collision is reported loudly rather than silently proposed as nothing. Scope note: this exercises OpenTofu’s default destroy-then-create ordering, not the create_before_destroy variant the stage’s Title names - see this section’s own header comment.
Crash between create and destroy (planned)not run
Teardown (planned)not run
Plan, review, apply (planned)not run
Greenfield applypass6 resources from nothing (4 tagged queues + 2 untaggable redrive types), all markers verified via the AWS CLI, 6 records in the local record store (#364 A2), replan empty, stock oracle in its own namespace matches structurally on all 4 queues
Strict profile (planned)not run

Last run at commit 6157e0b4fe on 2026-08-25T18:32:23Z, exit code 0.

First real five-stage crossing of this estate, and the first SQS surface in this corpus. Sourced and sketched at e4b12799da with every assertion past stage 1’s terraform apply DERIVED from reading terraform-aws-sqs’s naming locals rather than measured - that commit’s own header said so and deliberately added no entry here. This entry is the first one written from a real run: Docker/floci (ghcr.io/lex00/floci@sha256:8a882bcc, live/floci-image’s pin), real hashicorp terraform, and the AWS CLI throughout, in worktree ../wt/new-terraform-estate-2 off e4b12799da. All five stages PASS. WHAT THE DERIVATION GOT WRONG, and it was exactly one thing: the resource count. The estate builds SIX managed resources, not five. create_dlq = true makes the module emit an aws_sqs_queue_redrive_ALLOW_policy on the DLQ alongside the aws_sqs_queue_redrive_policy on the source queue; reading the naming locals found the second and missed the first. Everything else the sketch derived was right when checked against reality - all four queue names and URLs including the FIFO DLQ’s “-dlq.fifo” suffix, and all four rendered tofu-address strings. Corrected counts, measured: stage 1 “Apply complete! Resources: 6 added”, stage 2 “4 of 6 resource instance(s) are eligible for stamping” and “4 resource(s) newly stamped, 0 already stamped, 0 newly recorded, 0 already recorded, 0 failed, 2 skipped”. THE SCHEMA-FALLBACK RESULT, which is why this estate was sourced. Neither aws_sqs_queue_redrive_policy nor aws_sqs_queue_redrive_allow_policy has a row in internal/live/identity/table_generated.go; live/survey-full.json classifies both identically (path “client-named”, admission “schema”, required_for_import [“queue_url”], taggable false, list_resource false). Both resolved a live id through the provider’s own identity schema, and both resolved to the RIGHT queue, which is not the same queue for the two of them: redrive_policy -> https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete.fifo (the source queue), redrive_allow_policy -> https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-dlq.fifo (the DLQ). The schema-fallback path works on both, unmodified - no fix was needed and none was made. The script now asserts both by value, because a run that merely did not error would pass with the two swapped. Both are UNTAGGABLE (no tags argument in the provider schema), so live-import skips them and stage 2 stamps 4 of 6. That is the invariant working, not a shortfall: four tagged queues plus two resources whose entire identity IS a tagged queue’s URL - tagged, plus derived-from-tagged, no third bucket - and stage 3’s empty plan with no state file anywhere is what proves the derivation holds. EXPECTED IMPORT-TIME DRIFT, recorded so the next reader does not chase it: live-import reports the two FIFO queues as DRIFTED rather than VERIFIED, on redrive_policy and redrive_allow_policy respectively (cold state has “”, live has the JSON). That is the module’s own design - the queue resource does not manage those attributes, the separate redrive resources do - so the live object carries a value the queue’s state row never recorded. DRIFTED is still eligible for stamping, the convergence apply reconciles it, and the next plan is empty. The tofu-slot convergence apply corpus-iam-policy documented recurs here exactly as that entry predicts: all four aws_sqs_queue resources declare count = var.create ? 1 : 0, so one ordinary choudoufu apply (“0 added, 4 changed, 0 destroyed”) is folded into stage 2 before stage 3 is attempted. The two redrive resources are untaggable and carry no slot, which is why it is 4 changed and not 6. Stages 3-5 measured: test_plan proposes no resource action, reports “Foreign resources: none among the 1 type swept”, both re-read identities unchanged, no state file written; test_apply “0 added, 0 changed, 0 destroyed” with the tofu-estate-tagged object count 4 before and 4 after; drift_reconverge tampers ex-complete-default’s Example tag directly through the AWS CLI, live-plan proposes updating exactly module.default_sqs.aws_sqs_queue.this[0] and nothing else, and the apply reconverges it (“0 added, 1 changed, 0 destroyed”, tag back to “ex-complete”). THREE SELF-AUTHORED DEFECTS FIXED IN THE SCRIPT WHILE VERIFYING IT, each found by running it rather than reading it. (1) No TF_PLUGIN_CACHE_DIR, which corpus-lambda-simple and corpus-alb-complete both set. Without it the first real run spent 21 minutes in terraform init having pulled 48MB of hashicorp/aws and then died on a transient DNS failure before ever reaching terraform apply - the likely reason two earlier sessions reported this crossing as stalled rather than failed. With the shared cache the whole five-stage run is minutes. (2) The BREAK contract was unreachable. BREAK=1 set two corruptions, stage 3’s and stage 5’s, but the stage-3 one calls fail() and exits, so stage 5’s branch was dead code that had never run - and its inverted form would have exited 0 on a corrupted run anyway, proving nothing. BREAK is now three named values corrupting three different assertions, each verified for real to exit 1 at its own assertion and each reaching a later stage than the last: BREAK=schema (swap the two expected redrive URLs - both real queues, both types really do resolve, so only a by-value check catches the wrong pairing) fails in stage 2; BREAK=identity fails in stage 3; BREAK=drift fails in stage 5’s exactly-one-object assertion with both objects named. An unrecognized BREAK value is rejected up front. This same dead-stage-5-branch shape exists in corpus-iam-policy’s script, which this one was copied from - worth a slot there, not touched here. (3) A new managed-shape assertion added in this pass (terraform state list compared by name against the six documented addresses, so a moved corpus pin fails loudly instead of silently crossing a different estate - the exact failure mode that produced the wrong count) first failed on locale collation alone: “.” and “_” sort differently under a UTF-8 locale, so a hand-ordered list never matches a locale-sorted one. Both sides now go through LC_ALL=C sort. Caught because the assertion was run, not reviewed.

Reproduce it#

go run ./tools/gauntlet run corpus-sqs-basic

Needs Docker (the emulator is pulled at the pinned digest), the AWS CLI, and a stock terraform or tofu binary on PATH for the cold deploy. The script is live/e2e/corpus-sqs-basic/run.sh; BREAK=1 corrupts its assertions to show they are load-bearing.