Windows 365 provisioning policies define what a Cloud PC looks like – the image, network, region, and license type assigned to a user. You assign them via Microsoft Entra ID groups. That part is straightforward.
What’s less obvious is what happens when a user ends up in more than one of those assignment groups. Maybe a user was moved into a new department group. Maybe a nested group you didn’t fully audit also targets a provisioning policy. Maybe two teams independently created policies and both targeted the same broad license group.
The result is that the user is technically targeted by multiple provisioning policies simultaneously. Windows 365 will still provision them but it applies its own internal precedence logic to decide which policy wins, and that logic isn’t surfaced anywhere in the Intune admin centre.
Microsoft says When a user is a member of multiple groups assigned to different provisioning policies, Windows 365 uses internal precedence to determine which policy applies. The “first assigned” policy takes effect, but this ordering cannot be reliably inferred from the Graph API response order.
In practice, this means your provisioning outcomes become unpredictable and unless you’re actively looking for it, the overlap is invisible. There’s no native alert, no conflict indicator in the portal, nothing.
That’s the gap this script is designed to close.
What the script does
The script, Get-W365ProvisioningPolicyOverlap.ps1, uses Microsoft Graph to enumerate all Windows 365 provisioning policies and their group assignments, then checks whether a given user or any user across your entire tenant is a member of more than one of those groups.
It runs in two modes:
| Mode | What it does | Best used for |
|---|---|---|
| Single-user | Checks one UPN against all provisioning policy assignment groups using /checkMemberGroups (transitive by default) | Investigating a specific user complaint or pre-provisioning validation |
| Report | Enumerates all assignment groups, expands their members, and identifies every user with policy overlap across the tenant | Periodic tenant hygiene audits or post-migration review |
The report mode also supports -TransitiveMembers, which switches the Graph endpoint from /members to /transitiveMembers. This is the flag you actually need in most real tenants – nested groups are common, and direct-only membership checks will miss users who inherit group membership through a parent group.
Graph API permissions required
The script needs four delegated scopes, which it validates at connection time and will prompt to reconnect if any are missing:
User.Read.AllGroup.Read.AllCloudPC.Read.AllDirectory.Read.All
It uses the beta endpoint for provisioning policies (/beta/deviceManagement/virtualEndpoint/provisioningPolicies) since that’s where the $expand=assignments capability is exposed. The member/user lookups use the stable v1.0 endpoints.
Important The script is read-only. It makes no changes to Entra ID, Intune, or Windows 365 configuration. It calls Graph with GET and POST methods only (checkMemberGroups requires a POST body). Safe to run in production.Running it
Single-user interactive prompt
# Prompts for UPN, validates format and Entra ID existence before running
.\Get-W365ProvisioningPolicyOverlap.ps1Single-user non-interactive
.\Get-W365ProvisioningPolicyOverlap.ps1 -UserPrincipalName john.doe@contoso.comTenant report for direct members only
.\Get-W365ProvisioningPolicyOverlap.ps1 -ReportTenant report include nested group membership
.\Get-W365ProvisioningPolicyOverlap.ps1 -Report -TransitiveMembersTenant report export overlap results to CSV
.\Get-W365ProvisioningPolicyOverlap.ps1 -Report -TransitiveMembers -OutCsv .\w365-overlaps.csvThe CSV export produces a flattened row per user-group-policy combination, which makes it straightforward to load into Excel or Power BI if you’re doing a broader provisioning audit.
What the output looks like
In single-user mode, the script prints which assignment groups the user belongs to, which provisioning policies those groups map to, and a clear overlap flag. If no overlap is detected, it says so no false positives.
In report mode, it walks through each assignment group, prints every user member alongside the policy that group assigns, then surfaces a summary table of users with confirmed overlaps. The per-user section then expands the full group-to-policy mapping so you can see exactly which groups are causing the collision.
The output is intentionally verbose in report mode. You want the paper trail when you’re making remediation decisions.
A note on precedence
The script tells you whether an overlap exists it doesn’t tell you which policy will win.
Microsoft’s provisioning precedence is based on assignment order, which is internal state that can’t be reliably read from the Graph response. Any tool that claims to predict which policy “wins” is likely guessing based on response ordering, which is not guaranteed to be stable.
Windows 365 Enterprise applies one provisioning policy per user. If a user is targeted by multiple provisioning policies, the service applies the first assigned policy. This precedence reflects internal assignment sequencing that is not exposed via the Graph API.
The right remediation is to ensure each user belongs to exactly one provisioning-policy assignment group. The script gives you the data to make that happen the fix itself is an Entra ID group membership change.
Requirements
- PowerShell 5.1 or PowerShell 7+
- Microsoft Graph PowerShell SDK (
Install-Module Microsoft.Graph) - An account with the delegated permissions listed above (Global Reader covers most of them)
- Admin consent granted for the required scopes in your tenant
The script uses device code flow for authentication (Connect-MgGraph -UseDeviceCode), so it works from any machine including those without a browser-based auth prompt — handy for remote sessions.
Download
The full script is available on GitHub. It’s a single .ps1 file with no external dependencies beyond the Graph SDK.
