Home› Blog› Week 9 — The Service Accounts Nobody Ever Used
GCP Weekly Lab GCP Terraform

The service accounts nobody ever used

Two logins existed in my cloud that I did not create and have never used. Finding them was easy. The interesting part was nearly deleting the wrong one, and a security rule I briefly believed had been switched off.

Verified against current vendor documentation on 16 October 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.
GCP Platform Engineering Lab · Week 9 of 58

Why — The Problem This Solves

Every project in Google Cloud quietly comes with a login attached. You do not ask for it, you are not told about it, and it stays there for as long as the project exists.

After nine weeks this organization had five of these machine logins. Three I built on purpose. Two I had never heard of. An unused credential is not dramatic — it is just a door nobody checks, in a building where everything else is locked.

So this week is a tidy-up: find every machine identity, prove which have never been used, and close those — carefully enough that nothing breaks.

What You Need to Know — Skills & Tools

Google Cloud

  • IAM service accounts — an identity software signs in as: an email address, no password, and permissions attached exactly as they would be to a person
  • Default service accounts — one Google creates in every project automatically, so a virtual machine has something to run as before you have configured anything
  • Activity Analyzer — part of Policy Intelligence; reports when an identity last authenticated, which is the evidence that decides what is safe to close
  • Organization Policy Service — the organization-wide rules engine, used here to block service account key creation on both generations of the constraint
  • Resource Manager — the project hierarchy, walked end to end so the key sweep covers every project rather than the ones I remembered

Tooling

  • Terraform — hashicorp/google ~> 6.0; one resource type this week, google_project_default_service_accounts
  • HCP Terraform — runs the plan and the apply on Hashicorp's machines, workspace gcp-week-09, so nothing is applied from my laptop
  • Workload Identity Federation — how that remote run authenticates to Google: a short-lived token exchanged for an identity, never a key
  • gcloud CLI — four jobs here: list accounts, read org policy, enumerate projects, query last authentication

What these actually are

A service account is an identity for software, and that is the point of it. When this lab's pipeline creates a folder, it is not me doing it — it is tf-apply, and Google's audit log says so. The action has an owner you can name, and there is no password anywhere to leak.

A default service account is the one you did not ask for. In most organizations it is also granted Editor automatically, which is what makes it a well-known risk — though not here, because this organization's baseline switched that behaviour off on day one. What was left was two credentials that existed for no reason: a smaller problem than the usual one, and still a problem.

"Last authenticated" is the evidence that settles it. Activity Analyzer records when an identity last proved who it was. An account quiet for a month might be seasonal; an account that has never authenticated has never done anything at all, and that is a much safer thing to act on.

Organization policy constraints currently exist in two generations — an older name and a newer managed one. The same rule can be enforced under one and simply absent under the other, and absent reads identically to switched off. That detail costs me twenty minutes later in this post.

The tools, and what each is for. Terraform describes the end state you want and works out the calls to get there — here, unusually, managing accounts Google created rather than ones I made. HCP Terraform is where that code runs, on Hashicorp's machines rather than mine, which is what lets the pipeline hold no credentials locally; and Workload Identity Federation is what makes that safe, trading a short-lived token for the identity instead of storing a key anywhere. Of the four, Activity Analyzer is the only one that answers a question rather than making a change — and it is the one the whole week turns on.

Disable before delete Measure, do not assume idle A deleted identity is not recreatable Check both constraint generations

Architecture — How It Fits Together

One question, asked of every machine identity a service account 5 of them, here used recently never used keep, and leave alone 3 accounts · tf-plan, tf-apply, finops holds a role anywhere? 2 accounts · both default compute no — holds nothing DISABLE — reversible, one field this is where the week stops delete — only after a quiet period not done here: a recreated account is not the old one

Everything below is that tree applied to five accounts.

How We Built It — Step by Step

What we deployed

ResourceCountIts job
google_project_default_service_accounts2disable the unused default login in each project
google_org_policy_policy1block key creation on the second constraint generation

How we wrote it

The Terraform file tree for Week 9: versions.tf, variables.tf, a 79-line main.tf with three resources, and a 90-line validate.sh
Every file, and what it is for. The validator is longer than the Terraform, which is the right shape: disabling an account is three resources, but knowing which ones are safe to disable is the week.

Before writing any of it, I asked when each account last authenticated:

finops-reader@…        2026-10-09
tf-plan@…              2026-10-05
tf-apply@…             2026-10-05
398884169348-compute@  NEVER AUTHENTICATED
851623897121-compute@  NEVER AUTHENTICATED

The two at the bottom are default Compute Engine service accounts. They hold no role anywhere, and this organization's baseline already blocks the automatic grant that usually makes them dangerous. What is left is two credentials existing for no reason.

How we deployed it

The HCP Terraform run list for the Week 9 workspace showing an applied run with three resources
Three resources, applied. The run executes on HCP and authenticates to Google through Workload Identity Federation — no service account key exists anywhere in this pipeline.

What changed in the console

The service accounts list for the Logging project before the change, showing the default Compute Engine service account with status Enabled
Before. Status Enabled, no keys, created by nobody. This view had to be captured before the apply — afterwards it cannot be recreated.
The same service accounts list after the change, showing the default Compute Engine service account with status Disabled
After. Same view, same account, status Disabled. Still present — which is the point.

How we tested it, and what success looked like

Disabling a login is only safe if nothing depended on it. The honest test is not "did the resource apply" but "does everything built in the previous three weeks still work":

All Week 06 checks passed.
All Week 07 checks passed.
All Week 08 checks passed.

Plus this week's own: both accounts disabled, both still present rather than deleted, and zero downloadable keys anywhere in the organization.

Challenges — What Actually Went Wrong

1. I briefly believed a security rule had been switched off

This lab's first rule is that service account keys are never created. I checked the organization policy and got enforce: false, and for a few minutes I thought the rule had been off for weeks.

It had not. The rule is enforced under a second name — Google is migrating these constraints to a new generation, and this organization's baseline uses the new name for one key rule and the old name for another. A constraint with no policy attached reads as "not enforced", which looks identical to "switched off". This week sets both names.

2. The account that looked dormant was not

finops-reader was one I had forgotten creating, and I was ready to call it unused. It had authenticated five days earlier. Looking idle and being idle are different things, and only one of them is measurable.

3. Deleting is worse than it sounds

The obvious tidy-up is to delete. But a deleted account's email can be reused while IAM keeps tracking a hidden internal ID — so the permissions attached to the old one never apply to the replacement. A rebuild looks identical and quietly is not. Disabling avoids the whole class of problem.

Security — Controls at Every Layer

Reversible first. Disable, confirm nothing breaks, and only consider deleting after a quiet period. This week deliberately stops at disable.

Teardown restores rather than abandons. The Terraform sets restore_policy = "REVERT", so destroying this week re-enables the accounts instead of leaving them disabled with nothing managing them.

Defence in depth on the key rule. Setting the second constraint name is redundant while the baseline holds. That is what redundancy is for, and it removes a trap for the next person who checks.

Cost

$0. Identities, policies and usage analysis are all free, and nothing here runs.

Read from the billing export rather than a rate card: this organization has cost $0.006 since 1 July, total.

Cleanup

Exempt from teardown, with one caveat worth knowing: because of restore_policy = "REVERT", destroying this week puts the accounts back to enabled. That is deliberate — an account left disabled by a configuration that no longer exists is a mystery for someone later.

References

Measured, not documented: which accounts were actually unused, and that this organization enforces the two key constraints on different generations.

Key Takeaways

  • Every project hands you a login you did not ask for. Go and look at yours.
  • Idle-looking is not idle. One command tells you when an identity last authenticated; guessing does not.
  • Disable, then wait, then delete. Deleting breaks permissions in a way that a rebuild does not restore.
  • A rule can be enforced under a name you did not check. "Not enforced" and "no policy attached" read identically and mean very different things.

Next week: Cloud Identity and group-based access — giving permissions to groups rather than to people, and what that fixes.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent