The problem
The group's Azure estate had grown the way most do: one subscription at a time, each with its own habits. Apps authenticated to databases with connection strings pasted into config files, deployment used a shared service principal whose secret was known to "a few people", and several long-gone contractors still held Contributor on production. Nobody could answer the auditor's first question — who can access what? — without a week of spreadsheet archaeology.
The approach
The job was less about new technology and more about resolving, for every workload, which of Azure's authentication options it should be using — then enforcing that consistently:
- Inventory first — scripted sweeps over Microsoft Entra ID and Azure RBAC produced a map of every identity (human, service principal, managed identity) and every role assignment across all subscriptions. Printed out, it was ugly — which was the point. Those sweeps hardened into Bastion, a reusable read-only scanner that evaluates the estate against the baseline and reports drift with remediations.
- Managed identities by default — every app running in Azure moved to
a managed identity; .NET services switched to
DefaultAzureCredential, so the same code authenticates with the developer's own account locally and the managed identity in production. Client secrets were deleted as each workload converted. - Federation instead of deploy secrets — GitHub Actions pipelines switched to workload identity federation, so CI/CD authenticates with short-lived tokens and there is no deployment secret left to leak or rotate.
- RBAC that survives people leaving — role assignments moved from individuals to Entra ID groups at resource-group scope, elevated access went behind just-in-time activation (PIM), and the handful of secrets that genuinely must exist (third-party APIs) landed in Key Vault with rotation and RBAC-scoped access.
- The baseline as code — Bicep modules encode the whole arrangement, so a new subscription or environment starts compliant instead of starting over.
The result
By the end, not a single application held a credential in its configuration — everything authenticates as itself. The quarterly access review went from days of spreadsheet work to about an hour of reading a generated report, offboarding became "remove from groups" instead of a manhunt, and the next audit's access questions were answered from one page.