Synchronizing Device Groups with Entra User Groups using PowerShell
One fun problem I tend to have on a weekly basis is being given a list of usernames and being asked to deploy a software or policy to their devices. Well, ultimately, I need their device names and not user names to properly deliver on the ask. If done manually, you need to find or create a device group, then locate each user and their respective devices (assuming the devices are Intune managed) and add them to the device group. This process can be tedious if the number of users is high. Additionally, this group needs to be maintained, adding and removing devices based on users added and removed from the source group.
I wrote a simple script to make this process easy and consistent.
Script Requirements
Before running the script, ensure that the following requirements are met:
1️⃣ PowerShell Version
- PowerShell 7.0 or later is required to utilize
ForEach-Object -Parallel
for concurrent execution.
Check your version with:
$PSVersionTable.PSVersion
2️⃣ Required PowerShell Modules
- The script relies on the Microsoft.Graph.Beta module.
Install it if not already available:
Install-Module Microsoft.Graph.Beta -Scope CurrentUser -Force
3️⃣ Microsoft Graph API Permissions
- The script requires the following Microsoft Graph API permissions:
Group.ReadWrite.All
DeviceManagementManagedDevices.Read.All
Directory.ReadWrite.All
- These permissions should be granted to the account running the script.
How the Script Works (Step-by-Step Walkthrough)
1️⃣ Connect to Microsoft Graph API
Connect-MgGraph -Scopes "Group.ReadWrite.All", "DeviceManagementManagedDevices.Read.All", "Directory.ReadWrite.All"
2️⃣ Retrieve the User Group
The script searches for an Entra ID user group by its display name:
$userGroup = Get-MgBetaGroup -Filter "displayName eq '$UserGroupName'" | Select-Object -First 1
If the group does not exist, an error is thrown, and the script exits.
3️⃣ Create or Retrieve the Corresponding Device Group
A device group named $UserGroupName-Devices
is required. If it does not exist, the script creates it:
The naming convention can always be modified to suit your needs.
4️⃣ Retrieve All Users in the User Group
To identify the devices to be included in the device group, the script fetches all members of the user group:
$userMembers = Get-MgBetaGroupMember -GroupId $userGroup.Id -All |
Where-Object { $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.user" }
5️⃣ Retrieve Each User’s Managed Devices and Current Group Members (Parallel Execution)
Using ForEach-Object -Parallel
, the script retrieves devices for each user concurrently to maximize performance:
$deviceIdsFromUsers = $userMembers | ForEach-Object -Parallel {
$devices = Get-MgBetaUserManagedDevice -UserId $_.Id -All
$devices | ForEach-Object { $_.AzureAdDeviceId }
} -ThrottleLimit 10
Next, we gather all the current devices in the group:
$currentDevices = Get-MgBetaGroupMember -GroupId $deviceGroup.Id -All |
Where-Object { $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.device" }
$currentDeviceIds = $currentDevices | ForEach-Object { $_.AdditionalProperties.deviceId }
Write-Output "Current device group contains $($currentDeviceIds.Count) device(s)."
6️⃣ Compare Desired vs. Current Device Group Membership
The script then compares the retrieved device IDs against the current device group membership:
$devicesToAdd = $desiredDeviceIds | Where-Object { $_ -notin $currentDeviceIds }
$devicesToRemove = $currentDeviceIds | Where-Object { $_ -notin $desiredDeviceIds }
This step determines which devices need to be added or removed.
7️⃣ Add Missing Devices (Parallel Execution)
Devices that are not currently in the device group but should be are added concurrently:
$devicesToAdd | ForEach-Object -Parallel {
$device = Get-MgBetaDevice -Filter "DeviceID eq '$_'"
$params = @{ "@odata.id" = "https://graph.microsoft.com/beta/directoryObjects/$($device.Id)" }
New-MgBetaGroupMemberByRef -GroupId $using:deviceGroup.Id -BodyParameter $params
} -ThrottleLimit 10
This speeds up the process significantly by leveraging multiple threads.
8️⃣ Remove Devices That No Longer Belong (Parallel Execution)
Similarly, devices that should no longer be part of the group are removed concurrently:
$devicesToRemove | ForEach-Object -Parallel {
$device = Get-MgBetaDevice -Filter "DeviceID eq '$_'"
Remove-MgBetaGroupMemberByRef -GroupId $using:deviceGroup.Id -DirectoryObjectId $($device.Id)
} -ThrottleLimit 10
9️⃣ Final Confirmation
Once all operations are complete, the script outputs a confirmation message:
Write-Output "Device group synchronization complete."
💡 Key Benefits of This Approach
✅ Automated Group Management – Eliminates manual work, reducing administrative overhead.
✅ Real-Time Synchronization – Ensures device groups always reflect the latest user memberships.
✅ Optimized Performance – Uses PowerShell 7’s parallel processing for fast execution.
✅ Scalability – Works efficiently for both small and large organizations.
Full Script:
Bonus
I am not sure if this is a bug or a feature:
# This command works
Get-MgBetaDevice -Filter "DeviceID eq '$deviceID'"
# This does not work
Get-MgBetaDevice -DeviceID $deviceID