5 Account inventory (incl. shared/service accounts)
A dated register of every account — human, shared and service — each with a named owner. It is built by exporting users, groups and group memberships with GAM (the command-line Workspace admin tool), then reconciling that against the human register and cross-checking the identities that never appear as ordinary users: GCP service accounts and domain-wide delegation clients. The point of the register is that it makes an unowned account a finding rather than a curiosity, so an orphaned or attacker-created identity has nowhere to hide.
Visual explainer A worked example: from raw GAM output to a register with named owners
Caveats
- The inventory is only as good as its ‘unowned = suspicious’ rule — an account with a plausible name and no owner is exactly what an attacker leaves behind.
- Suspended accounts still hold data and can be re-enabled by any super admin — count them in, do not filter them out.
Setup steps
-
bash <(curl -s -S -L https://raw.githubusercontent.com/GAM-team/GAM/main/src/gam-install.sh) gam oauth info # proves the authorization stuck - open ↗
https://admin.google.com/ac/groups · captured 2026-07-15
gam print users fields primaryEmail,name,suspended,isAdmin,isDelegatedAdmin,lastLoginTime,creationTime,orgUnitPath > users-$(date +%F).csv -
gam print groups fields email,name,description,adminCreated > groups-$(date +%F).csv && gam print group-members > group-members-$(date +%F).csv - open ↗
https://admin.google.com/ac/owl/domainwidedelegation · captured 2026-07-15
Security › Access and data control › API controls › Manage Domain Wide DelegationCross-check GCP service accounts and Security > Access and data control > API controls > Manage Domain Wide Delegation against the register
- open ↗
https://admin.google.com/ac/users · captured 2026-07-15
Diff = zero unexplained accounts; every account has a named owner and a purpose
Ongoing maintenance
- automatable: script Monthly: re-export users/groups/delegation clients and diff against the register.
- requires a human Monthly: triage every unowned or unexplained account the diff surfaces.
How to verify
-
Check the newest inventory snapshot is no older than the cycle, then re-run the export and diff — an inventory is only as good as its date.
gam print users fields primaryEmail,suspended,lastLoginTime > users-check.csv && diff <(sort users-check.csv) <(sort users-<last-date>.csv)
v0.1.0 Assure policy #10 · #28 ↗