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 headline stage passes.

StageVerdictDurationDetail
Cold deploypass56s6 resources added by plain terraform (4 queues + redrive_policy + redrive_allow_policy), 0 objects carry tofu-estate before migration
Migratepass2m1s4 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 nothingpass3sno resource change proposed, no foreign resources; fifo and default queue tofu-address re-checked against SQS
No-op applypass3sgenuine no-op (0 added, 0 changed, 0 destroyed); 4 objects before, 4 after, no state file
Drift and reconvergepass5sone object tampered, exactly 1 object proposed and applied (0 added, 1 changed, 0 destroyed), tag reconverged to “ex-complete”
Renamepass9smoved 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 blockpass49schoudoufu: 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 countpass1m55ssynthetic block (all four module calls declare count = var.create ? 1 : 0, a boolean create toggle, so the estate has no knob that scales - issue #488’s sanctioned fallback, reusing aws_sqs_queue, a type this estate already exercises four times): scaling aws_sqs_queue.count_test from 2 to 1 proposed and applied exactly one destroy (0 add, 0 change, 1 destroy) of the HIGHER index, count_test[1]; the survivor count_test[0] kept its live queue URL (https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-count-test-0), its CreatedTimestamp (1788905466) and its tofu-address=aws_sqs_queue.count_test:0 / tofu-slot=0 markers, all read back through the AWS CLI, and count_test[1]’s local record was tombstoned rather than left naming a destroyed queue; scaling 1 back to 2 proposed and applied exactly one create (1 add, 0 change, 0 destroy), and because a queue URL is rebuilt from region + account + name the recreated instance comes back at the SAME url - so the destroy is witnessed two other ways instead, by AWS.SimpleQueueService.NonExistentQueue in between and by a strictly later CreatedTimestamp (1788905466 -> 1788905548), with tofu-address=aws_sqs_queue.count_test:1 back on the new object and index 0 untouched throughout; the next plan proposes no resource action; the G-ORACLE stock oracle stood the identical block up for real in the idle greenfield-oracle account and showed the identical shape - destroy the higher index only, create it back under the same url with a new CreatedTimestamp, the lower index unchanged both times
Replace with create_before_destroypass1m15schoudoufu: 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 destroynot run
Teardownnot run
Plan, review, applypass14sone argument edited (module.unencrypted_sqs’s tags gain Reviewed=yes; that module call declares no DLQ, so var.tags reaches exactly one resource instance), “plan -out=approved.tfplan” wrote a 26735-byte stock-format plan file whose whole change set is one update on module.unencrypted_sqs.aws_sqs_queue.this[0] (https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-unencrypted); the world then moved out of band (https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-default's Example tag, through the AWS CLI, never through choudoufu - STAGE 5’s own proven mutation, on a DIFFERENT instance and a DIFFERENT live queue from the one under review) and “apply approved.tfplan” refused with “The approved plan no longer matches the live system” at exit 3, classifying the drift under “This apply would do, and the approved plan does not include:” and naming both module.default_sqs.aws_sqs_queue.this[0] and the live https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-default it was computed against (an aws_sqs_queue’s identity IS its URL), with “Exit status 3” spelled out for a pipeline; nothing was applied - list-queue-tags on https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-unencrypted still returned no Reviewed tag, read back through the AWS CLI rather than from the absence of an “Apply complete!” line. Inverted control on the same run (the shape live/smoke/scenarios/apply-what-was-approved.sh reasons out): with the Example tag put back and nothing else changed, the IDENTICAL file applied - 0 added, 1 changed, 0 destroyed - and https://sqs.eu-west-1.amazonaws.com/000000000000/ex-complete-unencrypted read back with Reviewed=yes, so the refusal is earned by the drift and not handed out to every plan file. The edit was then reverted, re-applied, the unencrypted queue’s URL confirmed unchanged and the estate replanned empty with no state file, so PART D starts where it would have. BREAK_APPROVAL=1 asserts stage 12’s own recorded Break line (apply the planfile after a mutation and expect success) and correctly fails
Greenfield applypass3m6s6 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 (not a headline stage)not run

Last run at commit 933618dec4 on 2026-09-08T22:17:50Z, exit code 0, against emulator image ghcr.io/lex00/floci@sha256:a39185cc3971d0188663d61043cb038dff1260d8a975b1aa72c4e2bb1feac3cb. Stale: the current pin is ghcr.io/lex00/floci@sha256:d9207de14c919f4bfa50e956376cc441970f3679aabfdd43f3dbf4b779b20805. Total run time 10m36.6s. Oracle: stock terraform 1.16.1, stock tofu 1.12.6 (matches the current pin).

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.