ARM Emulator
— latest release →A clean-room Go emulator of the Azure Resource Manager control plane, management.azure.com: the ARM envelope and its error shape, subscriptions and resource groups, Microsoft.Authorization role definitions and assignments with real scope inheritance, Microsoft.KeyVault/vaults, and Microsoft.Fabric/capacities.
It exists to make one thing in the emulator family real rather than pretended: authorization. An assignment written here over ARM's wire is enforced by the sibling Key Vault data plane on the next request, and revoking it flips the answer back. Switching to real Azure is configuration, not a code path.
The az CLI and Microsoft's management SDKs for Go, Python, JavaScript and .NET run against it unmodified, which is the only reason any claim on this page is worth anything.
Every capability below names the check that proves it. Nothing here has been compared against a live Azure subscription, and this page says where that matters.
Authorization is the part of an Azure platform that is hardest to test and worst to get wrong, because the only place it runs is a tenant somebody pays for.
A data-plane emulator with no control plane beside it has to invent one. It either allows everything, or reads a hand-written allow list that no real client ever wrote. The behaviour you most need to test is the one part that was never exercised.
Real assignments, written by real clients, enforced by the sibling data plane
An assignment at a resource group applies to everything beneath it, on segment boundaries and case-insensitively. A deny assignment beats the role that granted the same action. An ABAC condition can fail closed on a missing attribute. Each of these is a rule you find out about in production.
Inheritance, deny assignments and the version 2.0 condition language, evaluated
An emulator tested only by its own client is a mirror. The evidence worth having comes from software written by someone else: Microsoft's own SDKs and the az CLI, which have already found two real defects here that our own tests were happy with.
The ledger names the tier of every witness, and the weak ones stay visible
Each capability carries its evidence and an honest status. The tiers are ranked: a CI job driving a packaged external client outranks Microsoft's own SDK in-process, which outranks a test written with our own client on both ends.
ARM-audience bearer tokens whose signature is verified before any claim is read, the 401 and WWW-Authenticate challenge azidentity walks unmodified, ARM's resource-ID grammar, api-version required and validated, the error envelope with correlation headers, and anonymous cloud discovery at /metadata/endpoints.
Built-in role definitions with their real GUIDs, custom definitions whose assignableScopes are enforced rather than merely stored, assignments at any scope with ARM's status codes, duplicate refusal, and scope inheritance on read. Group principals resolve through the token's groups claim, so a user never named in an assignment is authorized through it.
Deny assignments are read-only over ARM as they are in Azure, and they are evaluated: a deny beats the role assignment granting the same action, with wildcards, notDataActions carve-outs, excludePrincipals and doNotApplyToChildScopes all deciding the outcome. The version 2.0 condition language is parsed and evaluated, and a condition ARM would reject is refused at write time rather than stored.
Tenants and subscriptions discovery, resource groups with case-insensitive names and PUT-as-upsert, the subscription-wide resource list the CLI consults when a command names a resource without its group, and an asynchronous group delete where an armresources poller genuinely observes InProgress before Succeeded.
Vault CRUD with tags, SKU and vaultUri, access policies with the add, replace and remove operations, and the RBAC and purge-protection switches fed through to the data plane. Vault-level soft delete is real: a deleted vault holds its name, appears under deletedVaults with a purge date, and comes back through createMode: recover.
Capacity CRUD with the F-series SKU ladder, suspend and resume as real 202 long-running operations, check-name-availability, and the SKU lists armfabric deserialises. Two rows are graded emulated rather than real and stay that way on the page: list_usages reports provisioned capacity units, not consumed compute, and properties.overage is stored without bursting or throttling.
A controllable clock for deterministic token expiry, fault injection to exercise SDK retry paths, and two family feeds that hand effective assignments and Fabric capacities to the sibling data planes. Azure has no public equivalent of the feeds, so there is no ARM behaviour to grade them against, and they are deliberately thin.
Arbitrary resource providers, template and Bicep deployments, management groups and cross-tenant topology, Azure Policy and Resource Graph, private endpoints, Managed HSM, and just-in-time elevation. Each is listed with the reason, and a row appears in the boundary table or the graded tables, never both.
There is no differential harness in this repository, and no run against a real subscription has ever happened. Every green row rests on Microsoft's published REST specifications and on Microsoft's own clients driving the emulator, which is the strongest evidence available here and is still not Azure.
The numbers above are not typed into this page. The build fails if they are.
Every row the parity ledger grades green must name a witness in docs/witnesses.json, and check_witnesses.py --strict fails CI when a claim carries none or names a test that no longer exists. A renamed test leaving a green row behind is a lie the reader cannot see, so the manifest is what makes the claim checkable at all.
ci:a CI job driving a packaged external client. The strongest tier here.
sdk:Microsoft's own management SDK does the talking, in-process.
go:our own client on both ends. Proves only that the emulator agrees with itself.
verifiedcompared against a running Azure subscription. —
— The rest is worth naming rather than burying: a claim whose only witness is a go: test has been proved against nothing external, and the tier exists so that it is visible in the ledger rather than rounded up into the green total.
Two defects on this page were found by third-party clients and not by our own tests: Microsoft's JavaScript and .NET clients join endpoint and scope without normalising, so every request began //subscriptions/, and the redirect to the clean path cost them their Authorization header; and a ten-year certificate that Apple platforms refuse to trust at all. Both would have stayed green under our own client forever.
Coverage is measured in the same build that publishes this page, so the figure in the tile and the badge in the README cannot drift apart. It scores the unit and in-process server suites only. What catches consumer-facing defects is the real-client fleet, which no percentage scores.
One binary, or one container. No database to provision.
go install github.com/calvinchengx/arm-emulator/cmd/arm-emulator@latest
brew install calvinchengx/tap/arm-emulator
winget install calvinchengx.arm-emulator
docker pull ghcr.io/calvinchengx/arm-emulator:latest
Full details, including the entra-emulator pairing, are in installation.
The pair is the point: entra-emulator issues the ARM-audience token, this emulator verifies it.
git clone https://github.com/calvinchengx/arm-emulator
cd arm-emulator
make up # entra-emulator + arm-emulator
make status # is the pair actually usable?
make doctor # what is missing from the toolchain?
Then register the family as a cloud and let the CLI discover the rest of it:
az cloud register -n emulator --endpoint-resource-manager https://localhost:8443
az cloud set -n emulator
az login --service-principal -u "$CLIENT_ID" -p "$CLIENT_SECRET" --tenant "$TENANT_ID"
az group create -n demo -l westeurope
The quickstart carries the working values; authorization walks an assignment from the CLI through to a Key Vault request being allowed, and testing explains what each suite proves.