Src/Private/Get-AbrIntuneDevices.ps1
|
function Get-AbrIntuneDevices { <# .SYNOPSIS Documents managed devices enrolled in Microsoft Intune. .DESCRIPTION Collects and reports on: - Device inventory summary (platform breakdown, ownership, compliance state) - Stale device identification (>90 days no check-in) - Non-compliant devices - Per-device detail table (InfoLevel 2) .NOTES Version: 0.1.0 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Intune Managed Devices for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'Devices' } process { Section -Style Heading2 'Managed Devices' { Paragraph "The following section documents devices managed by Microsoft Intune in tenant $TenantId." BlankLine try { Write-Host " - Retrieving managed devices..." # /beta required -- v1.0 managedDevices returns BadRequest with some $select combinations # joinType is a beta-only property; use /beta to ensure all fields are available $DevicesResp = Invoke-MgGraphRequest -Method GET ` -Uri "$($script:GraphEndpoint)/beta/deviceManagement/managedDevices?`$select=id,deviceName,operatingSystem,osVersion,complianceState,managedDeviceOwnerType,enrolledDateTime,lastSyncDateTime,userPrincipalName,model,manufacturer,serialNumber,joinType,managementAgent" ` -ErrorAction Stop $Devices = $DevicesResp.value # Handle paging for large tenants while ($DevicesResp.'@odata.nextLink') { $DevicesResp = Invoke-MgGraphRequest -Method GET -Uri $DevicesResp.'@odata.nextLink' -ErrorAction SilentlyContinue if ($DevicesResp.value) { $Devices += $DevicesResp.value } } if ($Devices -and @($Devices).Count -gt 0) { #region Device Summary $TotalDevices = @($Devices).Count $Windows = @($Devices | Where-Object { $_.operatingSystem -like 'Windows*' }).Count $iOS = @($Devices | Where-Object { $_.operatingSystem -like 'iOS*' }).Count $Android = @($Devices | Where-Object { $_.operatingSystem -like 'Android*' }).Count $macOS = @($Devices | Where-Object { $_.operatingSystem -like 'macOS*' }).Count $Other = $TotalDevices - $Windows - $iOS - $Android - $macOS $Compliant = @($Devices | Where-Object { $_.complianceState -eq 'compliant' }).Count $NonCompliant = @($Devices | Where-Object { $_.complianceState -eq 'noncompliant' }).Count $InGracePeriod = @($Devices | Where-Object { $_.complianceState -eq 'inGracePeriod' }).Count $Unknown = @($Devices | Where-Object { $_.complianceState -eq 'unknown' -or $_.complianceState -eq 'configManager' }).Count $Corporate = @($Devices | Where-Object { $_.managedDeviceOwnerType -eq 'company' }).Count $Personal = @($Devices | Where-Object { $_.managedDeviceOwnerType -eq 'personal' }).Count $Stale90 = @($Devices | Where-Object { $_.lastSyncDateTime -and ((Get-Date) - [datetime]$_.lastSyncDateTime).Days -gt 90 }).Count $SumObj = [System.Collections.ArrayList]::new() $sumInObj = [ordered] @{ 'Total Managed Devices' = $TotalDevices 'Windows' = $Windows 'iOS / iPadOS' = $iOS 'Android' = $Android 'macOS' = $macOS 'Other Platforms' = $Other 'Compliant' = $Compliant 'Non-Compliant' = $NonCompliant 'In Grace Period' = $InGracePeriod 'Unknown / ConfigMgr Co-Managed' = $Unknown 'Corporate Owned' = $Corporate 'Personally Owned' = $Personal 'Stale (>90 days no check-in)' = $Stale90 } $SumObj.Add([pscustomobject]$sumInObj) | Out-Null # Store metrics for compliance checks in other sections (DeviceCompliance) $null = ($script:NonCompliantCount = $NonCompliant) $null = ($script:NonCompliantPct = if ($TotalDevices -gt 0) { [math]::Round(($NonCompliant / $TotalDevices) * 100, 0) } else { 0 }) $null = ($script:StaleDeviceCount = $Stale90) $null = ($script:StaleDevicePct = if ($TotalDevices -gt 0) { [math]::Round(($Stale90 / $TotalDevices) * 100, 0) } else { 0 }) $CompliantPct = if ($TotalDevices -gt 0) { [math]::Round(($Compliant / $TotalDevices) * 100, 0) } else { 0 } $PersonalDevicePct = if ($TotalDevices -gt 0) { [math]::Round(($Personal / $TotalDevices) * 100, 0) } else { 0 } $null = (& { if ($HealthCheck.Intune.Devices) { $null = ($SumObj | Where-Object { [int]$_.'Non-Compliant' -gt 0 } | Set-Style -Style Critical | Out-Null) $null = ($SumObj | Where-Object { [int]$_.'Stale (>90 days no check-in)' -gt 0 } | Set-Style -Style Warning | Out-Null) } }) $SumTableParams = @{ Name = "Device Summary - $TenantId"; List = $true; ColumnWidths = 55, 45 } if ($Report.ShowTableCaptions) { $SumTableParams['Caption'] = "- $($SumTableParams.Name)" } $SumObj | Table @SumTableParams #endregion #region ACSC E8 Assessment if ($script:IncludeACSCe8) { BlankLine Paragraph "ACSC Essential Eight Maturity Level Assessment -- Managed Devices:" BlankLine try { $_v = @{ TotalManagedDevices = $TotalDevices CompliantDevices = $Compliant CompliantPct = $CompliantPct StaleDevices = $Stale90 StaleDevicePct = $script:StaleDevicePct CorporateDevices = $Corporate PersonalDevices = $Personal PersonalDevicePct = $PersonalDevicePct NonCompliantDevices = $NonCompliant NonCompliantPct = $script:NonCompliantPct } $E8Checks = Build-AbrIntuneComplianceChecks -Definitions (Get-AbrIntuneE8Checks -Section 'Devices') -Framework E8 -CallerVariables $_v New-AbrIntuneE8AssessmentTable -Checks $E8Checks -Name 'Managed Devices' -TenantId $TenantId if ($E8Checks) { $null = $script:E8AllChecks.AddRange([object[]](@($E8Checks | Select-Object @{N='Section';E={'Devices'}}, ML, Control, Status, Detail))) } } catch { Write-AbrSectionError -Section 'E8 Devices Assessment' -Message "$($_.Exception.Message)" } } #endregion #region CIS Assessment if ($script:IncludeCISBaseline) { BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- Managed Devices:" BlankLine try { $_v = @{ TotalManagedDevices = $TotalDevices CompliantDevices = $Compliant CompliantPct = $CompliantPct StaleDevices = $Stale90 StaleDevicePct = $script:StaleDevicePct NonCompliantDevices = $NonCompliant NonCompliantPct = $script:NonCompliantPct } $CISChecks = Build-AbrIntuneComplianceChecks -Definitions (Get-AbrIntuneCISChecks -Section 'Devices') -Framework CIS -CallerVariables $_v New-AbrIntuneCISAssessmentTable -Checks $CISChecks -Name 'Managed Devices' -TenantId $TenantId if ($CISChecks) { $null = $script:CISAllChecks.AddRange([object[]](@($CISChecks | Select-Object @{N='Section';E={'Devices'}}, CISControl, Level, Status, Detail))) } } catch { Write-AbrSectionError -Section 'CIS Devices Assessment' -Message "$($_.Exception.Message)" } } #endregion #region Non-Compliant Devices (always shown if any exist) $NonCompliantDevices = $Devices | Where-Object { $_.complianceState -eq 'noncompliant' } if ($NonCompliantDevices -and @($NonCompliantDevices).Count -gt 0) { BlankLine Section -Style Heading3 'Non-Compliant Devices' { BlankLine $NCObj = [System.Collections.ArrayList]::new() foreach ($Dev in ($NonCompliantDevices | Sort-Object deviceName)) { $ncInObj = [ordered] @{ 'Device Name' = $Dev.deviceName 'OS' = "$($Dev.operatingSystem) $($Dev.osVersion)" 'Owner UPN' = if ($Dev.userPrincipalName) { $Dev.userPrincipalName } else { '--' } 'Ownership' = $Dev.managedDeviceOwnerType 'Last Check-In' = if ($Dev.lastSyncDateTime) { ([datetime]$Dev.lastSyncDateTime).ToString('yyyy-MM-dd') } else { '--' } } $NCObj.Add([pscustomobject]$ncInObj) | Out-Null } $null = (& { if ($HealthCheck.Intune.Devices) { $null = ($NCObj | Set-Style -Style Critical | Out-Null) } }) $NCTableParams = @{ Name = "Non-Compliant Devices - $TenantId"; ColumnWidths = 22, 22, 28, 14, 14 } if ($Report.ShowTableCaptions) { $NCTableParams['Caption'] = "- $($NCTableParams.Name)" } $NCObj | Table @NCTableParams if (Get-IntuneExcelSheetEnabled -SheetKey 'NonCompliantDevices') { $script:ExcelSheets['Non-Compliant Devices'] = $NCObj } } } #endregion #region Stale Devices $StaleDevices = $Devices | Where-Object { $_.lastSyncDateTime -and ((Get-Date) - [datetime]$_.lastSyncDateTime).Days -gt 90 } if ($StaleDevices -and @($StaleDevices).Count -gt 0) { BlankLine Section -Style Heading3 'Stale Devices (>90 Days No Check-In)' { BlankLine $StaleObj = [System.Collections.ArrayList]::new() foreach ($Dev in ($StaleDevices | Sort-Object lastSyncDateTime)) { $DaysSince = ((Get-Date) - [datetime]$Dev.lastSyncDateTime).Days $staleInObj = [ordered] @{ 'Device Name' = $Dev.deviceName 'OS' = "$($Dev.operatingSystem) $($Dev.osVersion)" 'Owner UPN' = if ($Dev.userPrincipalName) { $Dev.userPrincipalName } else { '--' } 'Last Check-In' = ([datetime]$Dev.lastSyncDateTime).ToString('yyyy-MM-dd') 'Days Since' = $DaysSince 'Compliance' = $Dev.complianceState } $StaleObj.Add([pscustomobject]$staleInObj) | Out-Null } $null = (& { if ($HealthCheck.Intune.Devices) { $null = ($StaleObj | Set-Style -Style Warning | Out-Null) } }) $StaleTableParams = @{ Name = "Stale Devices - $TenantId"; ColumnWidths = 20, 20, 28, 13, 9, 10 } if ($Report.ShowTableCaptions) { $StaleTableParams['Caption'] = "- $($StaleTableParams.Name)" } $StaleObj | Table @StaleTableParams if (Get-IntuneExcelSheetEnabled -SheetKey 'StaleDevices') { $script:ExcelSheets['Stale Devices'] = $StaleObj } } } #endregion #region Full Device Inventory (InfoLevel 2) if ($InfoLevel.Devices -ge 2) { BlankLine Section -Style Heading3 'Full Device Inventory' { BlankLine $DevObj = [System.Collections.ArrayList]::new() foreach ($Dev in ($Devices | Sort-Object deviceName)) { $devInObj = [ordered] @{ 'Device Name' = $Dev.deviceName 'OS' = $Dev.operatingSystem 'OS Version' = $Dev.osVersion 'Compliance' = $Dev.complianceState 'Ownership' = $Dev.managedDeviceOwnerType 'Owner UPN' = if ($Dev.userPrincipalName) { $Dev.userPrincipalName } else { '--' } 'Manufacturer' = if ($Dev.manufacturer) { $Dev.manufacturer } else { '--' } 'Model' = if ($Dev.model) { $Dev.model } else { '--' } 'Enrolled' = if ($Dev.enrolledDateTime) { ([datetime]$Dev.enrolledDateTime).ToString('yyyy-MM-dd') } else { '--' } 'Last Check-In' = if ($Dev.lastSyncDateTime) { ([datetime]$Dev.lastSyncDateTime).ToString('yyyy-MM-dd') } else { '--' } } $DevObj.Add([pscustomobject]$devInObj) | Out-Null } $DevTableParams = @{ Name = "Device Inventory - $TenantId"; ColumnWidths = 14, 9, 9, 10, 9, 16, 10, 10, 7, 6 } if ($Report.ShowTableCaptions) { $DevTableParams['Caption'] = "- $($DevTableParams.Name)" } $DevObj | Table @DevTableParams if (Get-IntuneExcelSheetEnabled -SheetKey 'DeviceInventory') { $script:ExcelSheets['Device Inventory'] = $DevObj } } } #endregion } else { Paragraph "No managed devices found in tenant $TenantId." } } catch { if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Managed Devices' -RequiredRole 'Intune Service Administrator or Global Administrator' } else { Write-AbrSectionError -Section 'Managed Devices' -Message "$($_.Exception.Message)" } } } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Devices' } } |