The Azure emulator family
— images pinned to one certified set →Entra, ARM, Key Vault, Fabric, API Management and Databricks, each a separate emulator in its own repository with its own release cadence and its own container image. This repository is where they are wired together: one compose file, one pinned set of versions, and one test that proves the published images still trust each other.
Entra is the STS. It issues every token and publishes the JWKS the others validate against, over HTTP, across a real network boundary. That is the production trust relationship rather than a shortcut around it, which is why the family is six containers and not one process.
The point is speed for whoever, or whatever, is doing the work. Proving an Azure-shaped data platform against the real services costs a paid tenant and slow round trips that are hard to reset. Build and prove it here first, then point the issuer variables at a real tenant: that move is configuration, not a code path.
This repository ships no emulator of its own. No binary, no image, no Go module. It is the neutral place where the family is pinned, documented and proved, so no single member gets to grade the others.
Every member already tests itself, thoroughly, from its own source tree. None of them can test the family, and the family is where the interesting failure lives.
Entra's suite proves entra issues correct tokens. ARM's suite proves ARM validates some issuer. Neither proves that ARM validates entra's tokens, or that the six images boot together in the right order.
A chain test over the published images, in a neutral repo
Tokens carry iss = <entra origin>/<tenant>/v2.0, so entra must advertise the origin its peers check. Get it wrong and every cross-service call returns 401 while every upstream suite stays green.
One PUBLIC_ORIGIN, repeated verbatim, and asserted
Any member can publish a release that breaks the family without a commit landing here. That arrives from outside, at a time nobody chose, and the first symptom is somebody else's broken afternoon.
A pinned bill of materials, plus a nightly run against latest to catch the drift
Each has its own documentation site, its own parity ledger and its own witness manifest. The version on each card is the one this repository's chain test certified; the grade is what that repository's own gate reports, not a second opinion computed here.
Issues every token in the family and publishes the JWKS the others validate against: OAuth 2.0 and OpenID Connect, Graph, SCIM. Upstream of everything, and the only hard dependency.
The Azure Resource Manager control plane with Microsoft.Authorization RBAC. It governs the vault the way Azure does: a role assignment decides who may read a secret, and no assignment means no access.
The Key Vault data plane: secrets, keys and certificates, authenticated by entra's tokens and authorized by ARM's role assignments, both over the network rather than in process.
The Fabric control plane and OneLake: workspaces, items, pipelines and the lakehouse surfaces. A consumer of the three above, and the largest ledger in the family. Behind the fabric profile because the image is heavy.
API Management: the management plane, the gateway, and the policy engine with its C# expression language. A consumer of entra alone, serving its own Microsoft.ApiManagement surface rather than calling ARM. Behind the apim profile.
The Databricks workspace REST surface. Identity is a personal access token plus its own OIDC; entra is an optional federated issuer and the vault an optional secret backend. Behind the databricks profile.
A grade counts rows in that repository's docs/parity.md, whose denominator is the scope that repository set out to reach. It moves when discovery adds a row, and it cannot see surface nobody has enumerated yet. Each ledger states separately, under its scope boundary, what it leaves out and why.
Every number on this page is read from a manifest when the page loads. None is typed into it, and the build fails if one ever is.
Across the family, — capabilities are graded green, — are partial, and — are enumerated as not implemented. A red row is a stated absence rather than a silence, which is the only reason the denominator means anything.
A green row must name a witness in that repository's docs/witnesses.json, and its CI fails when a named witness stops existing. The witnesses are ranked, and this page counts each claim once, by its strongest one.
ci:a packaged third-party client driving the emulator over a network in CI. The strongest tier.
sdk:Microsoft's own client does the talking, linked into a test in process.
go:our own client on both ends. It proves the emulator agrees with itself, which is not nothing and is not evidence.
verifiedcompared against a running Azure tenant. —
So — of the green claims rest on a client we did not write, and the rest on ours. None of them is graded on a comparison with Azure itself: no claim in any member's ledger carries a verified witness, which is why that tile reads —. entra comes closest, having captured six token-endpoint scenarios from a real tenant and diffing them offline. apim and fabric carry differential harnesses that have never run, for want of a tenant and its credentials. That is the honest ceiling on everything above.
The chain test is narrower still, on purpose. Its — assertions prove the seam: that the published images agree about who issues tokens and who trusts them. What each service does with a valid token is its own repository's business, tested there in depth. One of the assertions offers a foreign-issuer token and requires a refusal, because without it an emulator that skipped validation entirely would pass every other step.
There is nothing to install but Docker. This repository is a compose file, a bill of materials and a test; the images come from GHCR.
git clone https://github.com/calvinchengx/azure-emulators
cd azure-emulators
docker compose up
That is entra, ARM and Key Vault, wired to each other and governed the way Azure governs. The version defaults in the compose file are the bill of materials: a bare docker compose up runs the newest combination this repository's chain test has proved works together. Each is overridable per variable, so pinning one member back is a one-line change and never a fork.
The consumers are profiled, because most callers do not need them and both images are large.
docker compose --profile fabric up # …adds fabric-emulator :9443
docker compose --profile apim up # …adds apim-emulator :8446
docker compose --profile databricks up # …adds databricks-emulator :8447
State persists in named volumes, so a directory you seed once is still there tomorrow. docker compose down keeps it; docker compose down -v is the deliberate reset.
To run the family's own gate over the pinned images, on high ports so it never collides with a stack you already have up:
./e2e/chain/run.py # runs the certified versions, then tears down
KEEP_UP=1 ./e2e/chain/run.py # leave the stack up to poke at
Then follow the quickstart for a token and a first call, the family for how the six depend on each other, release coordination for how a bump is certified, and the chain test for what each assertion means when it fails.