Views/Devices.ps1
|
function Show-InTUIDevicesView { <# .SYNOPSIS Displays the Devices management view mimicking the Intune Devices blade. #> [CmdletBinding()] param() $exitView = $false while (-not $exitView) { Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Devices') $deviceChoices = @( 'All Devices', 'Windows Devices', 'iOS/iPadOS Devices', 'macOS Devices', 'Android Devices', 'Compliance Overview', 'Search Device', '-------------', 'Back to Home' ) $selection = Show-InTUIMenu -Title "[blue]Devices[/]" -Choices $deviceChoices Write-InTUILog -Message "Devices view selection" -Context @{ Selection = $selection } switch ($selection) { 'All Devices' { Show-InTUIDeviceList } 'Windows Devices' { Show-InTUIDeviceList -OSFilter 'Windows' } 'iOS/iPadOS Devices' { Show-InTUIDeviceList -OSFilter 'iOS' } 'macOS Devices' { Show-InTUIDeviceList -OSFilter 'macOS' } 'Android Devices' { Show-InTUIDeviceList -OSFilter 'Android' } 'Compliance Overview' { Show-InTUIComplianceOverview } 'Search Device' { $searchTerm = Read-InTUITextInput -Message "[blue]Search devices by name[/]" if ($searchTerm) { Write-InTUILog -Message "Searching devices" -Context @{ SearchTerm = $searchTerm } Show-InTUIDeviceList -SearchTerm $searchTerm } } 'Back to Home' { $exitView = $true } default { continue } } } } function Show-InTUIDeviceList { <# .SYNOPSIS Displays a paginated list of managed devices. #> [CmdletBinding()] param( [Parameter()] [string]$OSFilter, [Parameter()] [string]$SearchTerm, [Parameter()] [string]$ComplianceFilter ) $exitList = $false while (-not $exitList) { Clear-Host Show-InTUIHeader $breadcrumb = @('Home', 'Devices') if ($OSFilter) { $breadcrumb += "$OSFilter Devices" } elseif ($SearchTerm) { $breadcrumb += "Search: $SearchTerm" } else { $breadcrumb += 'All Devices' } Show-InTUIBreadcrumb -Path $breadcrumb $filter = @() if ($SearchTerm) { $safe = ConvertTo-InTUISafeFilterValue -Value $SearchTerm $filter += "contains(deviceName,'$safe')" } else { if ($OSFilter) { switch ($OSFilter) { 'Windows' { $filter += "contains(operatingSystem,'Windows')" } 'iOS' { $filter += "(operatingSystem eq 'iOS' or operatingSystem eq 'iPadOS')" } 'macOS' { $filter += "operatingSystem eq 'macOS'" } 'Android' { $filter += "contains(operatingSystem,'Android')" } } } if ($ComplianceFilter) { $filter += "complianceState eq '$ComplianceFilter'" } } $params = @{ Uri = '/deviceManagement/managedDevices' Beta = $true PageSize = 25 Select = 'id,deviceName,operatingSystem,osVersion,complianceState,managedDeviceOwnerType,enrolledDateTime,lastSyncDateTime,userPrincipalName,model,manufacturer,serialNumber,managementAgent' } if ($filter.Count -gt 0) { $params['Filter'] = $filter -join ' and ' } $devices = Show-InTUILoading -Title "[blue]Loading devices...[/]" -ScriptBlock { Get-InTUIPagedResults @params } if ($null -eq $devices -or $devices.Results.Count -eq 0) { Show-InTUIWarning "No devices found." Read-InTUIKey $exitList = $true continue } $deviceChoices = @() foreach ($device in $devices.Results) { $icon = Get-InTUIDeviceIcon -OperatingSystem $device.operatingSystem $compliance = $device.complianceState $compColor = Get-InTUIComplianceColor -State $compliance $lastSync = Format-InTUIDate -DateString $device.lastSyncDateTime $owner = if ($device.userPrincipalName) { $device.userPrincipalName.Split('@')[0] } else { 'Unassigned' } $displayName = "$icon $($device.deviceName) [$compColor]($compliance)[/] [grey]| $owner | $lastSync[/]" $deviceChoices += $displayName } $choiceMap = Get-InTUIChoiceMap -Choices $deviceChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') Show-InTUIStatusBar -Total $devices.TotalCount -Showing $devices.Results.Count -FilterText ($OSFilter ?? $SearchTerm) $selection = Show-InTUIMenu -Title "[blue]Select a device[/]" -Choices $menuChoices if ($selection -eq 'Back') { $exitList = $true } elseif ($selection -ne '─────────────') { $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt $devices.Results.Count) { Show-InTUIDeviceDetail -DeviceId $devices.Results[$idx].id } } } } function Show-InTUIDeviceDetail { <# .SYNOPSIS Displays detailed information about a specific device, mimicking the Intune device detail blade. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$DeviceId ) $exitDetail = $false while (-not $exitDetail) { Clear-Host Show-InTUIHeader $device = Show-InTUILoading -Title "[blue]Loading device details...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId" -Beta } if ($null -eq $device) { Show-InTUIError "Failed to load device details." Read-InTUIKey return } Show-InTUIBreadcrumb -Path @('Home', 'Devices', $device.deviceName) Add-InTUIHistoryEntry -ViewType 'Device' -ViewId $DeviceId -DisplayName $device.deviceName $compColor = Get-InTUIComplianceColor -State $device.complianceState $icon = Get-InTUIDeviceIcon -OperatingSystem $device.operatingSystem $propertiesContent = @" $icon [bold]$($device.deviceName)[/] [grey]Compliance State:[/] [$compColor]$($device.complianceState)[/] [grey]OS:[/] $($device.operatingSystem) $($device.osVersion) [grey]Model:[/] $($device.manufacturer) $($device.model) [grey]Serial Number:[/] $($device.serialNumber ?? 'N/A') [grey]Owner Type:[/] $($device.managedDeviceOwnerType) [grey]Management:[/] $($device.managementAgent) [grey]Enrolled:[/] $(Format-InTUIDate -DateString $device.enrolledDateTime) [grey]Last Sync:[/] $(Format-InTUIDate -DateString $device.lastSyncDateTime) [grey]User:[/] $($device.userPrincipalName ?? 'None') [grey]User Display Name:[/] $($device.userDisplayName ?? 'N/A') "@ Show-InTUIPanel -Title "[blue]Device Properties[/]" -Content $propertiesContent -BorderColor Blue $hwContent = @" [grey]Storage (Total):[/] $([math]::Round(($device.totalStorageSpaceInBytes / 1GB), 1)) GB [grey]Storage (Free):[/] $([math]::Round(($device.freeStorageSpaceInBytes / 1GB), 1)) GB [grey]Physical Memory:[/] $([math]::Round(($device.physicalMemoryInBytes / 1GB), 1)) GB [grey]IMEI:[/] $($device.imei ?? 'N/A') [grey]Wi-Fi MAC:[/] $($device.wiFiMacAddress ?? 'N/A') [grey]EAS Device ID:[/] $($device.easDeviceId ?? 'N/A') [grey]Azure AD Device ID:[/] $($device.azureADDeviceId ?? 'N/A') "@ Show-InTUIPanel -Title "[cyan]Hardware Information[/]" -Content $hwContent -BorderColor Cyan1 # Show Defender status panel for Windows devices if ($device.operatingSystem -match 'Windows') { Show-InTUIDefenderPanel -DeviceId $DeviceId } $actionChoices = @( 'Sync Device', 'Restart Device', 'Device Configuration Status', 'App Install Status', "What's Applied?" ) if ($device.userPrincipalName) { $actionChoices += 'View Primary User' } $actionChoices += @( 'Rename Device', 'Retire Device', 'Wipe Device', '-------------', 'Back to Devices' ) $action = Show-InTUIMenu -Title "[blue]Device Actions[/]" -Choices $actionChoices Write-InTUILog -Message "Device detail action" -Context @{ DeviceId = $DeviceId; DeviceName = $device.deviceName; Action = $action } switch ($action) { 'Sync Device' { Invoke-InTUIDeviceAction -DeviceId $DeviceId -Action 'syncDevice' } 'Restart Device' { $confirm = Show-InTUIConfirm -Message "[yellow]Are you sure you want to restart [bold]$($device.deviceName)[/]?[/]" if ($confirm) { Invoke-InTUIDeviceAction -DeviceId $DeviceId -Action 'rebootNow' } } 'Device Configuration Status' { Show-InTUIDeviceConfigStatus -DeviceId $DeviceId -DeviceName $device.deviceName } 'App Install Status' { Show-InTUIDeviceAppStatus -DeviceId $DeviceId -DeviceName $device.deviceName } "What's Applied?" { Show-InTUIDeviceWhatsApplied -DeviceId $DeviceId -DeviceName $device.deviceName } 'View Primary User' { if ($device.userPrincipalName) { $upn = ConvertTo-InTUISafeFilterValue -Value $device.userPrincipalName $userResult = Invoke-InTUIGraphRequest -Uri "/users?`$filter=userPrincipalName eq '$upn'&`$select=id" if ($userResult.value -and @($userResult.value).Count -gt 0) { Show-InTUIUserDetail -UserId @($userResult.value)[0].id } else { Show-InTUIWarning "Could not resolve user: $($device.userPrincipalName)" Read-InTUIKey } } } 'Rename Device' { $newName = Read-InTUITextInput -Message "[blue]Enter new device name[/]" if ($newName) { $body = @{ deviceName = $newName } $result = Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId" -Method PATCH -Body $body -Beta if ($null -ne $result) { Show-InTUISuccess "Device renamed to '$newName'. Change will apply on next sync." } else { Show-InTUIError "Failed to rename device." } Read-InTUIKey } } 'Retire Device' { $confirm = Show-InTUIConfirm -Message "[red]Are you sure you want to RETIRE [bold]$($device.deviceName)[/]? This will remove company data.[/]" if ($confirm) { Invoke-InTUIDeviceAction -DeviceId $DeviceId -Action 'retire' } } 'Wipe Device' { $confirm = Show-InTUIConfirm -Message "[red]DANGEROUS: Are you sure you want to WIPE [bold]$($device.deviceName)[/]? This will factory reset the device![/]" if ($confirm) { $confirm2 = Show-InTUIConfirm -Message "[red]FINAL CONFIRMATION: This action CANNOT be undone. Proceed with wipe?[/]" if ($confirm2) { Invoke-InTUIDeviceAction -DeviceId $DeviceId -Action 'wipe' } } } 'Back to Devices' { $exitDetail = $true } default { continue } } } } function Invoke-InTUIDeviceAction { <# .SYNOPSIS Executes a remote action on a managed device. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$DeviceId, [Parameter(Mandatory)] [ValidateSet('syncDevice', 'rebootNow', 'retire', 'wipe', 'resetPasscode', 'remoteLock', 'shutDown')] [string]$Action, [Parameter()] [hashtable]$Body ) Write-InTUILog -Message "Executing device action" -Context @{ DeviceId = $DeviceId; Action = $Action } $script:LastActionError = $true Show-InTUILoading -Title "[blue]Executing $Action...[/]" -ScriptBlock { $params = @{ Uri = "/deviceManagement/managedDevices/$DeviceId/$Action" Method = 'POST' Beta = $true } if ($Body) { $params['Body'] = $Body } $result = Invoke-InTUIGraphRequest @params $script:LastActionError = ($null -eq $result) } if ($script:LastActionError) { Write-InTUILog -Level 'ERROR' -Message "Device action failed" -Context @{ DeviceId = $DeviceId; Action = $Action } Show-InTUIWarning "Action '$Action' failed. See error above." } else { Write-InTUILog -Message "Device action initiated" -Context @{ DeviceId = $DeviceId; Action = $Action } Show-InTUISuccess "Action '$Action' has been initiated. Check the device for status." } Read-InTUIKey } function Show-InTUIDeviceConfigStatus { <# .SYNOPSIS Shows device configuration profile status. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$DeviceId, [Parameter()] [string]$DeviceName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Devices', $DeviceName, 'Configuration Status') $configs = Show-InTUILoading -Title "[blue]Loading configuration status...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId/deviceConfigurationStates" -Beta } if (-not $configs.value) { Show-InTUIWarning "No configuration profiles assigned to this device." Read-InTUIKey return } $rows = @() foreach ($config in $configs.value) { $stateColor = switch ($config.state) { 'compliant' { 'green' } 'notCompliant' { 'red' } 'error' { 'red' } 'notApplicable' { 'grey' } default { 'yellow' } } $rows += , @( $config.displayName, "[$stateColor]$($config.state)[/]", ($config.platformType ?? 'N/A'), ($config.settingCount ?? '0') ) } Show-InTUITable -Title "Configuration Profiles" -Columns @('Profile Name', 'State', 'Platform', 'Settings') -Rows $rows Read-InTUIKey } function Show-InTUIDeviceAppStatus { <# .SYNOPSIS Shows app installation status for a device. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$DeviceId, [Parameter()] [string]$DeviceName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Devices', $DeviceName, 'App Install Status') $appStatuses = Show-InTUILoading -Title "[blue]Loading app install status...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId/detectedApps" -Beta } if (-not $appStatuses.value) { Show-InTUIWarning "No app install data available for this device." Read-InTUIKey return } $rows = @() foreach ($app in $appStatuses.value) { $rows += , @( $app.displayName, ($app.version ?? 'N/A'), ($app.sizeInByte ? "$([math]::Round($app.sizeInByte / 1MB, 1)) MB" : 'N/A') ) } Show-InTUITable -Title "Detected Apps" -Columns @('App Name', 'Version', 'Size') -Rows $rows Read-InTUIKey } function Show-InTUIComplianceOverview { <# .SYNOPSIS Shows compliance overview with statistics. #> [CmdletBinding()] param() Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Devices', 'Compliance Overview') $complianceData = Show-InTUILoading -Title "[blue]Loading compliance data...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri '/deviceManagement/managedDeviceOverview' -Beta } if ($null -eq $complianceData) { Show-InTUIWarning "Could not load compliance data." Read-InTUIKey return } # Calculate totals for progress bars $compSum = $complianceData.deviceCompliancePolicyDeviceStateSummary $totalCompliance = ([int]($compSum.compliantDeviceCount ?? 0)) + ([int]($compSum.nonCompliantDeviceCount ?? 0)) + ([int]($compSum.inGracePeriodCount ?? 0)) + ([int]($compSum.errorCount ?? 0)) $compPercent = if ($totalCompliance -gt 0) { [Math]::Round(([int]($compSum.compliantDeviceCount ?? 0) / $totalCompliance) * 100, 1) } else { 0 } $compBar = Get-InTUIProgressBar -Percentage $compPercent -Width 30 $osSum = $complianceData.deviceOperatingSystemSummary $content = @" [bold white]Device Compliance Overview[/] --- [grey dim]Enrollment Summary[/] --- [grey]- Total Enrolled:[/] [white bold]$($complianceData.enrolledDeviceCount ?? 'N/A')[/] [grey]- MDM Enrolled:[/] [white bold]$($complianceData.mdmEnrolledCount ?? 'N/A')[/] [grey]- Dual Enrolled:[/] [white bold]$($complianceData.dualEnrolledDeviceCount ?? 'N/A')[/] --- [grey dim]Compliance Status[/] --- $compBar [white]$compPercent%[/] compliant [green]-[/] Compliant [white bold]$($compSum.compliantDeviceCount ?? 'N/A')[/] [red]-[/] Non-compliant [white bold]$($compSum.nonCompliantDeviceCount ?? 'N/A')[/] [yellow]-[/] In Grace Period [white bold]$($compSum.inGracePeriodCount ?? 'N/A')[/] [grey]-[/] Not Evaluated [white bold]$($compSum.notEvaluatedDeviceCount ?? 'N/A')[/] [red]-[/] Error [white bold]$($compSum.errorCount ?? 'N/A')[/] [orange1]-[/] Conflict [white bold]$($compSum.conflictDeviceCount ?? 'N/A')[/] --- [grey dim]OS Distribution[/] --- [blue]-[/] Windows [white bold]$($osSum.windowsCount ?? 'N/A')[/] [grey]-[/] iOS [white bold]$($osSum.iosCount ?? 'N/A')[/] [white]-[/] macOS [white bold]$($osSum.macOSCount ?? 'N/A')[/] [green]-[/] Android [white bold]$($osSum.androidCount ?? 'N/A')[/] [yellow]-[/] Linux [white bold]$($osSum.linuxCount ?? 'N/A')[/] "@ Show-InTUIPanel -Title "[blue]Compliance Overview[/]" -Content $content -BorderColor Blue $exitOverview = $false while (-not $exitOverview) { $choices = @( 'View Compliant Devices', 'View Non-Compliant Devices', 'Back' ) $selection = Show-InTUIMenu -Title "[blue]Filter by compliance[/]" -Choices $choices switch ($selection) { 'View Compliant Devices' { Show-InTUIDeviceList -ComplianceFilter 'compliant' } 'View Non-Compliant Devices' { Show-InTUIDeviceList -ComplianceFilter 'noncompliant' } 'Back' { $exitOverview = $true } } } } function Get-InTUIThreatLevelDisplay { <# .SYNOPSIS Returns color-coded threat level display. #> [CmdletBinding()] param( [Parameter()] [string]$ThreatLevel ) switch ($ThreatLevel) { 'none' { return "[green]None[/]" } 'low' { return "[yellow]Low[/]" } 'medium' { return "[orange1]Medium[/]" } 'high' { return "[red]High[/]" } 'severe' { return "[red bold]Severe[/]" } default { return "[grey]$($ThreatLevel ?? 'Unknown')[/]" } } } function Show-InTUIDefenderPanel { <# .SYNOPSIS Shows Defender protection status panel for a Windows device. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$DeviceId ) # Fetch Windows protection state $protectionState = Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices/$DeviceId`?`$select=windowsProtectionState" -Beta if (-not $protectionState.windowsProtectionState) { return # No Defender data available } $defender = $protectionState.windowsProtectionState $rtpStatus = if ($defender.realTimeProtectionEnabled) { "[green]Enabled[/]" } else { "[red]Disabled[/]" } $malwareStatus = if ($defender.malwareProtectionEnabled) { "[green]Enabled[/]" } else { "[red]Disabled[/]" } $networkStatus = if ($defender.networkInspectionSystemEnabled) { "[green]Enabled[/]" } else { "[grey]Disabled[/]" } $rebootRequired = if ($defender.rebootRequired) { "[yellow]Yes[/]" } else { "[green]No[/]" } $fullScanRequired = if ($defender.fullScanRequired) { "[yellow]Yes[/]" } else { "[green]No[/]" } $signatureOutOfDate = if ($defender.signatureUpdateOverdue) { "[red]Yes[/]" } else { "[green]No[/]" } $threatLevel = Get-InTUIThreatLevelDisplay -ThreatLevel $defender.deviceThreatState $defenderContent = @" [grey]Real-Time Protection:[/] $rtpStatus [grey]Malware Protection:[/] $malwareStatus [grey]Network Inspection:[/] $networkStatus [grey]Device Threat Level:[/] $threatLevel [grey]Reboot Required:[/] $rebootRequired [grey]Full Scan Required:[/] $fullScanRequired [grey]Signatures Outdated:[/] $signatureOutOfDate [grey]Last Quick Scan:[/] $(Format-InTUIDate -DateString $defender.lastQuickScanDateTime) [grey]Last Full Scan:[/] $(Format-InTUIDate -DateString $defender.lastFullScanDateTime) "@ Show-InTUIPanel -Title "[red]Microsoft Defender[/]" -Content $defenderContent -BorderColor Red } |