terraform-aws-modules/terraform-aws-s3-bucket examples/complete (tag v5.9.1), its flagship example
Source: https://github.com/terraform-aws-modules/terraform-aws-s3-bucket.git at v5.9.1.
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
Not clear yet.
| Stage | Verdict | Detail |
|---|---|---|
| Cold deploy | pass | 30 resources added by plain terraform, 4 buckets confirmed live, no tofu-address tag |
| Migrate | pass | 6 of 30 stamped, 1 recorded (random_pet, issue #340), 23 skipped (untaggable), 0 failed, 26 identities recorded (#364 unit A2); markers survived the residue-classification apply |
| Replan from nothing | pass | no resource action proposed; 29 rendered identity occurrences (11 distinct), all naming known roots |
| No-op apply | pass | no-op apply (0 added, 0 changed, 0 destroyed); bucket count unchanged at 4 |
| Drift and reconverge | pass | accelerate config drifted to Enabled, exactly 1 change proposed and applied, reconverged to Suspended, final plan empty |
| Rename | pass | moved block: module.cloudfront_log_bucket renamed to module.cloudfront_log_bucket_renamed with zero churn (0 add, 1 change, 0 destroy), the bucket’s tofu-address marker rewritten in place; live-mv: module.simple_bucket renamed to module.simple_bucket_renamed with zero churn, marker rewritten in place; both live bucket names unchanged, read via the AWS CLI; the post-rename plan proposes no resource action |
| Remove a block | pass | choudoufu: deleting module.simple_bucket_renamed’s block proposed exactly two destroys (0 add, 0 change, 2 destroy: the bucket and its untaggable public_access_block child), applied cleanly (0 added, 0 changed, 2 destroyed), the bucket is genuinely gone from the live account (head-bucket on simple-welcomed-gnu now fails, 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 also proposes exactly the same two destroys for the same two objects; the target was chosen to avoid issue #404’s shape (a sibling policy re-reading the removed bucket’s own ARN) - module.log_bucket and module.s3_bucket are both left untouched |
| Change count (planned) | not run | |
| Replace with create_before_destroy | not run | |
| Crash between create and destroy (planned) | not run | |
| Teardown (planned) | not run | |
| Plan, review, apply (planned) | not run | |
| Greenfield apply | pass | 29 resources from nothing (SCOPE REDUCTION’s own reduced count, random_pet pinned to a literal on both sides), 3 of 4 bucket markers verified via the AWS CLI, 26 records in the local record store (#364 A2), replan empty, stock oracle in its own namespace matches structurally on all 4 buckets (versioning, default encryption, policy presence) |
| Strict profile (planned) | not run |
Last run at commit fa5a6bd3bd on 2026-08-25T17:34:52Z, exit code 0.
Landed 1398574b03 (2026-08-18); re-verified for real 2026-08-18 in an isolated worktree against local main bcf78bacbd, full pipeline reproduced clean twice in a row. #306 is fixed (lex00/floci S3ControlController.tagResource now reads-merges-writes; reconciled into the currently-pinned floci image; internal/live/lifecycle/marker_tag_merge_live_test.go’s TestMarkerSurvivesIncrementalTagUpdate passes live against it). DELTA 6 (the tags-removal workaround) reverted - module.s3_bucket’s real tags = { Owner = “Anton” } is back, unmodified, and the stage 2c residue-classification apply leaves all four buckets’ markers intact with it restored. DELTA 5 (expected_bucket_owner) is UNCHANGED, for a reason independent of #306: it is ForceNew on most of these sub-resource types with no live representation at all (a request-header assertion, not a stored property), so a discovery-rebuilt prior would force a real replace on the very first onboarding apply - the same shape as DELTA 3’s random_pet. The prior investigation’s other open question - module.s3_bucket’s canned acl and website.routing_rules - was checked for real rather than assumed fixed, and DOES still block test_plan after #306’s fix, confirmed reproducibly. Traced past “the provider’s Read() needs a genuinely-remembered prior” (the prior investigation’s framing, which attributed this to #306 too - a conflation this session’s trace evidence rules out, since the mechanism is unrelated to tags/floci entirely): issue #275’s residue mechanism (internal/live/projection/residue.go) DOES run here and DOES try to classify acl - TF_LOG=trace on the stage 2c apply shows ‘residue candidate “acl”: readA=cty.StringVal(“private”) readB=cty.StringVal(“private”) applied=cty.StringVal(“private”)’, and classifyResidue’s own documented rule reads that as “the provider answers this from the remote”, so nothing is recorded as residue. Yet the SAME attribute, read moments later by internal/live/projection/build.go’s importAndRead (the function stage 3’s plan actually uses), comes back empty and proposes ‘+ acl = “private”’. The two reads disagree because their prior is built two different ways: residue.go’s identityOnly nulls every non-identity attribute outright, while importAndRead’s prior is whatever provider.ImportResourceState() returned - a zero-value stub (acl = “”, not null) for this SDKv2 resource. The provider’s Read() treats an explicit null differently from an SDKv2 zero-value string for this attribute; reconciling the two prior constructions is real work but importAndRead is the read path every projected resource in this fork goes through, so a fix needs corpus-wide validation, not a one-estate patch - out of scope here. SCOPED OUT instead (module.s3_bucket’s acl and website inputs removed, applied to BOTH the cold-deploy and choudoufu copies so the crossing tests one genuinely-reduced estate throughout, not choudoufu silently abandoning management of something the cold deploy still created) - same discipline corpus-sumaform-aws uses for its own two structural refusals. Estate is now 30 instances across 14 aws_s3_bucket_* types (down from 32/15; aws_s3_bucket_acl survives elsewhere via module.cloudfront_log_bucket’s own grant/owner-driven ACL). Also hit and fixed IN THIS SCRIPT (no choudoufu/floci change needed): THE OUTPUTS QUIRK already documented in corpus-iam-read-only-policy and corpus-root-dns-zones - module.s3_bucket’s outputs, re-exported at root, have no prior baseline across a stateless live-plan, so every run shows a permanent ‘Changes to Outputs’ section and OpenTofu’s renderer never prints a ‘Plan: N to add…’ line while that is true, empty of resource changes or not. Stage 3/5’s empty-plan assertions now check for the absence of a resource action header (‘will be (created|updated|destroyed)’) instead of grepping for ‘No changes.’/‘Plan: 0 to add…’, matching those two scripts’ own pattern; this was never exercised before since stage 3 had never previously reached an actually-empty-of-resource-changes plan. Also fixed the rendered-import-identity count check (N_IDS), which had silently never been exercised either: several of module.s3_bucket’s own sub-resource types (ownership_controls, versioning, policy, logging, cors_configuration, lifecycle_configuration, object_lock_configuration, public_access_block, accelerate_configuration, request_payment_configuration) import by the bucket’s own id alone, so after sort -u only 10 distinct strings remain for 30 instances - the raw (pre-dedup) occurrence count (29) is what actually approximates “one per instance” and is now what the sanity floor checks; the deduplicated set is still what ids_all_name_known_roots validates.
Reproduce it#
go run ./tools/gauntlet run corpus-s3-bucket-completeNeeds 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-s3-bucket-complete/run.sh; BREAK=1 corrupts its assertions to show they are load-bearing.