Skip to content

Architecture

From here it’s technical. One sentence: a Microsoft-Graph-shaped REST/OData v4 API in .NET 10 that fronts the Exchange Online PowerShell cmdlet surface with typed JSON.

  • The baseline is the cmdlet surface. Exchange Online exposes ~726 administratively-relevant PowerShell cmdlets. That is the source of truth for what can be done and what fields exist - we never invent shapes; we mirror the cmdlets, grounded against Microsoft’s own published docs.
  • The shape is Microsoft Graph conventions. Entity sets, OData query options, a /$metadata CSDL document, /$ref for relationships, alternate-key lookups, a paginated collection envelope with @odata.nextLink. If you’ve used Graph, the URLs and JSON feel familiar.

So: Graph-style where Graph has the concept; cmdlet-aligned where it does not.

Sequence of one request: the caller sends GET /exchange/v1.0/mailboxes with a bearer token; Entra JWT auth validates it and checks the route's role; the OData controller translates $filter to OPATH and picks the cmdlet; ICmdletExecutor borrows a warm runspace from the pool; Exchange Online returns PSObjects; the mapper turns them into typed POCOs; the controller returns 200 with clean JSON and a next link.Sequence of one request: the caller sends GET /exchange/v1.0/mailboxes with a bearer token; Entra JWT auth validates it and checks the route's role; the OData controller translates $filter to OPATH and picks the cmdlet; ICmdletExecutor borrows a warm runspace from the pool; Exchange Online returns PSObjects; the mapper turns them into typed POCOs; the controller returns 200 with clean JSON and a next link.

The internals - layers and the one seam that matters

Section titled “The internals - layers and the one seam that matters”
Layers: callers reach ASP.NET Core over HTTPS with a JWT; middleware, about 52 OData controllers and the OData engine sit in front of the ICmdletExecutor seam, which is the only place mocked in tests; the production executor runs dual runspace pools and the PSObject mapper, connected to Exchange Online and to Security and Compliance; the metadata project supplies entity POCOs, the entity catalog and the EDM model.Layers: callers reach ASP.NET Core over HTTPS with a JWT; middleware, about 52 OData controllers and the OData engine sit in front of the ICmdletExecutor seam, which is the only place mocked in tests; the production executor runs dual runspace pools and the PSObject mapper, connected to Exchange Online and to Security and Compliance; the metadata project supplies entity POCOs, the entity catalog and the EDM model.

The load-bearing idea is the seam. Every controller talks to one interface, ICmdletExecutor. In production it runs real cmdlets; in tests it returns canned data. That single abstraction is why the whole API is testable without a tenant - and why ~5,700 automated tests can cover behaviour that ultimately depends on PowerShell.

Exchange administration is split across two PowerShell connection modes; we keep a warm, reusable pool of sessions for each so requests don’t pay a fresh sign-in every time.

ICmdletExecutor routes to an EXO pool (Connect-ExchangeOnline) for mailboxes, groups, mail flow, Defender, retention and public folders, and to an IPPS pool (Connect-IPPSSession) for Purview: eDiscovery, DLP, sensitivity labels and retention compliance.ICmdletExecutor routes to an EXO pool (Connect-ExchangeOnline) for mailboxes, groups, mail flow, Defender, retention and public folders, and to an IPPS pool (Connect-IPPSSession) for Purview: eDiscovery, DLP, sensitivity labels and retention compliance.

Which pool a cmdlet runs in follows Microsoft’s own applicability metadata, not a guess - so a compliance cmdlet always goes to the compliance session. The allowlist membership (V1 = EXO, V1Ipps = IPPS) is the routing table, and the two sets are kept disjoint by a test.