4 min read
gcp-project-audit
Generated source view for the actual executable
engineering/gcp-project-auditskill. The durable routing article is Cloud, Data, and Service Skills. Source: skills/engineering/gcp-project-audit/SKILL.md
Runtime Source
| Field | Value |
|---|---|
| Category | engineering |
| Origin | personal |
| Slug | gcp-project-audit |
| Source slug | gcp-project-audit |
| Family | Cloud, Data, and Service Skills |
| Source | skills/engineering/gcp-project-audit/SKILL.md |
Bundled Resources
These files are part of the executable skill folder and must be preserved with the skill source.
| File | Role |
|---|---|
references/hygiene-commands.md |
Progressive reference |
scripts/inventory.sh |
Executable helper |
scripts/summarize.py |
Executable helper |
Description
Audit every Google Cloud project Kevin's personal Google account can see, read-only: billing, APIs, IAM, keys, buckets, Firebase and logs, with cleanup candidates. Use when Kevin says "audit my GCP projects" or before changing IAM, keys or billing.
Skill Source
---
name: gcp-project-audit
description: >-
Audit every Google Cloud project Kevin's personal Google account can see, read-only: billing,
APIs, IAM, keys, buckets, Firebase and logs, with cleanup candidates. Use when Kevin says "audit
my GCP projects" or before changing IAM, keys or billing.
origin: personal
source_slug: gcp-project-audit
metadata:
openclaw:
category: "engineering"
requires:
bins:
- gcloud
- python3
- curl
---
# GCP Project Audit
Thin router over `scripts/`. The inventory is read-only; the decisions it informs live in
`wiki/tools/gcp-projects.md`. Do not turn the audit rules into prompt text: change
`scripts/summarize.py` and re-run.
## Commands
```bash
skill_dir="${CLAUDE_SKILL_DIR:-${CODEX_SKILL_DIR:-skills/engineering/gcp-project-audit}}"
bash "$skill_dir/scripts/inventory.sh" # all visible projects, parallel, ~2 min
bash "$skill_dir/scripts/inventory.sh proj-a proj-b # subset
```
Output lands in `~/Library/Logs/gcp-audit/<UTC stamp>/`: one directory of raw JSON per project,
`summary.json`, and `report.md`. The report path is printed last. Read `report.md`, then update the
inventory table and the dated decisions in `wiki/tools/gcp-projects.md`.
## Procedure
1. Confirm `gcloud auth list` shows Kevin's personal Google account active. The script uses gcloud's user
credential and adds `x-goog-user-project` for Firebase REST calls; nothing needs a service account.
2. Run the inventory. Compute, Cloud Run, Functions, SQL and GKE calls fail on projects where those
APIs are off; that is normal and recorded in `_errors.txt`, not a finding.
3. Read `report.md`. Flags mean:
- `archive-candidate`: only default APIs, no resources, no log entry in 60 days. The log signal is
admin-activity audit logs only (config changes, OAuth client edits, API enablement); Firestore
reads, Hosting traffic and Auth sign-ins are not logged by default, so a serving Firebase app can
look dormant. Resource Manager writes are excluded so this skill's own label updates do not count.
- `dormant`: Firebase or data resources exist but no log entry in 60 days.
- `stale-sa-key`: user-managed service-account key older than 90 days.
- `unrestricted-api-key`: API key without API or application restrictions.
- `bucket-pap-not-enforced`: bucket where public access prevention is inherited, not enforced.
- `owner-not-kevin`: an IAM owner other than Kevin's personal account (`KEVIN` in `scripts/summarize.py`).
- `oauth-unchecked`: the project has no `oauth-app` label, so nobody has checked Google Auth
Platform for it; it is never an archive candidate until someone does (step 4).
4. OAuth clients are invisible to gcloud, and a project with nothing but default APIs can still be
the Sign in with Google backend of a live site (four of Kevin's were on 2026-09-25). For every
candidate open the Console pages `/auth/clients?project=<id>` and `/auth/overview?project=<id>`
and read the Project Checkup "Client usage" line: "All of your OAuth clients are actively being
used" means live within Google's inactivity window; "Unused clients" means Google will soon
delete them. Record the answer as the label `oauth-app` = `live-signin` | `cli-clients` |
`unused-client` | `none`, then re-run the summarizer. The Audience page's "N users / 100 cap"
counts only sensitive-scope grants, so 0 users does not mean no sign-ins.
5. Apply only reversible hygiene from this skill: project labels (`purpose`, `status`, `owner`,
`audited`) through the Resource Manager v3 API, and public access prevention on buckets. Both
commands are in `references/hygiene-commands.md`.
6. Anything that deletes (projects, keys, API keys, Firestore data) needs Kevin's explicit go-ahead
in the conversation and is done with plain gcloud, then recorded in the wiki page. Project
shutdown is recoverable for 30 days; key deletion is not.
## Hard rules
- Never delete or disable anything from this skill's scripts.
- Never create service-account keys or API keys.
- Never enable billing, link billing accounts, or accept terms on Kevin's behalf.
- `gcloud projects update --update-labels` is not available in the installed SDK; use the REST call.
Timeline
- 2026-11-02 | Generated a browseable source page from the actual executable skill file. Source: skills/engineering/gcp-project-audit/SKILL.md