Cerberus field notes

Azure Lighthouse doesn't project permissions onto data-plane APIs. Here's what breaks.

Azure Lighthouse delegation works for ARM control-plane calls but silently fails for data-plane APIs like Metrics Batch. What breaks, why, and the fallback that works at scale.

If you're building multi-tenant Azure tooling for MSPs, Azure Lighthouse looks like the obvious foundation. Delegated resource management, no guest accounts, customers deploy one ARM template and your service principal sees their subscriptions. We built our Lighthouse integration, tested it end to end on staging, and shipped it.

Then metrics collection failed in production. A 100% failure rate, on an API that worked perfectly against our own tenant.

The discovery

The failing call was the Azure Monitor Metrics Batch API, the endpoint designed for exactly our workload: bulk metric retrieval across large resource sets. Same service principal, same permissions, same code that passed staging. The only variable was that production traffic went through Lighthouse delegation.

The explanation took real digging, because I could find it documented almost nowhere in a form you'd encounter before hitting it. Azure Lighthouse projects authorisation onto the ARM control plane only. Data-plane APIs never see your delegated permissions.

The Metrics Batch API is a data-plane endpoint. It lives on regional endpoints like metrics.monitor.azure.com rather than management.azure.com. When your delegated principal calls it against a customer resource, the token is valid, the RBAC role would be sufficient, and the request still fails, because Lighthouse's authorisation projection does not extend there. Nothing warns you. The template deploys, the delegation shows healthy, control-plane calls succeed, and the data plane returns authorisation errors that look like a misconfiguration on your side.

Why this is easy to ship into production

Control-plane coverage is broad enough that everything you test first will pass: resource enumeration, ARM reads, Cost Management queries. The failure only surfaces on the subset of APIs that happen to be data-plane, and Azure gives you no property to filter on to know which those are in advance. You discover the boundary by hitting it.

Other casualties of the same boundary, for anyone keeping a list: storage data operations, Key Vault data access, and Log Analytics query endpoints. Any API where the resource itself authorises the call rather than ARM.

The fallback that works

The classic ARM metrics path at management.azure.com/.../providers/microsoft.insights/metrics is a control-plane API, so it works fully through Lighthouse. It's chattier than the batch endpoint. One call per resource instead of bulk retrieval means real throttling engineering: request pacing against ARM limits, retry with backoff, and careful batching of the inventory side through Azure Resource Graph, which is also control-plane and Lighthouse-safe.

Rebuilt on that path, the numbers from a recent production account: 802 resources, 1.6 million metric data points written, 97% first-pass success rate, with the remainder recovered on retry. Slower per call than the batch API, entirely workable at MSP portfolio scale, and functioning through delegation, which is the part that matters.

What to take from it

If your multi-tenant architecture assumes Lighthouse, audit every API you call and classify it as control-plane or data-plane before you build. The boundary is invisible when testing against your own tenant, where everything works, and absolute in production, where delegation is the path. Design your data collection around control-plane equivalents from the start, and treat any data-plane dependency as requiring a separate auth strategy.

We build Cerberus, an Azure FinOps platform for MSPs. It collects multi-tenant cost and usage evidence through exactly this kind of delegation, which is how we got to learn this the interesting way.