* Repair corrupted GroupMember state so deploys stop failing (#133)
Every Deploy to Production run since 2026-06-30 aborts about a minute
in with "googleapi: Error 409: Member already exists" and merged
changes (e.g. #134, #137) never apply. Two external-email GroupMember
records in the maintainers group are recorded in state with an empty
member ID ("groups/04f1mdlm38smb30/members/"), so each pulumi up plans
a create-before-delete replace whose create conflicts with the live
membership and fails the whole update. Five other records corrupted at
the same time were already healed by the 2026-07-02 deploy run.
Add a one-time TEMP block to the deploy workflow (same precedent as the
auth-maintainers repair and the experimental-ext-tasks state delete):
drop the two broken records, then re-import each membership by member
email so the provider records the numeric member ID. All commands are
|| true-guarded, and the block must be removed after one successful
deploy.
Fixes#133
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NazA3QKftRn72tdPG5vrpb
* Widen state repair to all seven corrupted GroupMember records (#133)
* Replace per-URN state deletes with export surgery; verify healing post-deploy (#133)
The committed repair used 'pulumi state delete <urn>' per record, but the
failed create-before-delete replaces left duplicate same-URN entries (a
live record plus a pending-delete leftover) for some of the seven
corrupted GroupMember records. 'pulumi state delete' fails on an
ambiguous URN, and the '|| true' guard hid that failure, making the
repair a silent no-op.
- Replace the per-URN deletes with state surgery: export the stack,
drop every entry (including pending-delete duplicates) whose URN is
one of the seven with jq, and re-import. Unguarded, so a surgery
failure is loud; skips the import when nothing matches (idempotent).
- Keep the seven imports-by-email ('|| true' so a not-currently-live
membership falls back to a create instead of blocking the deploy).
- Add a post-deploy verification step that fails if any GroupMember
record still has an empty member ID or a duplicate URN. The pinned
provider (SamuZad/googleworkspace 0.11.1) writes an empty member ID
even on successful creates (fixed upstream in 0.11.2), so a green
'make up' alone does not prove healing; the TEMP block must only be
removed after this verification step passes.
- Correct the root-cause comment: Google-side membership removals ->
refresh dropped the records -> re-create -> provider create bug wrote
empty IDs.
* Bump googleworkspace provider 0.11.1 -> 0.11.2 (#133)
0.11.2 carries upstream commit "handle edge case with group
memberships" (SamuZad/terraform-provider-googleworkspace, 2026-03-12),
which fixes the create-path bug that stores an empty member ID for
external-email group members — the bug that corrupted the seven records
repaired by the TEMP block in deploy.yml and that would re-corrupt any
record restored via the fallback create path. CI regenerates the SDK
from this pin via 'pulumi install' and typechecks against it.
---------
Co-authored-by: Claude <noreply@anthropic.com>
The hashicorp/googleworkspace provider has a known eventual-consistency
bug (issue #387): after creating a user, it polls for 4 consecutive
identical API etags before marking the resource created. Google's API
frequently flip-flops etags, so the check never passes and the 300s
default timeout trips — even though the user was actually created.
This caused cliff@ creation to timeout, leaving the user in GWS but not
in Pulumi state; subsequent runs fail with 409 Entity already exists.
Two changes:
- Mark cliff as existingGWSUser so Pulumi stops trying to create him
(matches the pattern used for other stranded users).
- Switch to the SamuZad/googleworkspace fork (v0.11.1), which lowers
numConsistent from 4 to 2, preventing the timeout for future user
creations. The upstream provider has been unmaintained for 2+ years.