Microsoft Entra is the Zero Trust policy enforcement engine sitting at the intersection of identities, endpoints, AI agents, networks and data. Every conversation about Conditional Access, every Privileged Identity Management workflow, every access package review, every token issued to an application or an agent: it all funnels through Entra. That is the entire point of the product, and that is why so much of the security industry's energy is spent hardening it.
The most interesting thing happening to Entra right now is not a new policy type. It is extensibility. In the last few years Entra has quietly become one of the most extensible identity platforms in the enterprise market. Custom authentication extensions let your code shape token issuance and authentication events. External authentication methods plug third-party MFA providers into the sign-in pipeline. PIM custom extensions let your business logic approve or deny privileged role activation. Lifecycle workflow custom task extensions let your code influence whether an employee account is enabled, disabled, or transformed. Entitlement management talks to Logic Apps for governance workflows, and a specific variant, dynamic approval, lets your code decide who approves an access package request, or whether the request should be approved at all. More extension points are coming. The trend line is obvious.
This is genuinely good. Identity decisions are business decisions, and business logic belongs in code your business writes. Extensibility is what turns Entra from a fixed product into a programmable enforcement engine, and that is the right direction.
It is also the part where the security conversation almost always stops.
The reframe nobody wants to write
Every extension point hands a piece of the Entra trust boundary to a system Entra does not own. The decision is still announced from Entra. The signal that drives the decision is shaped somewhere else. That somewhere else is now Control Plane, in the language of Microsoft's current Enterprise Access Model (what the legacy tiered administration model used to call Tier 0). Control Plane is the set of systems that grant access to everything else: the directory itself, the privileged groups inside it, and now, by extension, any code that shapes a directory decision at runtime.
Sit with the implications for a minute, scenario by scenario:
- A PIM custom extension can influence whether an identity gets a high-privilege role activated, in milliseconds, with no human in the loop. The endpoint that responds to PIM is now part of your privileged-access path.
- A lifecycle workflow custom task extension can be the anchor that decides whether a leaver's account is actually disabled tomorrow morning. The code that answers that workflow call is now part of your joiner-mover-leaver process.
- A Logic App driving dynamic approval can decide who is allowed to approve a request for a sensitive access package, or whether the request gets pre-approved with no human reviewer at all. The Logic App is now part of your access governance.
- An Azure Function backing a custom claims provider can inject custom claims into the access tokens your applications consume. The Function is now part of your token issuance.
Every one of those endpoints is a Control Plane system. Not "important." Not "in scope for the next pentest." Control Plane. The same category you reserve for your domain controllers, your Entra Connect server, your root CA, and the accounts that can write to any of them.
Most security writing about Entra extensibility focuses on the Entra side: how to register the extension, what scopes it needs, how the audit log captures the call. That work is necessary and well covered. The Azure side, where the actual code runs, is barely covered at all. That is the gap I want to close.
The Cloud Adoption Framework already saw half of this
The Azure Cloud Adoption Framework and the Azure landing zones reference architecture do something deliberate: they split platform landing zones from application landing zones, and inside platform they carve out a dedicated identity subscription for identity-related infrastructure. The subscription organization guidance treats identity as its own thing, not as a workload that shares a subscription with marketing analytics.
That is exactly the right instinct. The problem is that the identity subscription is almost always described as the home of legacy identity infrastructure: Active Directory Domain Services domain controllers, Entra Connect, a Privileged Access Workstation jump box, maybe a few related VMs. It is described as the place where the identity infrastructure that runs in Azure lives.
It is not yet described as the place where the Azure compute that influences Entra decisions at runtime lives. That is a different category, and it did not really exist five years ago in the volume it exists now. A landing zone designed in 2021 was not built to host the Function App behind your custom claims provider, the Logic App behind your dynamic approval policy, the queue-triggered Function that decides whether a lifecycle workflow disables an account. None of that was a workload pattern then.
It is now. And those workloads are at least as sensitive as Entra Connect, because they execute on every relevant identity event, and a successful tamper does not generate the noisy reconfiguration footprint that touching Entra Connect would generate. They are quieter and at least as powerful.
The Azure RBAC inheritance chain is the trap
Here is the part of the picture that does not get said out loud enough. Azure role-based access control inherits top-down, and the inheritance is unbreakable. A role assignment at a resource, resource group, subscription, management group, or the tenant root management group flows downward to every resource underneath. Contributor at the resource group flows to the resource. Contributor at the subscription flows to every resource group in it. Contributor at the management group flows to every subscription. Contributor at the root management group flows to everything.
This is fine for most workloads.
It is corrosive for workloads that influence Entra decisions.
That sentence is the entire argument of this article. Read it once more. The same RBAC model that makes Azure pleasant to operate at scale is the model that lets a Contributor four management groups above your Function App quietly become the author of the claims your applications trust. There is no Entra-side hardening that fixes this. There is no Conditional Access policy that fixes this. The fix is structural, and it lives in Azure.
Picture an organization with a moderately mature Azure footprint. There is a platform management group, an application management group, a couple of regional management groups, a Sandbox management group, a Production management group. The custom claims provider Function App lives in a shared "platform services" subscription because that is where the platform team puts platform things. Now ask the inheritance question: who is Contributor on that subscription? Who is Contributor on its parent management group? Who is Contributor on the root? In most organizations the answer is "more people than you think, and at least one service principal you forgot about." Any one of them can replace the deployed code on that Function App. They do not need to touch the source repository. They do not need to break the CI/CD pipeline. They just deploy a new ZIP. The next time Entra calls your custom claims provider, it is calling their code.
Source-control hardening is necessary and frequently celebrated. It is also not sufficient on its own, because Entra does not invoke your repository. Entra invokes the runtime.
Where these resources actually belong
My direct take. The Azure resources that host code which influences Entra decisions are Control Plane resources. They belong either:
- directly under the root management group, in a dedicated subscription that exists for exactly this purpose, or
- one level deeper, under a dedicated Control Plane management group that itself sits directly under the root.
Not deeper. Not in the platform identity subscription where your domain controllers live. Not in a shared services subscription. Not in an application landing zone, ever. The blast radius of "any Contributor anywhere on the inheritance path can swap the runtime" forces the resources up the tree, into a place where the list of principals with write rights is short, audited, and reviewed.
That choice has follow-on consequences. Resources at the top of the hierarchy do not benefit from the policies and guardrails the landing zone reference architecture applies further down. The dedicated Control Plane management group needs its own policy set: deny-by-default network rules, mandatory diagnostic settings to a tenant-level log analytics workspace, deny-create policies for everything except the resource types you actually need, and Resource Locks at the resource group level that survive the people who set them. Pinning these things at the top of the tree means designing them at the top of the tree.
The questions you must be able to answer
Before any Entra extension goes into production, the team that owns it has to be able to answer each of these without hedging:
- Who controls the source code? Specifically, who has push rights on the protected branch, who can bypass branch protection, and who can approve a pull request?
- Who controls the CI/CD pipeline? Who can edit the pipeline definition? Who can re-run a failed deployment with a different artifact?
- What identity does the pipeline run as when it deploys to Azure? If it is a service principal with a client secret or a stored certificate, treat that credential as already known to the threat actor.
- Who can directly replace the runtime, bypassing source and pipeline entirely? Walk the full RBAC inheritance chain from the resource up to the root, including every group, service principal, and managed identity with Contributor, Owner, or User Access Administrator.
- Where do the credentials this extension uses to call downstream systems live? Who has Owner on that Key Vault? Walk the inheritance chain again.
- Does the extension run with a system-assigned managed identity, a user-assigned managed identity shared with other workloads, a certificate in a Key Vault, or a client secret? Each answer has a different blast radius.
- How fast can you detect that the deployed code on the Function App or the deployed logic on the Logic App has changed? How fast can you detect that a role assignment was created on the hosting subscription or one of its parents?
If any of those questions is uncomfortable, the extension is not yet ready to be invoked by Entra in production.
What comes next
The two scenarios I want to walk in detail are a custom claims provider backed by an Azure Function and a dynamic approval Logic App backing an entitlement management access package. Both are concrete. Both are increasingly common. Both expose the entire chain: source repository, pipeline identity, runtime resource, hosting subscription, RBAC inheritance, downstream credentials, Key Vault, the lot.
And both lead into a question that deserves a fair, opinionated answer: what credential should that code use when it has to call back into Microsoft Graph or out to an external system? Static API key, certificate, or managed identity? Each option has a real failure mode and a real guardrail. The right answer is rarely the convenient one.
That is the subject of the next article in this series. Where Part 1 makes the case that Entra extensibility is Control Plane, Part 2 will walk the two examples end to end and grade the credential ladder beneath them. Watch this space.
The goal of this series is not to talk anyone out of using Entra extensibility. The opposite. Use it. It is what makes Entra a great product. Just host the code that influences Entra decisions the same way you host the things Entra is protecting.
References
- Custom authentication extensions overview
- Manage an external authentication method in Microsoft Entra ID
- PIM custom extensions for role activation
- Lifecycle workflow extensibility
- Entitlement management Logic Apps integration
- Externally determine approval requirements for an access package using custom extensions (dynamic approval)
- Azure Cloud Adoption Framework overview
- Azure landing zones
- Landing zone subscription organization and governance
- Landing zone identity and access management
- Azure RBAC overview
- Azure management groups
- Enterprise Access Model (Control Plane / Management Plane)
Top comments (0)