terraform-aws-modules/terraform-aws-vpc examples/complete (tag v6.6.1), its flagship example

Source: https://github.com/terraform-aws-modules/terraform-aws-vpc.git at v6.6.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.

StageVerdictDetail
Cold deploypassApply complete! Resources: 62 added, 0 changed, 0 destroyed.; 0 objects carry tofu-estate=vpc-complete-crossing before migration
Migratepass40 stamped, 22 skipped, 0 recorded, 0 failed; 39 objects carry tofu-estate=vpc-complete-crossing; the VPC’s tofu-slot reads 0 off EC2, written by the migration itself (choudoufu #372)
Replan from nothingpassempty plan; identity re-check unchanged: module.vpc.aws_vpc.this:0, aws_security_group.rds, module.vpc_endpoints.aws_vpc_endpoint.this:s3
No-op applypassgenuine no-op: 39 objects before, 39 after, no state file either time
Drift and reconvergepassone subnet tampered (Example tag), plan proposed fixing exactly one object, apply changed 1 and reconverged the tag to ex-complete
Renamepassmoved block: module.vpc_endpoints renamed with zero churn (0 add, 7 change, 0 destroy), marker rewritten in place across its taggable objects; live-mv: aws_security_group.rds 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 the dynamodb endpoint’s map entry (module.vpc_endpoints_renamed.aws_vpc_endpoint.this[“dynamodb”]) proposed exactly one destroy (0 add, 0 change, 1 destroy), applied cleanly (0 added, 0 changed, 1 destroyed), the endpoint is genuinely gone from the live account (State=absent, 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
Change count (planned)not run
Replace with create_before_destroynot run
Crash between create and destroy (planned)not run
Teardown (planned)not run
Plan, review, apply (planned)not run
Greenfield applypass62 resources from nothing (40 tag-stamped, 22 untaggable/derived), replan empty, stock oracle in its own namespace matches on vpc cidr, subnet count (18) and the s3 endpoint’s presence
Strict profile (planned)not run

Last run at commit 8195358eaf on 2026-08-25T17:42:40Z, exit code 0.

RE-CROSSED FOR REAL 2026-08-21 in worktree live/dhcp-options-355 off local main 41f8c8dd6a, real Docker/floci/terraform/AWS CLI throughout, floci ghcr.io/lex00/floci@sha256:cdd50ec0. STAGES UNCHANGED at 2 of 5, and the honest headline is that #355’s wall IS gone and three more stand behind it, none of them a choudoufu defect. Stage 1: ‘Apply complete! Resources: 62 added, 0 changed, 0 destroyed’, 0 objects carrying tofu-estate beforehand. Stage 2: dry run ‘40 of 62 eligible, 22 untaggable, 0 unadmitted’; -approve ‘40 stamped, 22 skipped, 0 recorded, 0 failed’; three identities asserted by value through the AWS CLI, all three passed. STAGE 3 NO LONGER REFUSES IN DISCOVERY: with #355 fixed, live-plan exits 0 and renders a full plan for the first time in this estate’s history. Proven by A/B against the SAME live migrated estate, same floci container, two binaries: main (41f8c8dd6a) exits 1 with ‘Error: Listed resource with no tags - Cloud Control listed a aws_vpc_dhcp_options (AWS::EC2::DHCPOptions) with no Tags in its Properties … Live resources: dopt-default’; the fixed binary exits 0 with that diagnostic absent and every other diagnostic identical. WHAT STANDS BEHIND IT, all measured in that run and none of them choudoufu’s: (1) the first live-plan after live-import proposes adding tofu-slot to 31 objects. That is documented, deliberate product behavior (see live/e2e/corpus-iam-policy/run.sh’s THE TOFU-SLOT FINDING; live-import cannot compute a slot from one state file), and three other crossing scripts fold a convergence apply into stage 2. This script does not, because it had never reached stage 3 to notice. (2) that convergence apply fails on a floci gap: ‘UnsupportedOperation: Operation ModifyVpcEndpoint is not supported’, 4 errors, one per interface VPC endpoint. 26 of the 31 tofu-slot writes do land. (3) the replan after that shows ‘Plan: 3 to add, 5 to change, 3 to destroy’ - three FORCED REPLACEMENTS from floci read fidelity, not drift: aws_nat_gateway.this[0] (floci’s DescribeNatGateways returns neither allocation_id nor subnet_id, so both read as absent and force replacement), aws_vpn_gateway.this[0] (floci returns availability_zone=‘eu-west-1a’ on a gateway whose config sets none, so the plan reads ‘- availability_zone -> null # forces replacement’), and aws_vpc_endpoint.this[s3], plus four endpoint in-place diffs (policy, route_table_ids, subnet_ids, cidr_blocks all read back empty). All floci work items, not choudoufu ones, and all four filed together as lex00/floci#97 (which also carries the Redshift Tagging-API gap below). THE #355 LOOSE END IS SETTLED, with evidence: the ‘39 objects carry tofu-estate’ line against the ‘40 stamped’ line is a floci Tagging-API coverage gap, not a choudoufu miscount. The 40th object is aws_redshift_subnet_group.redshift[0]; ‘aws redshift describe-cluster-subnet-groups’ shows it carrying tofu-address=module.vpc.aws_redshift_subnet_group.redshift:0 and tofu-estate=vpc-complete-crossing, while ‘resourcegroupstaggingapi get-resources’ for the same estate returns 39 ARNs with no Redshift among them. choudoufu’s own stamp count is the correct one. ONE PRIOR FIGURE CORRECTED: the note below records ’nine non-fatal Incomplete sweep for undeclared resources warnings’. The real number is 989, identical on both binaries - it is the tag sweep’s ARN-join-table coverage list (internal/live/discovery/tagging.go), produced before the type scans and untouched by this fix. Nine was a sample, not a count. test_apply and drift_reconverge stay not_run: stage 3 still ends in FAIL, so running them would prove nothing. PRIOR HISTORY BELOW. RE-CROSSED FOR REAL 2026-08-21 at dbc5ebf575 (GitHub issue #346’s fix), worktree live/live-read-346, real Docker/floci/terraform/AWS CLI throughout. STAGES UNCHANGED at 2 of 5 - and that is the honest headline, because #346’s own diagnostic IS gone and a different wall stands behind it. Stage 1: ‘Apply complete! Resources: 62 added, 0 changed, 0 destroyed’, 0 objects carrying tofu-estate beforehand. Stage 2: dry run ‘40 of 62 eligible, 22 untaggable, 0 unadmitted’; -approve ‘40 stamped, 22 skipped, 0 recorded, 0 failed’; three identities asserted by value straight through the AWS CLI and all three passed (vpc-68ae42e6 = module.vpc.aws_vpc.this:0, sg-1bac9cc2a99da4e8c = aws_security_group.rds, vpce-f81456900c35660a0 = module.vpc_endpoints.aws_vpc_endpoint.this:s3). Stage 3 no longer refuses in identity resolution at all: the #346 diagnostic (cidr_blocks = lookup(each.value, ‘cidr_blocks’, null) reaching module.vpc.vpc_cidr_block) appears nowhere in the run, and the run gets past live_plan.go’s step 4 (identity, fatal on error) into step 5 (discovery), which it could not have done otherwise. It now fails on exactly ONE blocking diagnostic, a NEW one that was hidden behind #346 one stage earlier: ‘Listed resource with no tags - Cloud Control listed a aws_vpc_dhcp_options (AWS::EC2::DHCPOptions) with no Tags in its Properties, and refining it with GetResource found none either, so its ownership markers cannot be read. Live resources: dopt-default.’ That is the ACCOUNT’S DEFAULT DHCP options set, which this estate did not create and does not declare. Filed separately. Nine non-fatal ‘Incomplete sweep for undeclared resources’ warnings also print (aws_xray_* x5, kubernetes_* x4), none of them a type this estate declares. One unexplained figure worth someone’s hour: the run’s own closing line reads ‘39 objects carry tofu-estate after migration’ against the ‘40 stamped’ line above it - the script does not assert it, so nothing failed, but the two disagree. test_apply and drift_reconverge stay not_run: running them against a refused plan would prove nothing. PRIOR HISTORY BELOW. Re-verified 2026-08-18 against ghcr.io/lex00/floci@sha256:f5b46236c6b6fff376ae2db8a2b3a51bf1d13b19a92b6a37af4827ccdf1ef180, published via floci’s own CI/GHCR-publish workflows (pushed to origin/main, not a local multi-arch build - see HANDOFF.md’s Traps). lex00/floci#66 (Redshift), #67 (DHCP options) and #69 (customer/VPN gateway) are confirmed fixed: cold deploy no longer errors on any of the three. It now fails one step later, on a fourth, narrower and previously-undetected gap in #68’s own CreateCacheSubnetGroup/ModifyCacheSubnetGroup implementation - it reads the SubnetIds member list under the generic SubnetIds.member.N key, but ElastiCache’s service model overrides SubnetIdentifierList’s member locationName to SubnetIdentifier, so every real client sends SubnetIds.SubnetIdentifier.N and the call 400s with MissingParameter. Filed as lex00/floci#70. A fix for exactly this was already sitting uncommitted in the shared floci checkout (another session’s in-progress work, left untouched - not this orchestrator’s to land). Stages 2-5 remain implemented in the script but unexercised since stage 1 still fails fast by design. Follow-up pass 2026-08-20, isolated worktree off local main (ea9fd62fc0), real Docker/floci/AWS CLI throughout, read from the script’s own PASS/FAIL lines: cold_deploy and migrate now PASS - this estate moves 0 of 5 to 2 of 5. lex00/floci#70 was already fixed AND already pinned (99f4cbce8f moved live/floci-image to sha256:5873331d, 83c1aa73’s published build) - the brief that sent this pass in believed the pin predated it and was wrong; re-running against that existing pin confirmed CreateCacheSubnetGroup succeeds and surfaced the NEXT gap one call later. That gap was lex00/floci#71 (ElastiCache served no tagging actions at all: ListTagsForResource/AddTagsToResource/RemoveTagsFromResource absent from ElastiCacheQueryHandler’s switch, and CacheSubnetGroup carried no tags field), and the AWS provider calls ListTagsForResource on EVERY read of aws_elasticache_subnet_group, so the resource was unusable even with no tags in the configuration - the sole remaining cold-deploy error, 1 of 1. Fixed in floci and merged to lex00/floci main (dc140fb0; CI and GHCR-publish both green), mirroring RDS’s identical trio on the identical Query protocol; tags live on the CacheSubnetGroup model beside its existing arn field so TaggedResourceScanner picks them up for resourcegroupstaggingapi GetResources with no extra wiring (asserted by a new test, not assumed), and resolveTagHandle reads the resource type off the ARN so another taggable ElastiCache resource is one branch plus a tags field on its model. live/floci-image re-pinned here to sha256:dc246b1e, with live/floci-capabilities.json regenerated for that digest (services with -watch networkmanager,storagegateway, cloudcontrol, cloudcontrol-scoped, tagging) plus the three hand-probed rows re-verified live against the new image - redshift implemented, qldb still unimplemented, opensearch still partial - giving 86 services / 749 types, matching the prior digest block’s shape exactly with no existing digest block touched. Real numbers from the run: STAGE 1 ‘Apply complete! Resources: 62 added, 0 changed, 0 destroyed’, 0 objects tagged before migration; STAGE 2 dry run ‘40 of 62 resource instance(s) are eligible for stamping’ with UNTAGGABLE (22) and no UNADMITTED_TYPE section at all, -approve ‘40 resource(s) newly stamped, 0 already stamped, 0 newly recorded, 0 already recorded, 0 failed, 22 skipped’, and three identities read straight through the AWS CLI: module.vpc.aws_vpc.this:0 on the VPC, aws_security_group.rds on the RDS SG, module.vpc_endpoints.aws_vpc_endpoint.this:s3 on the S3 endpoint; 39 objects carry tofu-estate after migration. All 22 skips are genuinely untaggable types (aws_route, aws_route_table_association, aws_vpc_dhcp_options_association, aws_security_group_rule - none has a tags argument in the provider’s schema), i.e. the invariant working, not a gap. TWO SELF-INFLICTED SCRIPT BUGS FOUND AND FIXED, neither of which had ever run because stage 1 had never passed: the stage-2 count assertion demanded ‘0 skipped’ (impossible for this estate; it now asserts 40/22 by value plus UNADMITTED_TYPE by absence), and all three identity assertions compared the tag value against OpenTofu’s BRACKET spelling (‘module.vpc.aws_vpc.this[0]’) when a tag value can never carry ‘[’ - the escaped form is ‘module.vpc.aws_vpc.this:0’ per internal/live/markers.EscapeKey. That is the same vacuous-comparison bug corpus-iam-policy and corpus-iam-read-only-policy each shipped once; both forms are now separate variables, and the bracket forms are used where stage 5 reads a plan diff header. STAGE 3 fails on EXACTLY ONE diagnostic, filed as #346, whose headline finding refutes the obvious fix: the diagnostic points at lookup(each.value, ‘cidr_blocks’, null) on the vpc-endpoints module’s line 116, but a resolveLookupCall beside coalesce.go would NOT unblock this estate. Checked with three hand-built live-check variants rather than assumed: a static-valued lookup() already resolves, and the same map written as a direct each.value.cidr_blocks refuses identically. What refuses is the VALUE - the example passes [module.vpc.vpc_cidr_block], i.e. aws_vpc.this[0].cidr_block, a non-identity attribute of another managed resource, into aws_security_group_rule’s identity-bearing cidr_blocks. Written inline the resolver reaches the reference and says so (‘Not an identity attribute’); through a local or a for_each map it falls to the generic refusal at resolve.go:2025. Same wall, two spellings - so widening the decomposition switch changes the message and leaves the estate blocked. Whether identity resolution may fold a managed resource’s own CONFIGURED attribute (this cidr_block is local.vpc_cidr, a static string) is a maintainer design call, so it was filed rather than forced. test_apply and drift_reconverge remain not_run: attempting them against a still-refused plan would prove nothing.

Reproduce it#

go run ./tools/gauntlet run corpus-vpc-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-vpc-complete/run.sh; BREAK=1 corrupts its assertions to show they are load-bearing.