Skip to content

The family

Six emulators, each its own repo, its own release cadence, its own GHCR image. This repo composes them and proves they still trust each other.

EmulatorPortRole
entra-emulator8443The STS. Issues every token; publishes the JWKS the others validate against
arm-emulator8445ARM control plane + Microsoft.Authorization RBAC
azure-keyvault-emulator8444Key Vault data plane — secrets, keys, certificates
fabric-emulator9443Fabric control plane + OneLake. A consumer — fabric profile
azure-apim-emulator8446API Management — management plane, gateway, policies. A consumer — apim profile
databricks-emulator8447Databricks workspace REST. PAT + its own OIDC; entra optional — databricks profile

entra is upstream of everything — it is the only hard dependency in the family, because a data plane with no STS cannot exercise the challenge flow every Microsoft SDK performs. arm is upstream of keyvault by default: role assignments and the vault resource’s own configuration decide what the data plane permits.

apim is a consumer of entra alone. It serves its own Microsoft.ApiManagement ARM surface rather than calling arm — the same way Azure’s resource providers each own their surface behind one front door.

Each emulator is a static Go binary on distroless, so all six share one base layer and cost a few tens of MB of RSS. Bundling them into one process would save essentially nothing — and would cost the thing that makes this family worth having: keyvault and arm validating entra’s tokens over HTTP against a separate origin is the production trust relationship.

Keep the boundary; compose across it.

Issuer alignment is the one thing that must match

Section titled “Issuer alignment is the one thing that must match”

Tokens carry iss = <entra origin>/<tenant>/v2.0, so entra must advertise the origin its peers validate against. On the compose network that origin is https://entra-emulator:8443 — hence PUBLIC_ORIGIN, and why every *_ENTRA_ISSUER repeats it verbatim.

Get it wrong and every cross-service call fails 401 while each emulator’s own test suite stays green. That is precisely the failure the chain test exists to catch.

Point the *_ENTRA_ISSUER variables at a real tenant instead and nothing else changes.

Shared across the family, and public knowledge by design:

Tenant6f89cf12-978b-4d23-ac18-9ef0c127cf87
Subscription6082bfda-63d0-46f4-8272-ae9195139feb
Daemon app (confidential)00d88624-f0d7-46f6-a641-6232c2608928 / daemon-app-secret

These are fixed random v4 GUIDs, not patterned placeholders. Real Entra never issues GUIDs with repeating nibbles, and a uniform GUID is a weak test oracle: it survives segment transposition and most mis-slicing, so a parsing bug can pass unnoticed.