terraform-aws-modules/terraform-aws-security-group examples/complete (tag v6.0.0), its flagship example
Source: https://github.com/terraform-aws-modules/terraform-aws-security-group.git at v6.0.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.
| Stage | Verdict | Duration | Detail |
|---|---|---|---|
| Cold deploy | pass | 22s | 67 resources (DELTA 2, lex00/floci#57) |
| Migrate | pass | 1m16s | 58 of 67 stamped, 67 identities recorded (#364 unit A2) |
| Replan from nothing | pass | 4s | the plan is genuinely empty: every choudoufu wall (#305, #307, #313 A and B, #321, #332) and both confirmed floci gaps (#102, #104) are fixed or absent this run; default route table identities asserted by value against the AWS CLI in step 3a |
| No-op apply | pass | 4s | no-op apply (0 added, 0 changed, 0 destroyed); tofu-estate-tagged object count unchanged at 58 objects, read through resourcegroupstaggingapi |
| Drift and reconverge | pass | 7s | one object tampered (DriftProbe tag on the main security group), exactly module.security_group.aws_security_group.this[0] proposed, apply changed 1 and the tag is gone, confirmed via the AWS CLI |
| Rename | pass | 14s | moved block: module.postgresql renamed to module.postgresql_renamed with zero churn (0 add, 4 change, 0 destroy) - the rule-children case, its own SG plus ingress/egress rules and rules_exclusive all moving under one moved block; live-mv: aws_security_group.app 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 block | pass | 11s | choudoufu: deleting module.postgresql_renamed’s block proposed exactly 5 destroys (0 add, 0 change, 5 destroy: SG + 2 ingress + 1 egress + 1 untaggable rules_exclusive), applied cleanly (0 added, 0 changed, 5 destroyed), the security group is genuinely gone from the live account (0 matches on describe-security-groups for the old id, read via the AWS CLI, not choudoufu’s own report), and the next plan proposes nothing; stock oracle on cold_deploy’s own state (D-ORACLE remove) also proposes exactly 5 destroys for the same 5 objects; classifyOrphans did not withhold the untaggable rules_exclusive destroy even though module.security_group’s and module.consul’s own rules_exclusive instances share its block key, because both surviving instances are bound, not unclaimed |
| Change count | pass | 28s | choudoufu: scaling aws_security_group.count_test from 2 to 1 destroyed exactly count_test[1] (0 add, 0 change, 1 destroy), leaving count_test[0]’s live GroupId (sg-388e5aceb94bd0d5d) and its tofu-address=aws_security_group.count_test:0 marker unchanged, both read through the AWS CLI; scaling back from 1 to 2 created exactly count_test[1] (1 add, 0 change, 0 destroy) under a NEW server-minted GroupId (sg-ccc8aef2f836b8abc, was sg-ceabc2389e1136146 - a security group id is never reused, so the destroy is directly observable rather than inferred) carrying tofu-address=aws_security_group.count_test:1, while count_test[0] kept both its id and its marker throughout; the local record store tracked the same facts by value at every step (index 0’s record never moved, index 1’s stopped naming the destroyed object and then named the new one); the next plan is empty. G-ORACLE is the stock leg for the identical shape, applied for real with plain terraform in the idle greenfield account: 3 created, then 0 add/0 change/1 destroy hitting count_test[1] only, then 1 add/0 change/0 destroy bringing it back under a new id, count_test[0]’s id unchanged both times - identical to choudoufu’s. SYNTHETIC BLOCK, and why: this estate declares no scalable knob of its own - every count in terraform-aws-security-group v6.0.0 is the boolean ’local.create ? 1 : 0’ toggle, and its real for_each maps (var.ingress_rules/var.egress_rules) cannot be scaled in isolation because main.tf line 96 feeds every rule id into aws_vpc_security_group_rules_exclusive.this[0], making the plan 0 add/1 change/1 destroy instead of the exact shape this stage asserts; aws_security_group.count_test is self-contained, named by nothing else here, and of a type this estate already exercises six times over (reference-ec2-vpc Part F and corpus-iam-policy Part G are the precedent). BREAK_COUNT=1 runs this stage’s Break control (expect count_test[0] to be the destroyed one) and correctly fails to hold. |
| Replace with create_before_destroy | pass | 11s | choudoufu: changing module.security_group’s ForceNew name argument proposed exactly one SG replace at the same declared address, cascading into its 7 ingress rules, 1 egress rule and 1 rules_exclusive enforcer (all replaced) - 10 to add, 10 to destroy, matching F-ORACLE’s own plan shape; applied cleanly; the old SG (sg-7d47321ffe396ce67) is confirmed gone and the new SG (sg-1aeeb0bfa4607e022) carries the marker, both via the AWS CLI; the local record store’s record at the same address now names the new SG, not the destroyed one; 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 security groups) 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. |
| Crash between create and destroy | not run | ||
| Teardown | not run | ||
| Plan, review, apply | pass | 18s | one argument edited (aws_security_group.app’s tags merge in Reviewed=yes; the standalone app SG has no rule children, so one argument is one row), “plan -out=approved.tfplan” wrote a 87593-byte stock-format plan file whose whole change set is one update on aws_security_group.app (sg-a7a28533562efa9a1); the world then moved out of band (a DriftProbe tag on sg-7d47321ffe396ce67 through the AWS CLI, never through choudoufu - STAGE 5’s own proven mutation, on a DIFFERENT instance and a DIFFERENT live security group 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.security_group.aws_security_group.this[0] and the live sg-7d47321ffe396ce67 it was computed against, with “Exit status 3” spelled out for a pipeline; nothing was applied - describe-tags on sg-a7a28533562efa9a1 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 DriftProbe tag deleted and nothing else changed, the IDENTICAL file applied - 0 added, 1 changed, 0 destroyed - and sg-a7a28533562efa9a1 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 app SG confirmed to be the same GroupId it started as, and the estate replanned empty, so PART F 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 apply | pass | 27s | 67 resources from nothing, all markers verified via the AWS CLI, 67 records in the local record store (#364 A2), replan empty, 6 tagged security groups (4 named + 2 default adopters) and every named one’s rule shape matches $PLAIN_EST’s own stage-1 apply object by object, tags stripped |
| 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 3m42.9s.
Oracle: stock terraform 1.16.1, stock tofu 1.12.6 (matches the current pin).
Landed c876435875 (2026-08-18), 67 real resources. The brief expected this crossing to hit #304 (a static lookup()-keyed count-index) directly, since a prior crossing found #304 through this same module as a dependency - checked, not assumed, and refuted: v6.0.0 rewrote the module from the classic single-aws_security_group-with-dynamic-blocks shape #304 lives in to a for_each-over-a-map shape emitting aws_vpc_security_group_ingress_rule/egress_rule per rule key, so that whole pattern is gone from this version’s own example. migrate genuinely passes (52 of 67 eligible and stamped: 35 VERIFIED + 17 DRIFTED; 15 skipped - 6 untaggable by design, 9 unadmitted). test_plan blocked by two real, distinct, filed gaps: #305 (6 sites, the familiar default_* adopter trio, doubled since this estate nests two terraform-aws-vpc calls) and a NEW one, filed as #307: aws_vpc_security_group_rules_exclusive is unadmitted (3 sites) - no CFN counterpart and the pinned provider ships no identity schema for it, but its own import docs are unambiguous that security_group_id (required, ForceNew, always a direct parent reference) is its whole identity, the same shape aws_vpc_security_group_vpc_association’s already-admitted row has. Also found and filed a real floci gap (lex00/floci#57: EC2 AssociateSecurityGroupVpc has no handler at all), worked around with a documented delta removing the estate’s one vpc_associations block (67 of 68 resources still stand up for real). One open, honestly-unresolved observation: 17 of the DRIFTED resources show referenced_security_group_id read back as “000000000000/sg-xxx” from floci where config computes a bare “sg-xxx” - doesn’t block stamping, but live-plan never got past #305/#307’s hard refusals to reveal whether the real provider’s diff-suppression absorbs this cleanly or would surface as an 18th change. Not filed separately; the next crossing attempt (once #305/#307 land) will show it for real. Follow-up pass 2026-08-19 (#313’s data-source fix, c636ab20f7/0284d8c408): re-verified for real against the current pin (67 cold-deployed, 58 stamped, state deleted). test_plan diagnostics dropped from 239 to 19 - #313’s canonical data.aws_availability_zones cause (50 sites) is fully gone, the module-output/resource-attribute variant (2 sites) still correctly refuses (out of scope by the maintainer’s own ruling), and the 187-site cascade collapsed to 5. The remaining 12 sites are a NEW, newly-reached class (previously masked behind #313’s hard refusal): element(
Reproduce it#
go run ./tools/gauntlet run corpus-security-group-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-security-group-complete/run.sh; BREAK=1 corrupts its assertions to show they are load-bearing.