Azure Entra nested groups and Directory Sync: Limitations and workarounds
Azure Entra ID doesn't support deep nested group expansion over SCIM, which catches a lot of teams off guard when setting up Directory Sync. This post explains exactly why that limitation exists, three practical patterns to work around it, and how the situation compares to Google Workspace.
If you've set up Directory Sync with Azure Entra ID and noticed that users in child groups aren't showing up in your application, you've hit one of the most common SCIM gotchas in enterprise identity. Here's what's happening and how to fix it.
The problem: SCIM doesn't know about nested groups
Azure Entra ID supports nested group membership: you can put a group inside another group and users will inherit access across Azure's own products. But when Entra provisions users and groups over SCIM, it does not expand nested memberships.
What gets synced is the literal membership of the group you assigned to your application in the Entra Enterprise App configuration. If Group A contains Group B, and Group B contains Alice, only Group B gets synced as a member of Group A, not Alice herself. Your application receives a group with a child group reference, not a flat list of users.
Entra's SCIM provisioning works this way by design. Microsoft's provisioning service can only read and provision users who are immediate members of a group explicitly assigned to the SCIM app. It cannot read or provision users in nested groups. The SCIM spec itself doesn't mandate nested group expansion, and Microsoft's implementation reflects that.
The result: users in nested groups won't be provisioned into your application, and they won't be able to log in. This is the root cause behind a large number of "user not found" issues after a Directory Sync setup with Entra.
Because Entra-to-WorkOS is pure push-based (SCIM) provisioning, WorkOS only ingests whatever Entra sends in its SCIM calls. We don't crawl Entra independently.
What this looks like in practice
A few real-world examples that illustrate how disruptive this can be:
- Near-empty "all engineering" group. One customer had a parent group
Company-Engineering-Allwith three regional child groups (Company-Engineering-UK,Company-Engineering-USA,Company-Engineering-INDIA) nested under it. Entra only provisioned the three direct members of the parent group into WorkOS, not the ~119 users in the nested child groups. The "all engineering" group appeared almost empty in the app, and the vast majority of engineers couldn't log in. - Unexpected flat list of groups. Another customer expected to see a hierarchy where parent and child groups were distinct and expandable, similar to how other enterprise platforms and Google Workspace present nested groups. Instead, Entra delivered a flat list of groups to WorkOS with no parent/child relationship and no way to infer nesting. This broke assumptions baked into their authorization model.
- Silent permission failures. A common symptom across multiple customers: users show up in WorkOS directories but have empty
groupsarrays where the admin expected memberships. Downstream app-level provisioning, like "put anyone in Group X into Org Y / Role Z," silently fails because the group membership was never sent.
This pattern comes up repeatedly across Entra-heavy tenants with 1,000+ users who already use nested groups in their internal RBAC and assume that hierarchy will "just work" via SCIM.
Three patterns that actually work
There's no single right answer here. The best approach depends on how your customer's Entra directory is structured and how much control they have over it.
1. Flatten the group membership
The most straightforward fix: restructure the group so all intended users are direct members of the group assigned to the application, not buried in child groups. Ask their IT admin to create a dedicated provisioning group (e.g., app-yourapp-users) and add every user directly.
Best fit: Works well when the number of affected groups is small (e.g., a handful of "all employees," "all engineers," "all managers" groups) and IT is comfortable adjusting Entra group membership rules. It's also a good fit when the main goal is one policy group per product role and the underlying Entra hierarchy is mostly for IT's own organization, not something the app needs to mirror exactly.
Tradeoffs: This approach requires the customer's IT team to create and maintain a parallel group structure specifically for your app. That's a reasonable ask for a handful of groups, but it doesn't scale well if the customer has dozens of nested groups they need to provision. It also means the customer now has two sources of truth for membership: their original nested structure and the flattened provisioning group. If someone forgets to add a new hire to both, the user falls through the cracks. Dynamic group rules in Entra (where membership is computed from user attributes like department == "Engineering") can reduce this maintenance burden significantly.
2. Assign child groups directly to the application
Instead of assigning one top-level group to the Entra Enterprise App, assign each child group separately, e.g., Company-Engineering-UK, Company-Engineering-USA, and Company-Engineering-INDIA alongside the parent. Entra provisions each group and its direct members independently. Your application receives multiple flat groups, and you union the memberships to determine access.
Best fit: Good for larger tenants (hundreds to thousands of users) that already think in terms of location- or BU-specific groups and want to model permissions that way (e.g., HR global vs. HR per-region, engineering by office). Also works where the app already has its own "team" concept and just needs flat groups as inputs, not a full tree.
Tradeoffs: The customer's IT admin has to explicitly assign every relevant child group to the Enterprise App. For a handful of groups, this is fine. For a deeply nested hierarchy with dozens or hundreds of groups, it becomes an ongoing maintenance task whenever new child groups are created. You also lose the parent/child relationship on the receiving end: WorkOS sees each group independently, so your application logic needs to handle the mapping from multiple flat groups back to any hierarchical concepts you need internally.
3. Supplement SCIM with the Microsoft Graph API
For customers with complex, deeply nested group hierarchies they can't or won't flatten, the Graph API is the escape hatch. Microsoft Graph supports transitive member lookups. You can query GET /groups/{id}/transitiveMembers to retrieve all users recursively across nested groups.
The pattern here is to use Directory Sync for the base provisioning flow, then use a scheduled Graph API call to reconcile transitive memberships that SCIM missed. Separately, the customer (or a backend they control) calls Microsoft Graph to query transitiveMembers / memberOf to compute true nested membership trees, then either:
- Stores that mapping in their own system and uses WorkOS Directory Sync just for identity and IDs, or
- Syncs a flattened list back into Entra groups that are then assigned to the SCIM app.
Best fit: Typically for large enterprises with strong internal IT or infra teams and existing automation around Entra (PowerShell/Graph scripts, ServiceNow, etc.). Most relevant when customers need to preserve a deep, multi-level hierarchy for audits or compliance but only need flattened outputs for SaaS app authorization.
Tradeoffs: This is the most powerful option but also the most complex. It requires the customer to build and maintain a separate integration with the Graph API, handle authentication (app registrations, client secrets or certificates, token management), and run the reconciliation on a schedule. There's inherent latency: SCIM provisioning happens near-real-time (within Entra's 20–40 minute sync cycle), but the Graph-based reconciliation only runs as often as the scheduled job. The customer also needs to decide how to handle conflicts between what SCIM says and what the Graph query returns. For most teams, this is a "we already have Graph automation" pattern, not a "let's build Grap
How this compares to Google Workspace
Google Workspace handles nested groups differently. WorkOS's Google connector is pull-based: it pulls from Google's Admin APIs, which natively support expanding nested groups, allowing WorkOS to compute transitive membership when syncing. Google Directory Sync via WorkOS explicitly supports nested groups.
This means the same directory structure that causes headaches with Entra "just works" with Google. For a Google "all engineering" group that contains child groups, users in those child groups can be treated as members of the parent when syncing into WorkOS. For Entra, that same structure does not expand through SCIM. Only direct memberships on the assigned group are visible, and parent/child relationships are lost.
If you're supporting both identity providers, keep the following in mind:
- Don't assume feature parity between connectors. Nested group expansion works with Google but not with Entra via SCIM. If your onboarding flow or docs describe group syncing generically, you'll set wrong expectations for Entra customers.
- Test with nested groups during integration QA. If you only test with flat group structures, you won't catch this until a customer hits it in production.
- Surface the difference in your admin-facing docs. A short callout in your Directory Sync setup guide ("If you're using Entra and your groups contain child groups, see our guide on nested group limitations") goes a long way toward heading off support tickets.
What to check first
When a customer reports missing users after setting up Directory Sync with Entra, run through this list:
- Check for nested groups. Ask the customer whether the group(s) assigned to the Enterprise App contain child groups. This is the most common cause of missing users after an Entra Directory Sync setup.
- Verify group assignment in the Enterprise App. Confirm that the correct groups are assigned under Users and Groups in the Entra Enterprise App configuration. Groups that aren't explicitly assigned won't be provisioned.
- Inspect the provisioning logs. Entra's provisioning logs (under the Enterprise App's Provisioning blade) show exactly which users and groups were synced (or skipped). Look for users that should have been provisioned but don't appear in the logs at all, which is a strong signal they're in a nested group that Entra didn't expand.
- Check for scoping filter issues. If the customer is using scoping filters in the Entra provisioning configuration, confirm those filters aren't inadvertently excluding users.
- Confirm the provisioning cycle has completed. Entra's provisioning service runs on a 20–40 minute cycle. If the customer just assigned a group, the users may not have been synced yet.
- Look at the user's group memberships in WorkOS. If the user exists in the WorkOS directory but has an empty
groupsarray, the user was likely provisioned as a direct assignment or through a different group, and the expected group membership was lost due to nesting.
Nested group expansion is one of those Entra behaviors that isn't well-documented in the context of SCIM provisioning. Once you've seen it cause an outage once, you learn to check for it upfront. Build the question, "Does your assigned group contain any child groups?", into your standard Directory Sync onboarding checklist. It'll save you a support ticket every time.