Intune-SetPrimaryUsers.ps1 – Version 6.0: 10x Faster, Smarter, and More Reliable
When I released version 5.0 of Intune-SetPrimaryUsers.ps1, the goal was clear: simplify the process of assigning Primary Users in Intune while making the script easier to run in Azure Automation. That release consolidated dependencies, introduced Invoke-MgGraphRequest, and improved overall stability. But performance was still a sticking point. Large tenants with thousands of devices could see runtimes stretch into hours. The culprit wasn’t the script itself, it was the way Microsoft Graph exposed the Primary User relationship.
Now, with version 6.0, I’m excited to share the biggest leap forward yet:
- Up to 10x faster execution thanks to new Graph capabilities
- More resilient error handling fallback and prevention on common issues
- Cleaner reporting and logging Nice fast logging function
This update isn’t just an incremental improvement, it’s a fundamental shift in logic, how the script interacts with Graph and how logging and reporting works.
👉 Grab the new version here: GitHub – Intune-Set-PrimaryUsers.ps1
Why Primary User Matters in Intune
Before diving into the technical details, let’s revisit why this matters. The Primary User property in Intune is more than just metadata:
- It determines which apps appear in the Company Portal for that user
- It drives visibility in the My Devices portal
- It’s critical for license compliance and device lifecycle management
- It simplifies helpdesk workflows, ensuring the right user is tied to the right device
In short: if Primary User is wrong, your end users feel it. Automating this process ensures accuracy, consistency, and scalability.
The Bottleneck in Version 5.0
In v5.0, the script had to:
- Query devices from Intune
- Batch query every device for it´s primary user
- Get all Windows Sign-in logs (and this could fail)
- Cross-reference sign-in logs, user objects and device objects
This worked, but it was chatty. Each device could require several requests, and in large environments, throttling and long runtimes were a fact.
What Changed in Microsoft Graph
The breakthrough came when Microsoft Graph introduced the ability to retrieve the Primary User directly from the device object.
This means:
- One call per device instead of multiple lookups
- No more traversing relationships across endpoints
- Less chance of throttling due to reduced request volume
For this automation, this is a game-changer. It allowed me to completely refactor the script’s logic around a leaner, faster query model.
What’s New in Version 6.0
Here’s a breakdown of the improvements:
Performance
- Direct Graph query to get all user objects (instead of batches)
- Execution time reduced by up to 90% in large tenants
- Scales smoothly across thousands of devices
Error Handling
- Fallback solution for environments with missing Windows Sign-in logs. It will then try to get Microsoft Intune Windows Agent as a fallback
- Smarter error handler for Graph errors
- Clearer error messages with actionable context
Reporting
- New Logging function that makes the logging more intelligent. You can log to GUI, disk or even event log (does not work in azure automation)
- Detail report now in better table view for Azure Automation
Compatibility
- Works also on iOS, MacOS and Android
- Supports both Windows PowerShell 5.1 and PowerShell 7.x and Azure Automation
- Requires only the Microsoft.Graph.Authentication module
Performance Boost
Enumerate Current Primary Owner
I happened to find that beta graph of devices in the managedDevices requests also had a UserID property that reflected the current primary user. This made it much more easier to enumerate owners. So for an environment with 1000 devices, instead of getting every devices primary owner in 1000 calls, I can now get all devices and all primary owner guid in just 1 call. Major boost!
Hash tables
To faster find all info on a primary owner by using the primary owner guid, I did one query to get all users and converted the result to a hash table with the guid as identifier. Blistering fast lookups compared to search the array. So all arrays are converted to hash tables for fast lookups.
Error handling to increase performance
I also discovered that if for example windows sign-ins did not exist in Entra sign-in logs, the query for the first 999 logs for the last 30 days could take ages. So now the script do a quick lookup for 1 event during last 24 hours. If none is found, it can use windows fallback to use Microsoft Intune Windows Agent. A bit more noise but should work for all environments.

And after some testing with large environments, I found that the sign-in logs collection can take long time to get and fail du to the timeouts. So I decided to device the queries into chunks of 7 days to prevent these timeout.

Support for iOS, MacOS and Android
The previous version of the script had a parameter to collect other OS´s. But it actually only worked with Windows. It did only get the sign-in logs for Windows Sign-ins, and could not find a Most Frequent User on other OS´s. Now the script support other OS´s by using filtered queries for each OS based on App ID:

New Logging function
The more logging the less performance! But although logging is excessive and makes execution time longer. We sometimes need to troubleshoot. I have been poking around with a logging function “Invoke-TboneLog” for a while now. And I have added it to this script. This function can write to GUI, Disk or Eventlog. But, it is completely optional. The function just proxy all write- functions and return the text with colour code and more info:
If removed, the script is using correct write- functions throughout the script. Just remove the lines with the function call for “Invoke-TboneLog” to skip using it.
Azure Automation Verbose Logging
Verbose logging can be enabled in my script, and supported by my logging function. But in Azure Automation, it also needs to be enabled in the Runbook:

How to Get Started
- Download the latest script: Intune-Set-PrimaryUsers.ps1
- To setup Azure Automation, Have a look at the old post for version 5 Set Intune Primary User with Azure Automation
- Assign the required Graph API permissions:
- DeviceManagementManagedDevices.ReadWrite.All
- AuditLog.Read.All
- User.Read.All
- Run in Test Mode first: .\Intune-Set-PrimaryUsers.ps1 -TestMode $true
- Review the output, then run in production mode.
So, go ahed and test it out and report issues on Github.
👉 Grab the new version here: GitHub – Intune-Set-PrimaryUsers.ps1

