Retrieve Intune Device Primary User and all users ever logged on to that device

One of our customers wanted to know per device which the real Primary User was and the user logon date of every user that used that device. (They have some shared devices.) In this blog post, I will show you how to retrieve that information from Intune and export it.

What is a Primary User?

“The primary user property is used to map a licensed Intune user to their devices in:

  • The Company Portal app
  • End-user website
  • IT pro experiences, like troubleshooting pages in the Azure portal. These pages map user accounts to devices by using the primary user.”

Source: https://learn.microsoft.com/en-us/mem/intune/remote-actions/find-primary-user

What does the script do?

It uses Microsoft Graph to connect to your tenant. It retrieves all the Intune devices and reports the Primary User and all users that logged into it with their last logon date. The results are outputted in a CSV file, which must be specified when running the script.

You can use the -Filter parameter to search for specific devices. For example, “-Filter PS” will return info for devices matching PS.

Running the script

Below is the output of the script running on the customer’s tenant. I changed all the device names, models, serial numbers, and user names for privacy. 🙂 You will be prompted for credentials if the account doesn’t have enough permissions to retrieve the required data… Then, you will be prompted to grant permissions.

Note: If the required PowerShell modules are not installed, the script will install them for you.

c:\scripts\Get-IntunePrimaryUser.ps1 -OutputFileName c:\temp\PrimaryUser.csv

When started, it will display the devices it checks and if the CSV export was successful. (Below is just a part of the total output.)

Processing DESKTOP-0U4ELUR...
Processing DESKTOP-1P7IVLI...
Processing DESKTOP-21H9G3N...
Processing DESKTOP-22BOU9L...
Processing DESKTOP-2J0P2E2...
Processing DESKTOP-68HRPDT...
Exported results to c:\temp\PrimaryUser.csv

This will look like this in Excel: (Below is just a part of the total output, and I changed some names.)

You can see in the results that some devices have multiple accounts which were logged into them. It shows who and when for those. It displays the Primary User (Or None if the Primary User can’t be found; this is the case if that account was deleted, for example)

The script

Below are the script’s contents; save it to c:\scripts\Get-IntunePrimaryUser.ps1, for example.

param(
    [parameter(Mandatory = $true)][string]$OutputFileName,
    [parameter(Mandatory = $false)][string]$Filter = ''
)

#Check if necessary modules are installed, install missing modules if not
if (-not ((Get-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -ListAvailable).count -ge 3 | Select-Object Name -Unique)) {
    Write-Warning ("One or more required modules were not found, installing now...")
    try {
        Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -Confirm:$false -SkipPublisherCheck -Scope CurrentUser -ErrorAction Stop
    }
    catch {
        Write-Warning ("Error installing required modules, exiting...")
        return
    }
}


#Connect MgGraph
try {
    Connect-MgGraph -Scopes 'DeviceManagementManagedDevices.Read.All, User.Read.All' | Out-Null
} 
catch {
    Write-Warning ("Error connecting Microsoft Graph, check Permissions/Accounts. Exiting...")
    return
}

#Loop through the devices and the logged on users per device 
$total = Foreach ($device in (Get-MgBetaDeviceManagementManagedDevice -All:$true -Filter "contains(DeviceName,'$($filter)')" | Where-Object OperatingSystem -eq Windows)) {
    Write-Host ("Processing {0}..." -f $device.DeviceName) -ForegroundColor Green
    foreach ($user in $device.UsersLoggedOn.UserId | Select-Object -Unique  ) {
        [PSCustomObject]@{
            Device            = $device.DeviceName
            Model             = $device.Model
            SerialNumber      = $device.SerialNumber
            "Users logged in" = (Get-MgUser -UserId $user).DisplayName
            LastLogon         = ($device.UsersLoggedOn | Where-Object Userid -eq $user | Sort-Object LastLogonDateTime | Select-Object -Last 1).LastLogOnDateTime
            PrimaryUser       = if ((Get-MgBetaDeviceManagementManagedDeviceUser -ManagedDeviceId $device.Id).DisplayName) {
                $((Get-MgBetaDeviceManagementManagedDeviceUser -ManagedDeviceId $device.Id).DisplayName)
            }
            else {
                "None"
            }
        }
    }
}
Disconnect-MgGraph | Out-Null

try {
    $total | Sort-Object Device, 'Users logged in' | Export-Csv -Path $OutputFileName -NoTypeInformation -Encoding UTF8 -Delimiter ';' -ErrorAction Stop
    Write-Host ("Exported results to {0}" -f $OutputFileName) -ForegroundColor Green
}
catch {
    Write-Warning ("Error saving results to {0}, check path/permissions..." -f $OutputFileName)
}

Download the script(s) from GitHub here.

71 thoughts on “Retrieve Intune Device Primary User and all users ever logged on to that device

  1. Have you ever needed to run this against a tenant with more than 1,000 devices? Script works great, just bumping up against the Graph 1,000 limit.

  2. I am seeing following error while running this script. Any where am I making mistake ?

    PS C:\scripts> c:\scripts\Get-IntunePrimaryUser.ps1 -OutputFileName c:\temp\PrimaryUser.csv
    Get-MgBetaDeviceManagementManagedDevice : The term ‘Get-MgBetaDeviceManagementManagedDevice’ is not recognized as the name of a cmdlet, function, script file, or operable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At C:\scripts\Get-IntunePrimaryUser.ps1:15 char:31
    + … Foreach ($device in (Get-MgBetaDeviceManagementManagedDevice | Where …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (Get-MgBetaDeviceManagementManagedDevice:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    Exported results to c:\temp\PrimaryUser.csv

      1. I got the desired output after my computer restart. It appears computer restart is required after installing the module. Your updated script works great!!!

  3. Hello Harm,

    Script exports only 950 devices against 32000. Do we need to remove any restriction to export data for 32000 devices ?

  4. Craig asked the same thing, just above your post, and I don’t have any experience in that (didn’t have a tenant with that many devices to test it on)

    I will look into this, there must be an override to retrieve more data

  5. I can’t seem to get it to work. When running the script from Github, I get the green text “Exported results to…” but the csv is empty, containing just “” in A1 cell. We have about 60 joined devices. I tried to restart the computer as someone mentioned above but nothing. Do you have any idea?

    1. You’re right, there was an issue after I added the -Filter parameter. The query was wrong when no Filter was used, changed the default value to ” instead of ‘*’. It should work now, let me know the results!

      1. Could you post the error that you’re getting? And could you run the script with the -Verbose parameter, too? (And see if it outputs more detailed error information?)

      2. The scripts is not prompting anything wrong, i’m getting a output-file Containing just “” in A1 cell.

      3. What does this give you?

        Connect-MgGraph -Scopes ‘DeviceManagementManagedDevices.Read.All, User.Read.All’
        Get-MgBetaDeviceManagementManagedDevice -All:$true -Filter “contains(DeviceName,’$($filter)’)” | Where-Object OperatingSystem -eq Windows

        Did you download the last version from the blog post or my GitHub page? (https://raw.githubusercontent.com/HarmVeenstra/Powershellisfun/refs/heads/main/Retrieve%20Intune%20Primary%20User/Get-IntunePrimaryUser.ps1)

      4. It gives me:

        Get-MgBetaDeviceManagementManagedDevice : The term ‘Get-MgBetaDeviceManagementManagedDevice’ is not recognized as the name of a cmdlet, function, script file, or operabl
        e program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
        At line:2 char:1
        + Get-MgBetaDeviceManagementManagedDevice -All:$true -Filter “contains( …
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : ObjectNotFound: (Get-MgBetaDeviceManagementManagedDevice:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

      5. The module Microsoft.Graph.Beta.DeviceManagement is not installed in your session, could you run this?

        if (-not ((Get-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -ListAvailable).count -ge 3 | Select-Object Name -Unique)) {
        Write-Warning (“One or more required modules were not found, installing now…”)
        try {
        Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -Confirm:$false -SkipPublisherCheck -Scope CurrentUser -ErrorAction Stop
        }
        catch {
        Write-Warning (“Error installing required modules, exiting…”)
        return
        }
        }

  6. Hi Harm, brilliant script. I’m a newbie in script, so kindly can you help me, telling how to get serial number? I believe that cmdlet can get this value but I don’t know how to set it onto code.

  7. Is there a way that this script could be modified to change the primary user to the last logged on user? I feel like the ingredients to do so are here but I am not capable of doing so, LOL!

    1. Hmmm… The ingredients to determine the last user who logged on are there but.. If that’s an admin… Then that would become the primary user… Not sure if that would be a good idea. But if you want, I could create something 😅

      1. Wow thank you for the quick reply 🙂
        the situation is that devices were deployed and the primary user is the admin account. So we would scope the script to only run on those devices. I am sure you have seen situations like this before, LOL!

  8. hi
    now it get this error
    Get-MgUser : One or more errors occurred.
    At line:32 char:9
    + [PSCustomObject]@{
    + ~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-MgUser_Get], AggregateException
    + FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.PowerShell.Cmdlets.GetMgUser_Get

      1. Ah, nice! And from Active Directory? (Not Entra ID) For Entra ID it’s in the Get-MgBetaDeviceManagementManagedDevice details, for Active Directory you can retrieve the Last Login date for the Computer Account, but not which user logged into that. You can retrieve those events from the Security Logs of the Domain Controllers if you audit logon/logoff events and store that somewhere before the security eventlog rotates after growing to a certain size.

  9. Great script however i get the error:

    Get-MgBetaDeviceManagementManagedDevice : One or more errors occurred.
    At line:25 char:114
    + … agedDevice -All:$true -Filter “contains(DeviceName,’$($filter)’)” | W …
    + ~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-MgBetaDevic…agedDevice_List], AggregateException
    + FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.Beta.PowerShell.Cmdlets.GetMgBetaDeviceManagementManagedDevice_List

    1. What does running “Connect-MgGraph -Scopes ‘DeviceManagementManagedDevices.Read.All, User.Read.All'” give you? And “Get-MgBetaDeviceManagementManagedDevice -All:$true” afterwards?

  10. Very nice script. Do you have any experience with iPads configured in shared mode? I manage these for a school division, and users log on to the iPad with AAD accounts (federated apple ids). The user account and profiles get cached, and show up on the devices’ Users tab. MG does have a method to remove the user (Remove-MgDeviceManagementManagedDeviceUserFromSharedAppleDevice), but it requires that you specify the UPN. For bulk actions, I’m trying to find a way to get all the cached users and remove them. Your script will occasionally show a couple of these cached users, but usually not (presumably because the users aren’t actively logged in). Any thoughts?

    1. No experience in that scenario (yet), but if it shows it in the output of the script… Then you could write some logic around it if there’s more than one user present… Keep the newest and remove the older ones.. Interesting situation, but I don’t have an environment with this to test it on

  11. Hello
    When i run this script i am not able to get any out put in excel – its just blank
    how can i resolve it

    1. I just checked again, still works (Did update the script for correctly counting required modules in case of both having them on PowerShell v5 and v7). Any error messages? You did specify the outputfile like c:\temp\report.csv ?

  12. Hello I get the following error:

    Get-MgBetaDeviceManagementManagedDevice : One or more errors occurred.
    At line:29 char:114
    + … agedDevice -All:$true -Filter “contains(DeviceName,’$($Filter)’)” | W …
    + ~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-MgBetaDevic…agedDevice_List], AggregateException
    + FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.Beta.PowerShell.Cmdlets.GetMgBetaDeviceManagementManagedDevice_List

  13. Hi harm,

    I’ve been pulling my hair out with gathering information about primary users and last sign ins for ages and this script does everything I need so thank you so much for that.

    I am facing a problem though and I’m not sure how to resolve it. The script appears to work great, it processes through the 120ish windows devices and creates the CSV. The issue is that only 42 of those 120ish devices show up in the CSV file. The script is definitely attempting to process all the devices (they show up in green when the script is running) but for whatever reason they’re not making it to the CSV.

    There is a correlation which may point to my tenant being the issue though. The reason I needed a script like this so badly was because those same devices which didn’t show up in the CSV also don’t show recent check-ins on the intune education portal. You may be aware of this already, but in Intune education you can see the most recent user to check-in. For whatever reason this isn’t an option on the main intune portal. The first thought would be that there aren’t any recent check ins at all, but if I go to the main intune portal, I can see the “last check-in time” and it’s constantly updating whenever the mystery user logs in!

    I’ve just created a ticket with Microsoft for this, and maybe this comment will point others in the right direction if they come accross the issue, but I have one question.. Am I correct to assume that the script is failing to show all the devices just because it can’t see the last checked in user? Could it just be that it doesn’t expect to not be able to fill in that column for those devices and just decides not to output anything at all? I’m still a novice with scripts so I could be off, but that seems like the only reasonable explanation. which would point towards this being an issue on the tenant.

    1. Thank you 🙂 And could you, for one of the missing devices in the report, run (Get-MgBetaDeviceManagementManagedDevice -Filter “contains(DeviceName,’devicenamehere’)”).UsersLoggedOn

      If the last command doesn’t return anything, it will not be added to the report (It loops though the logged on users on that device using “foreach ($user in $device.UsersLoggedOn.UserId | Select-Object -Unique “)

  14. Thank you for this script. I’ve run into an issue where the export csv is adding three headers into one.

    1. I use “Export-Csv -Path $OutputFileName -NoTypeInformation -Encoding UTF8 -Delimiter ‘;’ “, is there some special character perhaps or does changing the delimeter to ‘,’ help? (Depends on your language settings in Excel). Could you share the first line of the CSV? (Edit it in a notepad or similar)

  15. Didn’t work for me. It exported the csv but only one cell with a few random characters as described above in the comments.

    1. With OU you mean Organizational Unit like in Active Directory? This script queries Entra ID devices that Intune manages. Or do you mean Administrative Units perhaps in Entra ID?

  16. Hello
    I get the following error:

    Get-MgBetaDeviceManagementManagedDevice : One or more errors occurred.
    At line:29 char:114
    + … agedDevice -All:$true -Filter “contains(DeviceName,’$($Filter)’)” | W …
    + ~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-MgBetaDevic…agedDevice_List], AggregateException
    + FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.Beta.PowerShell.Cmdlets.GetMgBetaDeviceManagementManagedDevice_List

    1. There were others reporting the same issue, but they didn’t respond to the questions I asked them (It still works fine here, just test it)

      Could you try:
      Connect-MgGraph -Scopes ‘DeviceManagementManagedDevices.Read.All, User.Read.All’
      Get-MgDeviceManagementManagedDevice -All:$true -Filter “contains(DeviceName,”)” | Where-Object OperatingSystem -eq Windows

      And see what that gives you?

      1. I’ve got this:

        Connect-MgGraph -Scopes DeviceManagementManagedDevices.Read.All, User.Read.All
        Welcome to Microsoft Graph!
        ……

        Get-MgBetaDeviceManagementManagedDevice -All:$true -Filter “contains(DeviceName,’$($filter)’)” | Where-Object OperatingSystem -eq Windows

        Id AadRegistered ActivationLockBypassCode AndroidSecurityPatchLevel AutopilotEnrolled
        — ————- ———————— ————————- —————–
        7447a9de-0cc3-4c53-96ff-3c5d338ede35 True True
        8c34a075-201e-4688-aab2-3bb0c48c051e True True
        241fe8df-063b-4b85-9632-3b4afaa8e266 True True
        ……..

  17. hmm i get:

    Processing 634BYW3…
    Get-MgUser : One or more errors occurred.
    At C:\Users\me\desktop\deviceusers.ps1:32 char:9
    + [PSCustomObject]@{
    + ~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-MgUser_Get], AggregateException
    + FullyQualifiedErrorId : System.AggregateException,Microsoft.Graph.PowerShell.Cmdlets.GetMgUser_Get

      1. thanks! for some reason the logic to install missing cmdlets doesn’t work. I manually ran that part and was able to replace an older 2.5 ver of graph.users and all good now – thank you!

  18. if (-not ((Get-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -ListAvailable).count -ge 3 | Select-Object Name -Unique)) {
    Write-Warning (“One or more required modules were not found, installing now…”)
    try {
    Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -Confirm:$false -SkipPublisherCheck -Scope CurrentUser -ErrorAction Stop
    }
    catch {
    Write-Warning (“Error installing required modules, exiting…”)
    return
    }
    }

    Yields nothing from powershell.

    Still getting this error:

    Get-MgBetaDeviceManagementManagedDevice : The term ‘Get-MgBetaDeviceManagementManagedDevice’ is not
    recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the
    name, or if a path was included, verify that the path is correct and try again.
    At C:\scripts\Get-IntunePrimaryUser.ps1:29 char:31
    + … Foreach ($device in (Get-MgBetaDeviceManagementManagedDevice -All:$t …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (Get-MgBetaDeviceManagementManagedDevice:String) [], CommandNo
    tFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    1. After running the script, what does get-module return? Do you actually see the Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users in the list? I tested it again on my system, and on a Windows Sandbox with nothing else installed but PowerShell v5. If you run Get-Executionpolicy in the Shell you have open, what does it that give you?

      1. Hey, I’m very new to powershell so not sure what you mean in your first paragraph.

        After running Get-Executionpolicy it yields this text: RemoteSigned

        Thanks for the fast reply

      2. Could you try running set-executionpolicy bypass -scope currentuser ? Afterwards, run Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Beta.DeviceManagement, Microsoft.Graph.Users -Confirm:$false -SkipPublisherCheck -Scope CurrentUser . (Answer prompts for installing packagemanager with Yes) and then run the script again

      3. You’re a wizard mate… works now.

        Thanks so much for the insanely fast replies, I owe you one.

Leave a Reply to CraigCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.