Passwordless identity for services and pipelines across Microsoft 365 and Azure
Mailbox assistants, legacy relays, web forms and deploy pipelines all needed access to something. We gave each one a certificate or a federated identity scoped to exactly one thing, and left nothing behind to leak.
At a glance
- 7
service identities on the pattern
- 1
mailbox reachable per credential
- 4
Exchange RBAC scopes
- 0
client secrets stored in pipelines
The challenge
Several services needed to read, draft or send mail: an AI mailbox assistant, applications that only speak SMTP, a website contact form and a campaign mailer. The old answer was a user password or SMTP basic authentication, which Microsoft is retiring, and tenant-wide mail permission means one leaked credential can read every mailbox in the business.
The deploy pipelines had the same problem in a different shape, with long-lived tokens and keys sitting in a code host. We wanted one repeatable pattern where every service and every pipeline proves who it is without a stored secret.
Architecture
Swipe to explore the diagram
Services authenticate with a certificate and are scoped to one mailbox by Exchange. Pipelines authenticate with a federated token and can read only their own secrets.
What we built
The service pattern
- One Entra app registration per service or purpose, using client credentials with a certificate: no user, no password and no client secret
- Graph application permissions narrowed to named mailboxes with Exchange RBAC for Applications
- A scripted setup in az CLI and Exchange Online PowerShell, followed by a check that proves each app works on its own mailbox and fails on every other one
The pipeline pattern
- Azure DevOps service connections using workload identity federation, each scoped to one resource group or a handful of vault secrets
- Key Vault in RBAC mode with purge protection, and per-secret role assignments so a deploy reads only what it needs
- Static-site deploy tokens fetched at deploy time rather than stored anywhere
Legacy SMTP
- A small SMTP-to-Graph relay for applications that only speak SMTP, locked to a single sender per instance and never published on the host
- Messages over 4 MB routed through Graph upload sessions instead of bouncing
- Strict DMARC alignment on the sending domain, with SPF and DKIM verified at an external receiver
Engineering decisions
Certificates, not secrets
The private half of a certificate never leaves the vault and the container. There's nothing to paste into a log, and the only recurring job is a roll-over every two years.
Make RBAC the only authority
Entra grants and Exchange RBAC are additive, so one unscoped grant quietly defeats the mailbox scoping. The runbook removes the Entra grant and confirms every app's assignment list comes back empty.
Separate credentials for separate jobs
A draft-only assistant and a service that sends mail never share a credential, so each can be rotated or revoked on its own.
Scope to a resource group, not a subscription
Each pipeline identity can touch one resource group or one set of secrets. A compromised pipeline in one project can't reach into another.
Outcome
Every mail workload runs without a user password, with a blast radius of one mailbox. Pipelines deploy with no stored cloud credentials, and the tokens that used to live in the old code host have been reset and retired.