Views/Security.ps1
|
function Show-InTUISecurityView { <# .SYNOPSIS Displays the Security view with security baselines, endpoint protection, and BitLocker keys. #> [CmdletBinding()] param() $exitView = $false while (-not $exitView) { Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security') $choices = @( 'Security Baselines', 'Endpoint Protection Policies', 'Microsoft Defender Overview', 'BitLocker Recovery Keys', '-------------', 'Back to Home' ) $selection = Show-InTUIMenu -Title "[red]Security[/]" -Choices $choices Write-InTUILog -Message "Security view selection" -Context @{ Selection = $selection } switch ($selection) { 'Security Baselines' { Show-InTUISecurityBaselineList } 'Endpoint Protection Policies' { Show-InTUIEndpointProtectionList } 'Microsoft Defender Overview' { Show-InTUIDefenderOverview } 'BitLocker Recovery Keys' { Show-InTUIBitLockerKeys } 'Back to Home' { $exitView = $true } default { continue } } } } function Show-InTUISecurityBaselineList { <# .SYNOPSIS Displays a list of security baseline intents. #> [CmdletBinding()] param() $exitList = $false while (-not $exitList) { Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Security Baselines') $params = @{ Uri = '/deviceManagement/intents' Beta = $true PageSize = 25 Select = 'id,displayName,description,lastModifiedDateTime,isAssigned' } $intents = Show-InTUILoading -Title "[red]Loading security baselines...[/]" -ScriptBlock { Get-InTUIPagedResults @params } if ($null -eq $intents -or $intents.Results.Count -eq 0) { Show-InTUIWarning "No security baselines found." Read-InTUIKey $exitList = $true continue } $intentChoices = @() foreach ($intent in $intents.Results) { $assigned = if ($intent.isAssigned) { 'Yes' } else { 'No' } $modified = Format-InTUIDate -DateString $intent.lastModifiedDateTime $displayName = "[white]$(ConvertTo-InTUISafeMarkup -Text $intent.displayName)[/] [grey]| Assigned: $assigned | $modified[/]" $intentChoices += $displayName } $choiceMap = Get-InTUIChoiceMap -Choices $intentChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') Show-InTUIStatusBar -Total $intents.TotalCount -Showing $intents.Results.Count $selection = Show-InTUIMenu -Title "[red]Select a baseline[/]" -Choices $menuChoices if ($selection -eq 'Back') { $exitList = $true } elseif ($selection -ne '─────────────') { $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt $intents.Results.Count) { Show-InTUISecurityBaselineDetail -IntentId $intents.Results[$idx].id } } } } function Show-InTUISecurityBaselineDetail { <# .SYNOPSIS Displays detailed information about a specific security baseline intent. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$IntentId ) $exitDetail = $false while (-not $exitDetail) { Clear-Host Show-InTUIHeader $detailData = Show-InTUILoading -Title "[red]Loading baseline details...[/]" -ScriptBlock { $intent = Invoke-InTUIGraphRequest -Uri "/deviceManagement/intents/$IntentId" -Beta $assign = Invoke-InTUIGraphRequest -Uri "/deviceManagement/intents/$IntentId/assignments" -Beta $states = Invoke-InTUIGraphRequest -Uri "/deviceManagement/intents/$IntentId/deviceStates?`$top=200" -Beta @{ Intent = $intent Assignments = $assign DeviceStates = $states } } $intent = $detailData.Intent $assignments = $detailData.Assignments $deviceStates = $detailData.DeviceStates if ($null -eq $intent) { Show-InTUIError "Failed to load baseline details." Read-InTUIKey return } Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Security Baselines', $intent.displayName) $assigned = if ($intent.isAssigned) { '[green]Yes[/]' } else { '[grey]No[/]' } $propsContent = @" [bold white]$(ConvertTo-InTUISafeMarkup -Text $intent.displayName)[/] [grey]Description:[/] $(if ($intent.description) { $intent.description.Substring(0, [Math]::Min(200, $intent.description.Length)) } else { 'N/A' }) [grey]Assigned:[/] $assigned [grey]Last Modified:[/] $(Format-InTUIDate -DateString $intent.lastModifiedDateTime) "@ Show-InTUIPanel -Title "[red]Baseline Properties[/]" -Content $propsContent -BorderColor Red # Assignments panel $assignmentCount = if ($assignments.value) { @($assignments.value).Count } else { 0 } $assignContent = "[grey]Total Assignments:[/] [white]$assignmentCount[/]" if ($assignments.value) { $assignContent += "`n" foreach ($assignment in $assignments.value) { $targetType = switch ($assignment.target.'@odata.type') { '#microsoft.graph.allLicensedUsersAssignmentTarget' { '[blue]All Users[/]' } '#microsoft.graph.allDevicesAssignmentTarget' { '[blue]All Devices[/]' } '#microsoft.graph.groupAssignmentTarget' { "Group: $($assignment.target.groupId)" } '#microsoft.graph.exclusionGroupAssignmentTarget' { "[red]Exclude:[/] $($assignment.target.groupId)" } default { $assignment.target.'@odata.type' -replace '#microsoft\.graph\.', '' } } $assignContent += "`n $targetType" } } Show-InTUIPanel -Title "[red]Assignments[/]" -Content $assignContent -BorderColor Red # Device state summary panel $stateList = if ($deviceStates.value) { @($deviceStates.value) } else { @() } $succeeded = @($stateList | Where-Object { $_.state -eq 'succeeded' -or $_.state -eq 'compliant' }).Count $errorCount = @($stateList | Where-Object { $_.state -eq 'error' }).Count $conflict = @($stateList | Where-Object { $_.state -eq 'conflict' }).Count $notApplicable = @($stateList | Where-Object { $_.state -eq 'notApplicable' }).Count $stateContent = @" [grey]Total Devices:[/] [white]$($stateList.Count)[/] [green]Succeeded:[/] $succeeded [red]Error:[/] $errorCount [orange1]Conflict:[/] $conflict [grey]Not Applicable:[/] $notApplicable "@ Show-InTUIPanel -Title "[red]Device State Summary[/]" -Content $stateContent -BorderColor Red $actionChoices = @( 'View Device States', '─────────────', 'Back to Baselines' ) $action = Show-InTUIMenu -Title "[red]Baseline Actions[/]" -Choices $actionChoices Write-InTUILog -Message "Security baseline detail action" -Context @{ IntentId = $IntentId; BaselineName = $intent.displayName; Action = $action } switch ($action) { 'View Device States' { Show-InTUISecurityBaselineDeviceStates -IntentId $IntentId -BaselineName $intent.displayName } 'Back to Baselines' { $exitDetail = $true } default { continue } } } } function Show-InTUISecurityBaselineDeviceStates { <# .SYNOPSIS Displays device state table for a security baseline intent. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$IntentId, [Parameter()] [string]$BaselineName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Security Baselines', $BaselineName, 'Device States') $states = Show-InTUILoading -Title "[red]Loading device states...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceManagement/intents/$IntentId/deviceStates?`$top=50" -Beta } if (-not $states.value) { Show-InTUIWarning "No device state data available for this baseline." Read-InTUIKey return } $rows = @() foreach ($state in $states.value) { $stateColor = switch ($state.state) { 'succeeded' { 'green' } 'compliant' { 'green' } 'error' { 'red' } 'conflict' { 'orange1' } 'notApplicable' { 'grey' } default { 'yellow' } } $rows += , @( ($state.deviceDisplayName ?? 'N/A'), "[$stateColor]$($state.state)[/]", ($state.userName ?? 'N/A'), (Format-InTUIDate -DateString $state.lastReportedDateTime) ) } Show-InTUITable -Title "Device States" -Columns @('Device', 'State', 'User', 'Last Reported') -Rows $rows Read-InTUIKey } function Show-InTUIEndpointProtectionList { <# .SYNOPSIS Displays a list of endpoint protection device configurations. #> [CmdletBinding()] param() $exitList = $false while (-not $exitList) { Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Endpoint Protection') $params = @{ Uri = '/deviceManagement/deviceConfigurations' Beta = $true PageSize = 25 Select = 'id,displayName,description,lastModifiedDateTime,createdDateTime' } $configs = Show-InTUILoading -Title "[red]Loading endpoint protection policies...[/]" -ScriptBlock { Get-InTUIPagedResults @params } if ($null -eq $configs -or $configs.Results.Count -eq 0) { Show-InTUIWarning "No device configurations found." Read-InTUIKey $exitList = $true continue } # Client-side filter for endpoint protection types $filteredResults = @($configs.Results | Where-Object { $odataType = $_.'@odata.type' $odataType -match 'endpointProtection' -or $odataType -match 'windowsDefender' -or $odataType -match 'firewallRules' }) if ($filteredResults.Count -eq 0) { Show-InTUIWarning "No endpoint protection policies found." Read-InTUIKey $exitList = $true continue } $configChoices = @() foreach ($config in $filteredResults) { $typeName = ($config.'@odata.type' -replace '#microsoft\.graph\.', '') $modified = Format-InTUIDate -DateString $config.lastModifiedDateTime $displayName = "[white]$(ConvertTo-InTUISafeMarkup -Text $config.displayName)[/] [grey]| $typeName | $modified[/]" $configChoices += $displayName } $choiceMap = Get-InTUIChoiceMap -Choices $configChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') Show-InTUIStatusBar -Total $filteredResults.Count -Showing $filteredResults.Count $selection = Show-InTUIMenu -Title "[red]Select a policy[/]" -Choices $menuChoices if ($selection -eq 'Back') { $exitList = $true } elseif ($selection -ne '─────────────') { $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt $filteredResults.Count) { Show-InTUIEndpointProtectionDetail -ConfigId $filteredResults[$idx].id } } } } function Show-InTUIEndpointProtectionDetail { <# .SYNOPSIS Displays detailed information about an endpoint protection configuration. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ConfigId ) $exitDetail = $false while (-not $exitDetail) { Clear-Host Show-InTUIHeader $detailData = Show-InTUILoading -Title "[red]Loading policy details...[/]" -ScriptBlock { $config = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ConfigId" -Beta $assign = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ConfigId/assignments" -Beta $statuses = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ConfigId/deviceStatuses?`$top=200" -Beta @{ Config = $config Assignments = $assign Statuses = $statuses } } $config = $detailData.Config $assignments = $detailData.Assignments $statuses = $detailData.Statuses if ($null -eq $config) { Show-InTUIError "Failed to load policy details." Read-InTUIKey return } Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Endpoint Protection', $config.displayName) $typeName = ($config.'@odata.type' -replace '#microsoft\.graph\.', '') $propsContent = @" [bold white]$(ConvertTo-InTUISafeMarkup -Text $config.displayName)[/] [grey]Type:[/] $typeName [grey]Description:[/] $(if ($config.description) { $config.description.Substring(0, [Math]::Min(200, $config.description.Length)) } else { 'N/A' }) [grey]Created:[/] $(Format-InTUIDate -DateString $config.createdDateTime) [grey]Last Modified:[/] $(Format-InTUIDate -DateString $config.lastModifiedDateTime) [grey]Version:[/] $($config.version ?? 'N/A') "@ Show-InTUIPanel -Title "[red]Policy Properties[/]" -Content $propsContent -BorderColor Red # Assignments panel $assignmentCount = if ($assignments.value) { @($assignments.value).Count } else { 0 } $assignContent = "[grey]Total Assignments:[/] [white]$assignmentCount[/]" if ($assignments.value) { $assignContent += "`n" foreach ($assignment in $assignments.value) { $targetType = switch ($assignment.target.'@odata.type') { '#microsoft.graph.allLicensedUsersAssignmentTarget' { '[blue]All Users[/]' } '#microsoft.graph.allDevicesAssignmentTarget' { '[blue]All Devices[/]' } '#microsoft.graph.groupAssignmentTarget' { "Group: $($assignment.target.groupId)" } '#microsoft.graph.exclusionGroupAssignmentTarget' { "[red]Exclude:[/] $($assignment.target.groupId)" } default { $assignment.target.'@odata.type' -replace '#microsoft\.graph\.', '' } } $assignContent += "`n $targetType" } } Show-InTUIPanel -Title "[red]Assignments[/]" -Content $assignContent -BorderColor Red # Device status summary panel $statusList = if ($statuses.value) { @($statuses.value) } else { @() } $succeeded = @($statusList | Where-Object { $_.status -eq 'succeeded' }).Count $failed = @($statusList | Where-Object { $_.status -eq 'failed' }).Count $pending = @($statusList | Where-Object { $_.status -eq 'pending' -or $_.status -eq 'notApplicable' }).Count $other = $statusList.Count - $succeeded - $failed - $pending $statusContent = @" [grey]Total Devices:[/] [white]$($statusList.Count)[/] [green]Succeeded:[/] $succeeded [red]Failed:[/] $failed [yellow]Pending:[/] $pending "@ if ($other -gt 0) { $statusContent += "`n[grey]Other:[/] $other" } Show-InTUIPanel -Title "[red]Device Status Summary[/]" -Content $statusContent -BorderColor Red $actionChoices = @( 'View Device Statuses', '─────────────', 'Back to Endpoint Protection' ) $action = Show-InTUIMenu -Title "[red]Policy Actions[/]" -Choices $actionChoices Write-InTUILog -Message "Endpoint protection detail action" -Context @{ ConfigId = $ConfigId; ConfigName = $config.displayName; Action = $action } switch ($action) { 'View Device Statuses' { Show-InTUIEndpointProtectionDeviceStatuses -ConfigId $ConfigId -ConfigName $config.displayName } 'Back to Endpoint Protection' { $exitDetail = $true } default { continue } } } } function Show-InTUIEndpointProtectionDeviceStatuses { <# .SYNOPSIS Displays device status table for an endpoint protection configuration. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ConfigId, [Parameter()] [string]$ConfigName ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Endpoint Protection', $ConfigName, 'Device Statuses') $statuses = Show-InTUILoading -Title "[red]Loading device statuses...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ConfigId/deviceStatuses?`$top=50" -Beta } if (-not $statuses.value) { Show-InTUIWarning "No device status data available for this policy." Read-InTUIKey return } $rows = @() foreach ($status in $statuses.value) { $stateColor = switch ($status.status) { 'succeeded' { 'green' } 'failed' { 'red' } 'error' { 'red' } 'pending' { 'yellow' } 'notApplicable' { 'grey' } default { 'yellow' } } $rows += , @( ($status.deviceDisplayName ?? 'N/A'), "[$stateColor]$($status.status)[/]", ($status.userName ?? 'N/A'), (Format-InTUIDate -DateString $status.lastReportedDateTime) ) } Show-InTUITable -Title "Device Statuses" -Columns @('Device', 'Status', 'User', 'Last Reported') -Rows $rows Read-InTUIKey } function Show-InTUIBitLockerKeys { <# .SYNOPSIS Search for a device and display its BitLocker recovery keys. #> [CmdletBinding()] param() $exitView = $false while (-not $exitView) { Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'BitLocker Recovery Keys') $searchTerm = Read-InTUITextInput -Message "[red]Enter device name or device ID to search[/]" if (-not $searchTerm) { $exitView = $true continue } Write-InTUILog -Message "BitLocker key search" -Context @{ SearchTerm = $searchTerm } # Search for matching devices $devices = Show-InTUILoading -Title "[red]Searching for devices...[/]" -ScriptBlock { $safe = ConvertTo-InTUISafeFilterValue -Value $searchTerm Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices?`$filter=contains(deviceName,'$safe')&`$select=id,deviceName,azureADDeviceId" -Beta } if (-not $devices.value) { Show-InTUIWarning "No devices found matching '$searchTerm'." Read-InTUIKey continue } $deviceChoices = @() foreach ($device in $devices.value) { $deviceChoices += "[white]$($device.deviceName)[/] [grey]| $($device.azureADDeviceId ?? 'No Azure AD ID')[/]" } $choiceMap = Get-InTUIChoiceMap -Choices $deviceChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') $selection = Show-InTUIMenu -Title "[red]Select a device[/]" -Choices $menuChoices if ($selection -eq 'Back') { continue } elseif ($selection -ne '─────────────') { $idx = $choiceMap.IndexMap[$selection] if ($null -ne $idx -and $idx -lt @($devices.value).Count) { $selectedDevice = @($devices.value)[$idx] if (-not $selectedDevice.azureADDeviceId) { Show-InTUIWarning "Selected device does not have an Azure AD Device ID. Cannot retrieve BitLocker keys." Read-InTUIKey continue } Write-InTUILog -Message "Retrieving BitLocker keys for device" -Context @{ DeviceName = $selectedDevice.deviceName AzureADDeviceId = $selectedDevice.azureADDeviceId } Show-InTUIBitLockerKeysForDevice -DeviceName $selectedDevice.deviceName -AzureADDeviceId $selectedDevice.azureADDeviceId } } } } function Show-InTUIBitLockerKeysForDevice { <# .SYNOPSIS Displays BitLocker recovery keys for a specific device. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$DeviceName, [Parameter(Mandatory)] [string]$AzureADDeviceId ) Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'BitLocker Recovery Keys', $DeviceName) # Get BitLocker recovery keys (v1.0, not beta) $keys = Show-InTUILoading -Title "[red]Loading BitLocker recovery keys...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/informationProtection/bitlocker/recoveryKeys?`$filter=deviceId eq '$AzureADDeviceId'" } if (-not $keys.value) { Show-InTUIWarning "No BitLocker recovery keys found for '$DeviceName'." Read-InTUIKey return } Write-InTUILog -Message "BitLocker keys found" -Context @{ DeviceName = $DeviceName; KeyCount = @($keys.value).Count } $rows = @() foreach ($key in $keys.value) { $rows += , @( ($key.id ?? 'N/A'), (Format-InTUIDate -DateString $key.createdDateTime), ($key.volumeType ?? 'N/A'), '[grey]********-****-****-****-************[/]' ) } Show-InTUITable -Title "BitLocker Recovery Keys for $DeviceName" -Columns @('Key ID', 'Created', 'Volume Type', 'Recovery Key') -Rows $rows # Offer to reveal individual keys $revealChoices = @() foreach ($key in $keys.value) { $revealChoices += "Reveal key: $($key.id)" } $choiceMap = Get-InTUIChoiceMap -Choices $revealChoices $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back') $revealSelection = Show-InTUIMenu -Title "[red]Reveal a recovery key?[/]" -Choices $menuChoices if ($revealSelection -ne 'Back' -and $revealSelection -ne '─────────────') { $revealIdx = $choiceMap.IndexMap[$revealSelection] if ($null -ne $revealIdx -and $revealIdx -lt @($keys.value).Count) { $selectedKey = @($keys.value)[$revealIdx] Write-InTUILog -Message "Revealing BitLocker recovery key" -Context @{ KeyId = $selectedKey.id; DeviceName = $DeviceName } $fullKey = Show-InTUILoading -Title "[red]Retrieving recovery key...[/]" -ScriptBlock { Invoke-InTUIGraphRequest -Uri "/informationProtection/bitlocker/recoveryKeys/$($selectedKey.id)?`$select=key" } if ($fullKey.key) { $keyContent = @" [bold white]BitLocker Recovery Key[/] [grey]Key ID:[/] $($selectedKey.id) [grey]Device:[/] $DeviceName [grey]Volume Type:[/] $($selectedKey.volumeType ?? 'N/A') [grey]Created:[/] $(Format-InTUIDate -DateString $selectedKey.createdDateTime) [bold red]Recovery Key:[/] [white]$($fullKey.key)[/] "@ Show-InTUIPanel -Title "[red]Recovery Key[/]" -Content $keyContent -BorderColor Red } else { Show-InTUIWarning "Could not retrieve the recovery key. Check permissions." } Read-InTUIKey } } } function Show-InTUIDefenderOverview { <# .SYNOPSIS Displays Microsoft Defender aggregate status across all Windows devices. #> [CmdletBinding()] param() Clear-Host Show-InTUIHeader Show-InTUIBreadcrumb -Path @('Home', 'Security', 'Microsoft Defender Overview') Write-InTUILog -Message "Loading Defender overview" $data = Show-InTUILoading -Title "[red]Loading Defender status...[/]" -ScriptBlock { # Get Windows devices with protection state $devices = Invoke-InTUIGraphRequest -Uri "/deviceManagement/managedDevices?`$filter=contains(operatingSystem,'Windows')&`$select=id,deviceName,windowsProtectionState&`$top=200" -Beta if (-not $devices.value) { return $null } $stats = @{ Total = 0 RTPEnabled = 0 RTPDisabled = 0 MalwareProtectionEnabled = 0 SignaturesUpToDate = 0 SignaturesOutdated = 0 RebootRequired = 0 FullScanRequired = 0 ThreatLevels = @{ None = 0 Low = 0 Medium = 0 High = 0 Severe = 0 Unknown = 0 } DevicesWithThreats = @() } foreach ($device in $devices.value) { if (-not $device.windowsProtectionState) { continue } $stats.Total++ $defender = $device.windowsProtectionState if ($defender.realTimeProtectionEnabled) { $stats.RTPEnabled++ } else { $stats.RTPDisabled++ } if ($defender.malwareProtectionEnabled) { $stats.MalwareProtectionEnabled++ } if ($defender.signatureUpdateOverdue) { $stats.SignaturesOutdated++ } else { $stats.SignaturesUpToDate++ } if ($defender.rebootRequired) { $stats.RebootRequired++ } if ($defender.fullScanRequired) { $stats.FullScanRequired++ } $threatState = $defender.deviceThreatState ?? 'unknown' switch ($threatState) { 'none' { $stats.ThreatLevels.None++ } 'low' { $stats.ThreatLevels.Low++ } 'medium' { $stats.ThreatLevels.Medium++ } 'high' { $stats.ThreatLevels.High++; $stats.DevicesWithThreats += $device } 'severe' { $stats.ThreatLevels.Severe++; $stats.DevicesWithThreats += $device } default { $stats.ThreatLevels.Unknown++ } } } $stats } if ($null -eq $data -or $data.Total -eq 0) { Show-InTUIWarning "No Windows devices with Defender data found." Read-InTUIKey return } Write-InTUILog -Message "Defender overview loaded" -Context @{ TotalDevices = $data.Total RTPEnabled = $data.RTPEnabled ThreatCount = $data.ThreatLevels.High + $data.ThreatLevels.Severe } # Protection Status Panel $rtpPercent = if ($data.Total -gt 0) { [math]::Round(($data.RTPEnabled / $data.Total) * 100) } else { 0 } $protectionContent = @" [bold]Real-Time Protection[/] [green]Enabled:[/] $($data.RTPEnabled) devices ($rtpPercent%) [red]Disabled:[/] $($data.RTPDisabled) devices [bold]Malware Protection[/] [green]Enabled:[/] $($data.MalwareProtectionEnabled) devices [bold]Signature Status[/] [green]Up to Date:[/] $($data.SignaturesUpToDate) devices [red]Outdated:[/] $($data.SignaturesOutdated) devices [bold]Actions Required[/] [yellow]Reboot Required:[/] $($data.RebootRequired) devices [yellow]Full Scan Required:[/] $($data.FullScanRequired) devices "@ Show-InTUIPanel -Title "[red]Protection Status ($($data.Total) Windows Devices)[/]" -Content $protectionContent -BorderColor Red # Threat Levels Panel $threatContent = @" [green]None:[/] $($data.ThreatLevels.None) devices [yellow]Low:[/] $($data.ThreatLevels.Low) devices [orange1]Medium:[/] $($data.ThreatLevels.Medium) devices [red]High:[/] $($data.ThreatLevels.High) devices [red bold]Severe:[/] $($data.ThreatLevels.Severe) devices "@ Show-InTUIPanel -Title "[red]Threat Levels[/]" -Content $threatContent -BorderColor Red # Show devices with high/severe threats if ($data.DevicesWithThreats.Count -gt 0) { Write-InTUIText "[red bold]Devices with High/Severe Threats:[/]" Write-InTUIText "" $rows = @() foreach ($device in $data.DevicesWithThreats) { $threatLevel = Get-InTUIThreatLevelDisplay -ThreatLevel $device.windowsProtectionState.deviceThreatState $rows += , @( ($device.deviceName ?? 'N/A'), $threatLevel ) } Show-InTUITable -Title "Devices Requiring Attention" -Columns @('Device', 'Threat Level') -Rows $rows -BorderColor Red } Read-InTUIKey } |