Service principals never clean themselves up.

And no one remembers why they exist.

Here is how we ended up with hundreds of them.

They Start With Good Intentions

Someone needs to deploy an app. They create a service principal.

Someone needs a CI/CD pipeline. Another service principal.

Someone needs cross-tenant access. One more.

Each one made sense at the time.

Each one solved a real problem.

None of them had an expiration date.

The Documentation Dies First

The service principal gets created. It gets used. It works.

No one writes down:

  • what it is for
  • which system uses it
  • who owns it
  • when it can be deleted

Six months later, someone looks at the list of service principals.

They see names like:

  • sp-prod-deploy
  • app-service-identity
  • terraform-ci
  • legacy-integration

They have no idea which ones are still in use.

Deleting the wrong one could break production. So they delete nothing.

Naming Does Not Help

We tried naming conventions.

We added tags.

We put the team name in the description.

None of it mattered.

Teams changed. People left. Projects ended.

The service principal stayed.

The tags became stale. The names became meaningless.

Managed Identity Should Have Replaced Them

Managed Identity exists for exactly this reason.

It eliminates:

  • credential management
  • rotation
  • storage
  • sprawl

We knew this. We recommended it. We documented it.

But old habits die hard.

Developers still created service principals because:

  • they already knew how
  • the docs they found online used service principals
  • some tools did not support Managed Identity yet
  • cross-tenant scenarios still required it

We could have enforced it with policy. We did not, until it was too late.

The Cost Is Not Just Clutter

Hundreds of unused service principals create real problems:

Security risk. Each one is a potential entry point if credentials leak.

Audit noise. Security teams waste time investigating principals that do nothing.

Compliance overhead. We have to prove each principal is necessary and secured.

Rotation burden. Even unused principals need credential rotation.

We spent more time managing dead service principals than we did creating new ones.

How We Started Cleaning Up

We could not just delete things and hope for the best.

We built a process:

  • query Azure AD for all service principals
  • check sign-in logs to see which ones were actually used
  • cross-reference with known systems and pipelines
  • tag unused principals with a “review” flag
  • wait 30 days and check again
  • delete principals with no activity and no owner response

We deleted 40% of our service principals in the first pass.

Not a single thing broke.

What We Changed Going Forward

We made new rules:

  • default to Managed Identity for all new workloads
  • require justification for any new service principal
  • enforce expiration dates on service principals where possible
  • tag every principal with owner, purpose, and system
  • quarterly review of all service principals

We also built automation:

  • alert when a service principal has not signed in for 90 days
  • require annual attestation from owners
  • auto-disable credentials that have not been used in 6 months

It is not perfect. But it stopped the sprawl.

When Service Principals Are Still Necessary

We still use service principals. But only when:

  • Managed Identity is not supported
  • cross-tenant access is required
  • third-party integrations demand it
  • GitHub Actions needs Azure access (until Managed Identity support improves)

Everything else uses Managed Identity.

The Lesson

Service principals are tools, not defaults.

They should be rare. They should be documented. They should be cleaned up.

If you have more than a dozen, something went wrong.

If you have hundreds, you have a process problem, not a technical one.

We learned that the hard way.

Now we treat every service principal like a liability.

Because it is.