Public/Get-GkDeviceInventory.ps1
|
function Get-GkDeviceInventory { <# .SYNOPSIS Inventory Entra-registered/joined devices with OS, join type, last activity, and a stale flag. .DESCRIPTION Reads GET /devices and reports each device's operating system, join type, compliance/management state, and inactivity derived from approximateLastSignInDateTime. Devices with no recorded activity are treated as stale. Join type is mapped from trustType (per Microsoft Graph): * AzureAd -> AzureADJoined (cloud-only Entra joined) * ServerAd -> HybridJoined (on-premises AD domain / hybrid joined) [there is no "Hybrid" literal] * Workplace -> Registered (personal / BYO registered) Note: approximateLastSignInDateTime is APPROXIMATE — Entra updates it periodically, not in real time — so treat the stale threshold as indicative, not exact. .PARAMETER StaleDays Inactivity threshold in days (default 90). A device is stale when its last activity is at least this many days ago, or when it has no recorded activity. .PARAMETER StaleOnly Return only stale devices. .PARAMETER JoinType Filter by join type: All (default), AzureADJoined, HybridJoined, or Registered. .PARAMETER AsReport Add a ReportGeneratedUtc column for clean export. .EXAMPLE Get-GkDeviceInventory -StaleOnly -StaleDays 180 | Sort-Object InactiveDays -Descending Devices with no activity for 180+ days, most inactive first. .EXAMPLE Get-GkDeviceInventory -JoinType Registered | Where-Object { -not $_.IsCompliant } Personal (registered) devices that are not compliant. .EXAMPLE Get-GkDeviceInventory -AsReport | Export-Csv .\devices.csv -NoTypeInformation .OUTPUTS PSGraphKit.Device #> [CmdletBinding()] [OutputType('PSGraphKit.Device')] param( [ValidateRange(1, 3650)] [int] $StaleDays = 90, [switch] $StaleOnly, [ValidateSet('All', 'AzureADJoined', 'HybridJoined', 'Registered')] [string] $JoinType = 'All', [switch] $AsReport ) begin { Test-GkConnection -FunctionName 'Get-GkDeviceInventory' | Out-Null $now = [datetime]::UtcNow } process { $select = 'id,deviceId,displayName,operatingSystem,operatingSystemVersion,trustType,approximateLastSignInDateTime,registrationDateTime,accountEnabled,isCompliant,isManaged,deviceOwnership' $devices = Invoke-GkGraphRequest -Uri "/devices?`$select=$select&`$top=999" -CallerFunction 'Get-GkDeviceInventory' foreach ($d in $devices) { $trustType = [string](Get-GkDictValue $d 'trustType') $deviceJoin = switch ($trustType) { 'AzureAd' { 'AzureADJoined' } 'ServerAd' { 'HybridJoined' } 'Workplace' { 'Registered' } default { if ($trustType) { $trustType } else { 'Unknown' } } } if ($JoinType -ne 'All' -and $deviceJoin -ne $JoinType) { continue } $lastActivity = ConvertTo-GkDateTime (Get-GkDictValue $d 'approximateLastSignInDateTime') $neverActive = ($null -eq $lastActivity) $inactiveDays = if ($neverActive) { $null } else { [int][math]::Floor(($now - $lastActivity).TotalDays) } $isStale = $neverActive -or ($inactiveDays -ge $StaleDays) if ($StaleOnly -and -not $isStale) { continue } $obj = [ordered]@{ PSTypeName = 'PSGraphKit.Device' DisplayName = [string](Get-GkDictValue $d 'displayName') OperatingSystem = [string](Get-GkDictValue $d 'operatingSystem') OSVersion = [string](Get-GkDictValue $d 'operatingSystemVersion') JoinType = $deviceJoin TrustType = $trustType LastActivity = $lastActivity InactiveDays = $inactiveDays NeverActive = $neverActive IsStale = $isStale IsCompliant = [bool](Get-GkDictValue $d 'isCompliant') IsManaged = [bool](Get-GkDictValue $d 'isManaged') AccountEnabled = [bool](Get-GkDictValue $d 'accountEnabled') Ownership = [string](Get-GkDictValue $d 'deviceOwnership') Registered = ConvertTo-GkDateTime (Get-GkDictValue $d 'registrationDateTime') DeviceId = [string](Get-GkDictValue $d 'deviceId') Id = [string](Get-GkDictValue $d 'id') } if ($AsReport) { $obj['ReportGeneratedUtc'] = $now } [pscustomobject]$obj } } } |