IntuneHow-to

Automating Intune device reports with Microsoft Graph PowerShell

Export your Intune device inventory to CSV with the Microsoft Graph PowerShell SDK, spot stale and noncompliant devices, and run it unattended with certificate-based app-only sign-in.

The device list in the Intune admin center is fine for a quick look, but a weekly inventory, a stale-device list or a noncompliance report is easier to produce from a script. In this post I'll build that script step by step with the Microsoft Graph PowerShell SDK, then make it run unattended with a certificate.

How this guide is organised: Prerequisites → Step-by-step → Verify → Tips & gotchasFlow diagram of the article's sections in reading order: 1. Prerequisites. 2. Step-by-step (6 steps: Install the module and connect; Export the inventory; Find stale devices; List noncompliant devices; Run it unattended with a certificate; Schedule it and keep a history). 3. Verify. 4. Tips & gotchas. Toolbox: Disconnect-MgGraph, Get-MgContext, Retry-After, Devices › All devices, lastSyncDateTime.1Prerequisites2Step-by-step3Verify4Tips & gotchas1Install themodule and c…2Export theinventory3Find staledevices4Listnoncompliant…5Run itunattended w…6Schedule itand keep a hi…TOOLBOXDisconnect-MgGraphGet-MgContextRetry-AfterDevices › All deviceslastSyncDateTimeHow this guide is organised: Prerequisites → Step-by-step → Verify → Tips & gotchasFlow diagram of the article's sections in reading order: 1. Prerequisites. 2. Step-by-step (6 steps: Install the module and connect; Export the inventory; Find stale devices; List noncompliant devices; Run it unattended with a certificate; Schedule it and keep a history). 3. Verify. 4. Tips & gotchas. Toolbox: Disconnect-MgGraph, Get-MgContext, Retry-After, Devices › All devices, lastSyncDateTime.1Prerequisites2Step-by-step1Install the module and connect2Export the inventory3Find stale devices4List noncompliant devices5Run it unattended with a certificate6Schedule it and keep a history3Verify4Tips & gotchasTOOLBOXDisconnect-MgGraphGet-MgContextRetry-AfterDevices › All deviceslastSyncDateTime
At a glance: how this guide is organised · 6 steps · 5 key settings and tools

Prerequisites#

  • PowerShell 7 (recommended) or Windows PowerShell 5.1.
  • The Microsoft.Graph.DeviceManagement module, or the full Microsoft.Graph SDK.
  • The Graph permission DeviceManagementManagedDevices.Read.All. It's the least-privileged permission for listing managed devices and exists as both a delegated and an application permission.
  • For interactive runs, an account with an Intune role that can read devices, such as Read Only Operator.
  • An active Intune licence in the tenant, which the Intune Graph API requires.

Step-by-step#

1. Install the module and connect#

PowerShell
Install-Module Microsoft.Graph.DeviceManagement -Scope CurrentUser
Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All" -NoWelcome
Get-MgContext | Select-Object Account, Scopes

2. Export the inventory#

Ask only for the properties you need, and always add -All; without it you only get the first page of results.

PowerShell
$props = 'id','deviceName','userPrincipalName','operatingSystem','osVersion',
         'complianceState','lastSyncDateTime','enrolledDateTime',
         'managedDeviceOwnerType','serialNumber','model','manufacturer'

$devices = Get-MgDeviceManagementManagedDevice -All -Property $props

$devices |
    Select-Object DeviceName, UserPrincipalName, OperatingSystem, OsVersion,
        ComplianceState, LastSyncDateTime, EnrolledDateTime,
        ManagedDeviceOwnerType, SerialNumber, Model, Manufacturer |
    Export-Csv -Path .\IntuneDevices.csv -NoTypeInformation -Encoding UTF8

Setting the encoding explicitly keeps non-Latin characters, such as Arabic user names, intact in the file. managedDeviceOwnerType tells you whether a device is company or personal, which is usually the first column managers ask about.

For a quick summary in the console before you open the CSV:

PowerShell
$devices | Group-Object OperatingSystem, ComplianceState -NoElement |
    Sort-Object Count -Descending

3. Find stale devices#

The inventory is already in memory, so filter it locally instead of calling Graph again:

PowerShell
$cutoff = (Get-Date).AddDays(-30)

$devices |
    Where-Object { $_.LastSyncDateTime -lt $cutoff } |
    Sort-Object LastSyncDateTime |
    Select-Object DeviceName, UserPrincipalName, OperatingSystem, LastSyncDateTime |
    Export-Csv -Path .\StaleDevices.csv -NoTypeInformation -Encoding UTF8

If stale devices are all you need, Graph can filter on the server as well: lastSyncDateTime supports the lt and gt operators in -Filter.

4. List noncompliant devices#

complianceState supports eq and or in a server-side filter. Its values include compliant, noncompliant, conflict, error, inGracePeriod and configManager.

PowerShell
$nonCompliant = Get-MgDeviceManagementManagedDevice -All -Property $props `
    -Filter "complianceState eq 'noncompliant'"

$nonCompliant | Group-Object OperatingSystem | Select-Object Name, Count

5. Run it unattended with a certificate#

Scheduled runs shouldn't depend on someone signing in. Use app-only authentication:

  1. Register an app in Microsoft Entra ID and upload the public key of a certificate (.cer, .pem or .crt).
  2. Add the application permission DeviceManagementManagedDevices.Read.All and grant admin consent.
  3. Install the certificate, with its private key, in the certificate store of the account that runs the scheduled task.
  4. Connect with the app's client ID, your tenant ID and the certificate thumbprint:
PowerShell
$connect = @{
    ClientId              = '00000000-0000-0000-0000-000000000000'  # application (client) ID
    TenantId              = '11111111-1111-1111-1111-111111111111'  # directory (tenant) ID
    CertificateThumbprint = 'YOUR-CERTIFICATE-THUMBPRINT'
}
Connect-MgGraph @connect -NoWelcome

(Get-MgContext).AuthType   # returns AppOnly

The rest of the script runs unchanged; finish with Disconnect-MgGraph. A certificate is far safer than a client secret sitting in a script file.

6. Schedule it and keep a history#

Save the script, stamp each export with the date so you can compare weeks, and run it from Task Scheduler under the account that holds the certificate. Use full paths, because a scheduled task doesn't start in your script folder:

PowerShell
$stamp = Get-Date -Format 'yyyy-MM-dd'
$path  = Join-Path $PSScriptRoot "IntuneDevices-$stamp.csv"   # use as -Path in step 2

# Task Scheduler action
# Program:   pwsh.exe
# Arguments: -NoProfile -NonInteractive -File C:\Scripts\Get-IntuneDeviceReport.ps1

The module must be available to that account too: install it while signed in as that account, or use -Scope AllUsers from an elevated session. Keep the output folder access-controlled. The CSV holds user names and serial numbers, so treat it like any other personal data.

Verify#

  • Get-MgContext shows the scope you asked for, or AppOnly for the scheduled run.
  • $devices.Count is in line with the total under Devices › All devices in the Intune admin center.
  • The CSV opens with one row per device, populated columns and sensible LastSyncDateTime values.

Tips & gotchas#

  • Paging: -All follows every page for you. -PageSize only changes how many records each request returns.
  • Throttling: Graph answers bursts with HTTP 429 and a Retry-After header, and Microsoft's guidance notes the Graph SDKs already include retry handlers that honour it. The best protection is fewer calls: pull the list once and filter locally rather than querying device by device.
  • Unselected means empty: with -Property, anything you didn't request comes back blank. A few properties are only populated when you get a single device rather than the list.
  • Time zones: Graph timestamps are in UTC, so allow for that when you choose cut-offs or share reports with colleagues elsewhere.
  • SDK v2: Select-MgProfile no longer exists. Beta cmdlets live in the Microsoft.Graph.Beta module, for example Get-MgBetaDeviceManagementManagedDevice.

References#

Written and checked against current Microsoft Learn documentation. Test changes with a pilot group before rolling them out to everyone, and if an admin center path has moved since, search for the setting name instead.

Spotted a mistake, or did this fix work differently for you? Email me or message me on LinkedIn — corrections are credited in the article.

OE
Written by

Omer Eltayeb

Independent Microsoft Intune consultant in Cairo, Egypt, former Microsoft Cloud Solutions Architect, Microsoft Certified Trainer and Microsoft Innovative Educator Expert (2024–26) and Microsoft Elevate Educator Expert (2026–27). I share practical, step-by-step guides, study plans, scripts and toolkits for Microsoft Intune, Microsoft Entra ID, Microsoft Defender and Exchange Online with the community.

Microsoft Certified Trainer (MCT) 2026Microsoft Innovative Educator Expert 2025–2026Microsoft Elevate Educator Expert 2026–2027ISC2 Certified Information Systems Security Professional (CISSP)Microsoft 365 Certified: Enterprise Administrator Expert (MS-102)