Reporting Operating System Versions for Azure AD Registered Devices

Know What Operating System Used by Azure AD Registered Devices

After reading an article about populating extension attributes for Azure AD registered devices, a reader asked me how easy it would be to create a report about the operating systems used for registered devices. Microsoft puts a lot of effort into encouraging customers to upgrade to Windows 11 and it’s a good idea to know what’s the device inventory. Of course, products like Intune have the ability to report this kind of information, but it’s more fun (and often more flexible) when you can extract the information yourself.

As it turns out, reporting the operating systems used by registered devices is very easy because the Microsoft Graph reports this information in the set of properties retrieved by the Get-MgDevice cmdlet from the Microsoft Graph PowerShell SDK.

PowerShell Script to Report Azure AD Registered Devices

The script described below creates a report of all registered devices and sorts the output by the last sign in date. Microsoft calls this property ApproximateLastSignInDateTime. As the name indicates, the property stores the approximate date for the last sign in. Azure AD doesn’t update the property every time someone uses the device to connect. I don’t have a good rule for when property updates occur. It’s enough (and approximate) that the date is somewhat accurate for the purpose of identifying if a device is in use, which is why the script sorts devices by that date.

Any Windows device that hasn’t been used to sign into Azure AD in the last six months is likely not active. This isn’t true for mobile phones because they seem to sign in once and never appear again. The report generated for my tenant still has a record for a Windows Phone which last signed in on 2 December 2015. I think I can conclude that it’s safe to remove this device from my inventory.

Figuring Out Device Owners

In the last script I wrote using the Get-MgDevice cmdlet, I figured out the owner of the device by extracting the user identifier from the PhysicalIds property. While this approach works, it’s complicated. A much better approach is to use the Get-MgDeviceRegisteredOwner cmdlet which returns the user identifier for the Azure AD account of the registered owner. With this identifier, we can retrieve any account property that makes sense, such as the display name, user principal name, department, city, and country. You could easily add other properties that make sense to your organization. See this article for more information about using the Get-MgUser cmdlet to interact with Azure AD user accounts.

The Big Caveat About Operating System Information

The problem that exists in using registered devices to report operating system information is that it’s not accurate. The operating system details noted for a device are accurate at the point of registration but degrade over time. If you want to generate accurate reports, you need to use the Microsoft Graph API for Intune.

With that caveat in mind, here’s the code to report the operating system information that Azure AD stores for registered devices:

Connect-MgGraph -Scope User.Read.All, Directory.Read.All
Select-MgProfile Beta

Write-Host "Finding registered devices"
[array]$Devices = Get-MgDevice -All
If (!($Devices)) { Write-Host "No registered devices found - exiting" ; break }
Write-Host ("Processing details for {0} devices" -f $Devices.count)
$Report = [System.Collections.Generic.List[Object]]::new() 
$i = 0
ForEach ($Device in $Devices) {
  Write-Host ("Reporting device {0} ({1}/{2}" -f $Device.DisplayName, $i, $Devices.count)
  $DeviceOwner = $Null
  Try {
    [array]$OwnerIds = Get-MgDeviceRegisteredOwner -DeviceId $Device.Id
    $DeviceOwner = Get-MgUser -UserId $OwnerIds[0].Id }
  Catch {}

  $ReportLine = [PSCustomObject][Ordered]@{
   Device             = $Device.DisplayName
   Id                 = $Device.Id
   LastSignIn         = $Device.ApproximateLastSignInDateTime
   Owner              = $DeviceOwner.DisplayName
   OwnerUPN           = $DeviceOwner.UserPrincipalName
   Department         = $DeviceOwner.Department
   Office             = $DeviceOwner.OfficeLocation
   City               = $DeviceOwner.City
   Country            = $DeviceOwner.Country
   "Operating System" = $Device.OperatingSystem
   "O/S Version"      = $Device.OperatingSystemVersion
   Registered         = $Device.RegistrationDateTime
   "Account Enabled"  = $Device.AccountEnabled
   DeviceId           = $Device.DeviceId
   TrustType          = $Device.TrustType }

} #End Foreach Device

# Sort in order of last signed in date
$Report = $Report | Sort-Object {$_.LastSignIn -as [datetime]} -Descending

$Report | Out-GridView

Figure 1 is an example of the report as viewed through the Out-GridView cmdlet.

Reporting operating system information for Azure AD registered devices
Figure 1: Reporting operating system information for Azure AD registered devices

An Incomplete Help

I’ve no idea whether this script will help anyone. It’s an incomplete answer to a question. However, even an incomplete answer can be useful in the right circumstances. After all, it’s just PowerShell, so use the code as you like.

Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

One Reply to “Reporting Operating System Versions for Azure AD Registered Devices”

  1. Your script was very much of help, thank you Tony.
    For my use case, I needed to filter out certain security groups in order to get more specific results. Thought I’d share in case someone else needs has a similar need:

    # Define the Object IDs of the security groups you want to filter for
    $SecurityGroupIds = @(
    “Group_objectID_here”, #Groupname 1
    “Group_objectID_here”, #Groupname 2

    # Loop through each device in the array
    ForEach ($Device in $Devices) {
    # Display a message indicating the current device being processed
    Write-Host (“Reporting device {0} ({1}/{2}” -f $Device.DisplayName, $i, $Devices.count)

    # Initialize a variable to store the device owner’s details
    $DeviceOwner = $Null

    Try {
    # Get an array of registered owner IDs for the current device
    [array]$OwnerIds = Get-MgDeviceRegisteredOwner -DeviceId $Device.Id
    # Get the user details for the first registered owner
    $DeviceOwner = Get-MgUser -UserId $OwnerIds[0].Id

    # Check if the device owner is a member of any of the specified security groups
    $IsMember = $false
    $UserGroups = Get-MgUserMemberOf -UserId $DeviceOwner.Id
    foreach ($group in $UserGroups) {
    if ($SecurityGroupIds -contains $group.Id) {
    $IsMember = $true

    # If the owner is not a member of any of the security groups, skip processing this device
    if (!$IsMember) {
    Write-Host (“Device owner is not a member of any of the specified security groups – skipping”)
    Catch {}

Leave a Reply

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