Every Intune admin has been in this situation. You export a list of devices — maybe from an Entra ID group, maybe a compliance report — and all you get is a column of hostnames. No idea who is using them, when they last checked in, or whether they are even still active. Manually looking up each one in the Intune admin centre works for five devices. It does not work for five hundred.
This script solves that in one run. Feed it a CSV of hostnames, and it gives you back a clean report: who the primary user is, the last time each device synced with Intune, the OS, and whether the device was found at all. Everything you need to turn a raw device list into something you can actually act on.
| Hostname | PrimaryUser | UserPrincipalName | LastSyncTime | OS | Status |
|---|---|---|---|---|---|
| DESKTOP-A1B2C3 | Sarah Johnson | sarah.johnson@contoso.com | 2026-06-25 08:14 | Windows | Found |
| LAPTOP-X7Y8Z9 | Mark Davies | mark.davies@contoso.com | 2026-05-03 14:52 | Windows | Found |
| WS-FINANCE-04 | — | — | — | — | NotFoundInIntune |
Before you run it — what you need
The script connects to Microsoft Graph using a registered app and a certificate. This sounds more complicated than it is. Here is exactly what to set up, in order.
The script
Update the three config values at the top — everything else runs automatically. The script installs any missing Graph modules, connects silently, queries each device, and saves the output CSV to your Downloads folder.
#Requires -Version 5.1
<#
.SYNOPSIS
Gets the primary user and last sync time for Intune-managed devices from a CSV of hostnames.
.NOTES
Required permission: DeviceManagementManagedDevices.Read.All (Application, read-only)
#>
# ── Config ────────────────────────────────────────────────────
$tenantId = 'YOUR-TENANT-ID'
$clientId = 'YOUR-CLIENT-ID'
$thumbprint = 'YOUR-CERTIFICATE-THUMBPRINT'
$csvInput = "$env:USERPROFILE\Downloads\devices.csv"
$csvOutput = "$env:USERPROFILE\Downloads\DevicePrimaryUser_$(Get-Date -Format 'yyyy-MM-dd').csv"
# ──────────────────────────────────────────────────────────────
function Ensure-Module {
param([string]$Name)
if (-not (Get-Module -ListAvailable -Name $Name)) {
Write-Host "Installing $Name ..." -ForegroundColor Yellow
Install-Module -Name $Name -Scope CurrentUser -Repository PSGallery -Force -AllowClobber
}
Import-Module $Name -ErrorAction Stop
}
Ensure-Module 'Microsoft.Graph.Authentication'
Ensure-Module 'Microsoft.Graph.DeviceManagement'
# Connect using certificate — no interactive prompt
$cert = Get-Item "Cert:\CurrentUser\My\$thumbprint" -ErrorAction Stop
Connect-MgGraph -TenantId $tenantId -ClientId $clientId -Certificate $cert -NoWelcome
# Load hostnames from CSV
$devices = Import-Csv -Path $csvInput | Select-Object -ExpandProperty displayName | Where-Object { $_ }
Write-Host "Loaded $($devices.Count) hostnames" -ForegroundColor Cyan
# Query each device
$results = foreach ($hostname in $devices) {
Write-Host " Querying: $hostname" -ForegroundColor Cyan
$filter = "deviceName eq '$hostname'"
$select = 'deviceName,userDisplayName,userPrincipalName,lastSyncDateTime,operatingSystem,managedDeviceOwnerType'
try {
$found = Get-MgDeviceManagementManagedDevice -Filter $filter -Select $select -ErrorAction Stop
if ($found) {
foreach ($d in $found) {
[PSCustomObject]@{
Hostname = $d.DeviceName
PrimaryUser = $d.UserDisplayName
UserPrincipalName = $d.UserPrincipalName
LastSyncTime = $d.LastSyncDateTime
OS = $d.OperatingSystem
OwnerType = $d.ManagedDeviceOwnerType
Status = 'Found'
}
}
} else {
[PSCustomObject]@{
Hostname = $hostname
PrimaryUser = ''
UserPrincipalName = ''
LastSyncTime = ''
OS = ''
OwnerType = ''
Status = 'NotFoundInIntune'
}
}
}
catch {
[PSCustomObject]@{
Hostname = $hostname
PrimaryUser = ''
UserPrincipalName = ''
LastSyncTime = ''
OS = ''
OwnerType = ''
Status = "Error: $($_.Exception.Message)"
}
}
}
# Export and disconnect
$results | Export-Csv -Path $csvOutput -NoTypeInformation -Encoding UTF8
Write-Host "`nDone. Saved to: $csvOutput" -ForegroundColor Green
Disconnect-MgGraph | Out-Null
Reading the output — what LastSyncTime actually tells you
The LastSyncTime column is the most useful field in the report. It shows the last time the device checked in with Intune to receive policy, pull app updates, and confirm compliance. Use it as the primary signal for device health:
| Last sync | What it means | Action |
|---|---|---|
| Within 7 days | Active and healthy | No action needed |
| 8 – 30 days | Device may be offline or user on leave | Monitor — check again next cycle |
| 30 – 60 days | Likely stale — policies may be out of date | Contact primary user to confirm status |
| 60 – 90 days | Stale — device not receiving Intune policy | Investigate — escalate to service desk |
| Over 90 days or blank | Device likely decommissioned or unenrolled | Consider retiring from Intune and Entra |
What NotFoundInIntune means
A device that comes back as NotFoundInIntune is worth investigating before you assume the device is gone. The most common causes:
- The hostname changed — device was renamed after the CSV was exported
- Device was unenrolled — manually removed from Intune or wiped
- The CSV has a typo — check for trailing spaces, wrong casing, or truncated names
- Device was re-imaged — old record deleted from Intune, new record has a different name
- Stale group membership — the Entra group still has the device, but Intune has already removed it
What you can do with the report
Get the script
The script is available on GitHub. Download it, add your tenant ID, client ID, and certificate thumbprint at the top, and it is ready to run.
🔗 View on GitHub — Imran76Awan/Daily-Tasks
Before sharing your copy: Remove your tenant ID, client ID, and thumbprint and replace them with placeholders. Never commit real credentials or certificate private keys (.pfx files) to a public repository.