Your CI/CD variables page is a graveyard of forgotten secrets. I know because I just audited one.

55 variables. That's what I found when I opened the GitLab CI/CD settings for a production service. Connection strings in plain text. Deprecated passwords nobody remembered creating. Duplicate tokens with slightly different names. A beautiful mess that everyone was too scared to touch.

Sound familiar?

The Problem Nobody Talks About

Here's the thing about CI/CD variables: they accumulate like dust. Every developer who touches the pipeline adds "just one more variable." Nobody deletes them because nobody knows what breaks. Six months later, you have 55 variables, half of which are ghosts.

But it's worse than clutter. It's a security risk.

Connection strings sitting in GitLab UI. Even "masked" — they're one misconfigured job away from leaking into logs. Service principal credentials that rotate... never. Passwords for databases that were decommissioned two sprints ago.

This isn't DevOps. This is a ticking incident report.

The Audit

I did what nobody wanted to do. I went through every single variable. One by one. Cross-referenced with the actual pipeline YAML. Cross-referenced with the PowerShell deployment scripts. Mapped who uses what, where, and why.

The result?

  • 25 variables — actively used, essential for deployment
  • 30 variables — dead. Unused. Legacy. Forgotten.
  • 4 of those 30 — actual connection strings that should have been rotated months ago

55% of the pipeline's "configuration" was garbage. More than half.

The Migration: From Variables to Vault

Connection strings don't belong in CI/CD variables. Period. They belong in Azure Key Vault — centralized, access-controlled, auditable, rotatable.

The migration pattern:

Before: Pipeline reads connection string from GitLab variable. Plaintext. Managed manually. Rotated... when someone remembers.

After: Pipeline authenticates to Azure via UAMI (User-Assigned Managed Identity), fetches the secret from Key Vault at runtime, uses it, forgets it. No plaintext in the pipeline. No manual rotation. No "who changed this variable last Tuesday?"

The implementation is surprisingly clean:

  1. Store secret names (not values) in pipeline YAML — these are just references, not secrets
  2. Authenticate to Azure using federated tokens — zero credentials stored
  3. Fetch secrets dynamically at deploy time — az keyvault secret show
  4. Use them, then they're gone from memory

The beautiful part? UAMI means the pipeline doesn't store a single Azure credential. No service principal passwords. No client secrets. The identity is managed by Azure itself. The pipeline just says "I am who I am" — and Azure believes it because of the OIDC federation trust.

Why UAMI Over Service Principals

Service principals are the duct tape of Azure authentication. They work, sure. But they come with baggage:

  • Client secrets that expire and break your pipeline at 3 AM
  • Manual rotation ceremonies that everyone dreads
  • Credentials stored in yet another CI/CD variable (defeating the purpose)

UAMI flips the model. The identity is assigned to the runner, managed by Azure, and authenticated via OIDC federation. No secrets. No rotation. No 3 AM incidents because someone forgot to update a password.

Zero-trust isn't a marketing term when you actually implement it.

The Cleanup

After migrating secrets to Key Vault, I deleted the deprecated variables. Four connection strings that had been sitting in GitLab for months — gone. Then proposed cleanup for the remaining 26 unused variables pending script review.

The pipeline didn't flinch. Because the pipeline never needed them.

That's the hardest part about cleanup: realizing how much of your "critical configuration" is just leftovers from someone's experiment two years ago.

What I Learned

Secrets management isn't a one-time project. It's hygiene. Like brushing your teeth — boring, repetitive, but skip it and things rot.

Every pipeline deserves an audit. Not once a year for compliance theater. Regularly. Because variables accumulate, secrets age, and what was "temporary" in Sprint 12 becomes permanent technical debt by Sprint 30.

Your pipeline's variable count is inversely proportional to your team's security posture. The fewer variables — the cleaner the architecture.

55 variables. 25 survivors. One Key Vault. Zero secrets in the pipeline.

That's how it should be.