Public/Get-MsecIntuneDevice.ps1
|
function Get-MsecIntuneDevice { <# .SYNOPSIS Lists every managed device known to Intune, projected to a flat PowerShell shape suitable for filtering / grouping / exporting. .DESCRIPTION Calls Microsoft Graph /v1.0/deviceManagement/managedDevices with a $select for the audit-relevant columns, paginates through @odata.nextLink, and emits one PSCustomObject per device. Aggregate stats are produced in PowerShell on the consumer side - msec only returns the raw device list. This matches Search-MsecAzureResourceGraph / Get-MsecIntuneCompliancePolicy: the module returns rows, the consumer decides what to do with them. See the examples. Requires the 'DeviceManagementManagedDevices.Read.All' application permission. Different from DeviceManagementConfiguration.Read.All (which msec also has) - configuration is about POLICIES, this is about DEVICES. A clearer error is raised on the typical 403. .EXAMPLE # Compliance counts. Get-MsecIntuneDevice | Group-Object ComplianceState | Sort-Object Count -Descending .EXAMPLE # Devices not seen in 30 days - stale management. A device that stopped checking # in keeps its last compliance verdict, so these read as compliant while being # entirely unverified. Get-MsecIntuneDevice | Where-Object { $_.LastSyncDateTime -lt (Get-Date).AddDays(-30) } .EXAMPLE # OS family breakdown. Get-MsecIntuneDevice | Group-Object Os | Select-Object Name, Count .EXAMPLE # Snapshot-style headline percentages for an archive or a posture report. $d = Get-MsecIntuneDevice [pscustomobject]@{ Total = $d.Count Compliant = ($d | Where-Object ComplianceState -eq 'compliant').Count Noncompliant = ($d | Where-Object ComplianceState -eq 'noncompliant').Count InGracePeriod = ($d | Where-Object ComplianceState -eq 'inGracePeriod').Count CompliantPercent = if ($d.Count) { [math]::Round(($d | Where-Object ComplianceState -eq 'compliant').Count / $d.Count * 100, 2) } else { 0 } } .OUTPUTS PSCustomObject per device, with the columns documented in the .NOTES. .NOTES Projected columns (Graph field -> output property): id -> Id deviceName -> DeviceName userPrincipalName -> UserPrincipalName userDisplayName -> UserDisplayName operatingSystem -> Os osVersion -> OsVersion model -> Model manufacturer -> Manufacturer complianceState -> ComplianceState complianceGracePeriodExpirationDateTime -> ComplianceGraceUntil (null when no grace) managementState -> ManagementState managementAgent -> ManagementAgent managedDeviceOwnerType -> Ownership ('company' / 'personal' / 'unknown') isEncrypted -> IsEncrypted jailBroken -> Jailbroken azureADRegistered -> EntraRegistered enrolledDateTime -> EnrolledDateTime lastSyncDateTime -> LastSyncDateTime serialNumber -> SerialNumber #> [CmdletBinding()] param() Assert-MsecSession # $select trims the response to ~20% of the full managedDevice shape. Stops # us paging through 80+ noisy columns we don't need. Comma-joined so we don't # have to URL-encode anything ourselves; Graph accepts the bare list. $select = @( 'id' 'deviceName' 'userPrincipalName' 'userDisplayName' 'operatingSystem' 'osVersion' 'model' 'manufacturer' 'complianceState' 'complianceGracePeriodExpirationDateTime' 'managementState' 'managementAgent' 'managedDeviceOwnerType' 'isEncrypted' 'jailBroken' 'azureADRegistered' 'enrolledDateTime' 'lastSyncDateTime' 'serialNumber' ) -join ',' $path = "/v1.0/deviceManagement/managedDevices?`$select=$select" try { $devices = @(Invoke-MsecGraphRequest -Path $path -All) } catch { if ($_.Exception.Message -match '403|Forbidden') { throw "Forbidden when calling /deviceManagement/managedDevices. The msec app needs the 'DeviceManagementManagedDevices.Read.All' application permission (admin consent required). Re-run New-MsecApp to add and consent it. Original error: $($_.Exception.Message)" } throw } foreach ($d in $devices) { # Graph returns a sentinel '9999-12-31T...' when no grace period is set; # surface that as $null so the caller can filter / compare cleanly. $grace = $null if ($d.complianceGracePeriodExpirationDateTime -and $d.complianceGracePeriodExpirationDateTime -notmatch '^9999-') { $grace = [datetime]$d.complianceGracePeriodExpirationDateTime } [PSCustomObject]@{ Id = $d.id DeviceName = $d.deviceName UserPrincipalName = $d.userPrincipalName UserDisplayName = $d.userDisplayName Os = $d.operatingSystem OsVersion = $d.osVersion Model = $d.model Manufacturer = $d.manufacturer ComplianceState = $d.complianceState ManagementState = $d.managementState ManagementAgent = $d.managementAgent Ownership = $d.managedDeviceOwnerType IsEncrypted = $d.isEncrypted Jailbroken = $d.jailBroken EntraRegistered = $d.azureADRegistered EnrolledDateTime = if ($d.enrolledDateTime) { [datetime]$d.enrolledDateTime } else { $null } LastSyncDateTime = if ($d.lastSyncDateTime) { [datetime]$d.lastSyncDateTime } else { $null } ComplianceGraceUntil = $grace SerialNumber = $d.serialNumber } } } |