terraform-aws-modules/terraform-aws-ec2-instance examples/complete (tag v6.4.0), the most-downloaded module on the registry; absent from the measurement set until 2026-08-23

Source: https://github.com/terraform-aws-modules/terraform-aws-ec2-instance.git at v6.4.0.

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 deploypass55s35 resources added across 13 types (aws_instance, aws_eip, aws_iam_role/instance_profile/role_policy_attachment, aws_ebs_volume, aws_volume_attachment, aws_security_group x2, aws_vpc_security_group_egress_rule x2, aws_security_group_rule x2, vpc/subnet/route*/igw/default_* from the vpc module), 0 objects carry tofu-estate before migration
Migratepass31s24 of 35 eligible (11 untaggable across 5 types - aws_iam_role_policy_attachment, aws_volume_attachment, aws_security_group_rule x2, aws_route, aws_route_table_association x6 - all resolved by provider identity schema), 24 stamped, 0 failed, 11 skipped; the IAM role policy attachment’s composite live id asserted by value; genuine no-op on the follow-up apply
Replan from nothingpass5sno resource change proposed by either plan; the default plan reports “nothing was swept” (the CollectUnclaimed ruling (#604) made the account-inventory question opt-in, and a run that did not ask must say so), and a second plan run with TOFU_LIVE_COLLECT_UNCLAIMED=1 finds exactly 8 foreign objects - the instance’s own root volume plus floci’s default-VPC bootstrap; instance tofu-address re-checked against EC2
No-op applypass3sgenuine no-op (0 added, 0 changed, 0 destroyed); 24 objects before, 24 after, no state file
Drift and reconvergepass7sone object tampered, exactly 1 object proposed and applied (0 added, 1 changed, 0 destroyed), tag reconverged to “ex-complete”
Renamepass15smoved block: module.vpc renamed with zero churn (0 add, 15 change, 0 destroy), marker rewritten in place; live-mv: module.security_group’s security group renamed with zero churn, its two untaggable rules followed for free; 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 blockpass29schoudoufu: deleting module.ec2_complete’s block proposed exactly 10 destroys (0 add, 0 change, 10 destroy), matching the stock oracle’s own count and applied cleanly; the instance is confirmed terminated and the tagged object count dropped, both via the AWS CLI, not through choudoufu’s own report; the next plan proposes no resource action; stock oracle on cold_deploy’s own state (D-REMOVE-ORACLE) also proposes exactly 10 destroys for the same module
Change countpass2m9schoudoufu: scaling aws_ebs_volume.count_test from 2 to 1 destroyed exactly count_test[1] (vol-67c8a86b27a31c7ea, 0 add, 0 change, 1 destroy) and left count_test[0] (vol-f0629348c57d128f1) with the same live VolumeId, the same tofu-address=aws_ebs_volume.count_test:0 and the same tofu-slot=0, all read back through the AWS CLI rather than choudoufu’s own report; the destroyed volume is genuinely gone (describe-volumes answers InvalidVolume.NotFound for it). Scaling back from 1 to 2 planned exactly 1 to add, 0 to change, 0 to destroy and brought count_test[1] back as a NEW object (vol-3f7f56fe6a4f9e68a, not vol-67c8a86b27a31c7ea) carrying tofu-address=aws_ebs_volume.count_test:1 and tofu-slot=1, above the live high-water mark count_test[0] still holds, while count_test[0] stayed untouched throughout; the next plan is empty, and scaling the block to zero destroys both and leaves the estate planning empty again. C-ORACLE, the same 2-instance block stood up for real with plain terraform in its own working directory at the SAME resolved provider version (6.63.0), shows the identical shape: destroy the higher index only (vol-7cfc2d40f129f4472), create the higher index back under a new id (vol-590e8a99ec35af6e0), the lower index’s id (vol-21c550824c2c57510) unchanged both times. SYNTHETIC BLOCK, and why: terraform-aws-ec2-instance v6.4.0 declares no scalable count or for_each knob this estate reaches - all nine of its own count usages are boolean create toggles of the form ‘count = local.create ? 1 : 0’, which can never hold two instances, and the upstream example’s one real for_each fan-out (module.ec2_multiple) is dropped by this script’s reduction because floci does not model the surfaces around it - so this section adds a new, self-contained count block of a type the estate ALREADY exercises (aws_ebs_volume, module.ec2_complete’s own /dev/sdf data volume), the sanctioned fallback live/GAUNTLET.md #8 names, with reference-ec2-vpc Part F and corpus-iam-policy Part G as precedent. BREAK_COUNT=1 asserts the WRONG instance (count_test[0]) was destroyed and reports day2_count fail, proving the assertion is load-bearing.
Replace with create_before_destroypass49schoudoufu: changing module.ec2_complete’s ForceNew ami argument proposed exactly one instance replace at the same declared address, cascading into the eip (updated in-place) and the volume attachment (also replaced, instance_id is ForceNew there too) - 2 to add, 1 to change, 2 to destroy, matching F-ORACLE’s own plan shape; applied cleanly; the old instance is confirmed terminated and the new instance carries the marker, both via the AWS CLI; the local record store’s record at the same address now names the new instance’s id, not the terminated one (i-9fe92adc44cecd245 -> i-b3db2aaa84ac422cc); the next plan proposes no resource action; BREAK=replace confirms a manufactured marker collision is reported loudly (“Indistinguishable instances without per-instance markers”, naming both live instances) rather than silently proposed as nothing - internal/live/discovery/supersededclaimant.go (#849) tombstones only what an apply actually destroyed, so a live duplicate with no tombstone is never pruned away. 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 and corpus-sqs-basic’s matching one.
Crash between create and destroynot run
Teardownnot run
Plan, review, applypass16sone argument edited (the “/dev/sdf” entry’s MountPoint volume tag inside module “ec2_complete”’s ebs_volumes argument, /mnt/data -> /mnt/data-reviewed - the module merges each entry’s tags into that entry’s aws_ebs_volume alone, so it reaches module.ec2_complete.aws_ebs_volume.this["/dev/sdf"] and nothing else), “plan -out=approved.tfplan” wrote a 72488-byte stock-format plan file whose whole change set is that one update; the world then moved out of band (i-9fe92adc44cecd245’s Example tag, through the AWS CLI, never through choudoufu - the same mutation STAGE 5 uses) 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.ec2_complete.aws_instance.this[0] and the live i-9fe92adc44cecd245 it was computed against, with “Exit status 3” spelled out for a pipeline; nothing was applied - volume vol-89225b2660b986979 still read MountPoint=/mnt/data through ec2 describe-tags, not 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 i-9fe92adc44cecd245’s tag put back and nothing else changed, the IDENTICAL file applied - 0 added, 1 changed, 0 destroyed - and vol-89225b2660b986979 read back with MountPoint=/mnt/data-reviewed, so the refusal is earned by the drift and not handed out to every plan file. The edit was then reverted, re-applied and the estate replanned empty, so PART C 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 applypass1m0s35 resources from nothing, matching stock’s own cold-deploy count; the instance’s markers verified via the AWS CLI; 35 records in the local record store including untaggable types; replan empty; the instance’s own shape (type/ami/block-device-count) matches stock’s cold deploy, via the AWS CLI on both endpoints, marker tags never compared; 24 objects carry the estate tag
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 6m39.2s. Oracle: stock terraform 1.16.1, stock tofu 1.12.6 (matches the current pin).

Reproduce it#

go run ./tools/gauntlet run corpus-ec2-instance-complete

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-ec2-instance-complete/run.sh; BREAK=1 corrupts its assertions to show they are load-bearing.