Public/Show-TCMMonitor.ps1
|
function Show-TCMMonitor { <# .SYNOPSIS Inspect what your TCM monitor is watching — resource types, workloads, and quota. .DESCRIPTION Shows a human-readable breakdown of monitored resource types grouped by workload, with descriptions, severity ratings, and admin portal links. Without parameters: Shows the first (or only) active monitor. With -Profile: Previews a built-in profile definition without needing a monitor. With -Browser: Opens an interactive HTML page with full details. With -Detailed: Adds descriptions and admin portal links to console output. .PARAMETER MonitorId Show a specific monitor by ID. Defaults to the first monitor found. .PARAMETER Profile Preview a built-in profile definition (SecurityCritical, Recommended, Full) without needing an active monitor. Shows which resource types are included. .PARAMETER Detailed Include descriptions and admin portal links in the console output. .PARAMETER Browser Open an interactive HTML page in the default browser instead of console output. .EXAMPLE Show-TCMMonitor # Quick overview of your monitor .EXAMPLE Show-TCMMonitor -Detailed # Full details with descriptions and admin links .EXAMPLE Show-TCMMonitor -ProfileName SecurityCritical # Preview what SecurityCritical monitors .EXAMPLE Show-TCMMonitor -Browser # Open rich HTML view in browser #> [CmdletBinding(DefaultParameterSetName = 'Monitor')] param( [Parameter(ParameterSetName = 'Monitor')] [string]$MonitorId, [Parameter(ParameterSetName = 'Profile', Mandatory)] [ValidateSet('SecurityCritical', 'Recommended', 'Full')] [string]$ProfileName, [switch]$Detailed, [switch]$Browser ) $catalog = Get-TCMResourceTypeCatalog # ── Browser mode (HTML view) ───────────────────────────────── if ($Browser) { if ($PSCmdlet.ParameterSetName -eq 'Profile') { # Profile preview → read-only HTML with profile types pre-selected $profiles = Get-TCMMonitoringProfile $profileTypes = switch ($ProfileName) { 'SecurityCritical' { $profiles.SecurityCritical } 'Recommended' { $profiles.Recommended } 'Full' { @($catalog.Keys) } } $html = Get-TCMMonitorHtml -Catalog $catalog -MonitoredTypes $profileTypes -ProfileLabel $ProfileName -Mode ReadOnly } else { # Monitor mode → read-only HTML with monitor data $monitors = @(Get-TCMMonitor) if ($monitors.Count -eq 0) { Write-Host '' Write-Host ' No monitors found. Create one first:' -ForegroundColor Yellow Write-Host ' Start-TCMMonitoring -Profile Recommended' -ForegroundColor Cyan Write-Host '' return } $mon = if ($MonitorId) { $monitors | Where-Object { $_.Id -eq $MonitorId } } else { $monitors[0] } if (-not $mon) { Write-Warning "Monitor '$MonitorId' not found. Use Get-TCMMonitor to list available monitors." return } $html = Get-TCMMonitorHtml -Catalog $catalog -MonitorId $mon.Id -MonitorDisplayName $mon.DisplayName ` -MonitorStatus $mon.Status -ResourceCount $mon.ResourceCount ` -MonitoredTypes @($mon.MonitoredTypes) -Mode ReadOnly } $tempPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "EasyTCM-Monitor-$(Get-Date -Format 'yyyyMMdd-HHmmss').html") $html | Set-Content -Path $tempPath -Encoding utf8 Start-Process $tempPath Write-Host " Opened in browser: $tempPath" -ForegroundColor Green return } # ── Profile preview mode (console) ──────────────────────────── if ($PSCmdlet.ParameterSetName -eq 'Profile') { Show-TCMProfilePreview -ProfileName $ProfileName -Catalog $catalog -Detailed:$Detailed return } # ── Monitor mode ────────────────────────────────────────────── $monitors = @(Get-TCMMonitor) if ($monitors.Count -eq 0) { Write-Host '' Write-Host ' No monitors found. Create one first:' -ForegroundColor Yellow Write-Host ' Start-TCMMonitoring -Profile Recommended' -ForegroundColor Cyan Write-Host '' return } $monitor = if ($MonitorId) { $monitors | Where-Object { $_.Id -eq $MonitorId } } else { $monitors[0] } if (-not $monitor) { Write-Warning "Monitor '$MonitorId' not found. Use Get-TCMMonitor to list available monitors." return } # ── Build display data ──────────────────────────────────────── $monitoredSet = [System.Collections.Generic.HashSet[string]]::new( [string[]]@($monitor.MonitoredTypes), [StringComparer]::OrdinalIgnoreCase ) # Group catalog entries by workload $workloads = @{} foreach ($key in $catalog.Keys) { $entry = $catalog[$key] $wl = $entry.Workload if (-not $workloads.ContainsKey($wl)) { $workloads[$wl] = @() } $workloads[$wl] += @{ Key = $key; Entry = $entry; Monitored = $monitoredSet.Contains($key) } } # ── Console output ──────────────────────────────────────────── $statusColor = if ($monitor.Status -eq 'active') { 'Green' } else { 'Yellow' } $statusIcon = if ($monitor.Status -eq 'active') { '●' } else { '○' } Write-Host '' Write-Host " $statusIcon $($monitor.DisplayName)" -ForegroundColor $statusColor -NoNewline Write-Host " — $($monitor.ResourceCount) resources across $($monitoredSet.Count) types ($($monitor.Status))" -ForegroundColor Gray Write-Host " Frequency: every $($monitor.Frequency) | Created: $($monitor.CreatedDateTime)" -ForegroundColor DarkGray Write-Host '' $workloadOrder = @('Entra', 'Exchange', 'Teams', 'Intune', 'SecurityAndCompliance') $notMonitored = @() foreach ($wl in $workloadOrder) { if (-not $workloads.ContainsKey($wl)) { continue } $types = $workloads[$wl] | Sort-Object { $_.Entry.DisplayName } $monCount = @($types | Where-Object { $_.Monitored }).Count $totalCount = $types.Count # Friendly workload name $wlDisplay = switch ($wl) { 'Entra' { 'Entra ID' } 'Exchange' { 'Exchange Online' } 'Teams' { 'Microsoft Teams' } 'Intune' { 'Microsoft Intune' } 'SecurityAndCompliance' { 'Security & Compliance' } default { $wl } } Write-Host " $wlDisplay ($monCount/$totalCount types)" -ForegroundColor White foreach ($t in $types) { $e = $t.Entry if ($t.Monitored) { Write-Host " ✅ $($e.DisplayName)" -ForegroundColor Green if ($Detailed) { Write-Host " $($e.Description)" -ForegroundColor DarkGray if ($e.AdminPortal) { Write-Host " 🔗 $($e.AdminPortal)" -ForegroundColor DarkCyan } } } else { $notMonitored += $t } } Write-Host '' } # Not monitored section if ($notMonitored.Count -gt 0) { Write-Host " Not monitored ($($notMonitored.Count) types available)" -ForegroundColor DarkGray foreach ($t in ($notMonitored | Sort-Object { $_.Entry.Workload }, { $_.Entry.DisplayName })) { $e = $t.Entry $quotaWarn = if ($e.QuotaNote) { " ⚠️ $($e.QuotaNote)" } else { '' } Write-Host " ⬚ $($e.DisplayName)$quotaWarn" -ForegroundColor DarkGray if ($Detailed) { Write-Host " $($e.Description)" -ForegroundColor DarkGray if ($e.AdminPortal) { Write-Host " 🔗 $($e.AdminPortal)" -ForegroundColor DarkCyan } } } Write-Host '' } # Quota hint Write-Host " Quota: $($monitor.ResourceCount) resources per run" -ForegroundColor DarkGray Write-Host " 💡 Run " -ForegroundColor DarkGray -NoNewline Write-Host "Show-TCMMonitor -Detailed" -ForegroundColor Cyan -NoNewline Write-Host " for descriptions and admin links" -ForegroundColor DarkGray Write-Host '' } function Show-TCMProfilePreview { <# .SYNOPSIS Internal: shows a profile definition without a monitor. #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$ProfileName, [Parameter(Mandatory)][hashtable]$Catalog, [switch]$Detailed ) $profiles = Get-TCMMonitoringProfile $profileTypes = switch ($ProfileName) { 'SecurityCritical' { $profiles.SecurityCritical } 'Recommended' { $profiles.Recommended } 'Full' { $Catalog.Keys } } $profileSet = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($t in $profileTypes) { [void]$profileSet.Add($t) } Write-Host '' Write-Host " 📊 Profile: $ProfileName — $($profileSet.Count) resource types" -ForegroundColor Cyan Write-Host '' $workloadOrder = @('Entra', 'Exchange', 'Teams', 'Intune', 'SecurityAndCompliance') foreach ($wl in $workloadOrder) { $types = $Catalog.GetEnumerator() | Where-Object { $_.Value.Workload -eq $wl -and $profileSet.Contains($_.Key) } | Sort-Object { $_.Value.DisplayName } if ($types.Count -eq 0) { continue } $wlDisplay = switch ($wl) { 'Entra' { 'Entra ID' } 'Exchange' { 'Exchange Online' } 'Teams' { 'Microsoft Teams' } 'Intune' { 'Microsoft Intune' } 'SecurityAndCompliance' { 'Security & Compliance' } default { $wl } } Write-Host " $wlDisplay ($($types.Count) types)" -ForegroundColor White foreach ($t in $types) { $e = $t.Value $severityColor = switch ($e.Severity) { 'SHALL' { 'Red' } 'SHOULD' { 'Yellow' } default { 'Gray' } } Write-Host " ✅ $($e.DisplayName)" -ForegroundColor Green -NoNewline Write-Host " [$($e.Severity)]" -ForegroundColor $severityColor if ($Detailed) { Write-Host " $($e.Description)" -ForegroundColor DarkGray if ($e.AdminPortal) { Write-Host " 🔗 $($e.AdminPortal)" -ForegroundColor DarkCyan } } } Write-Host '' } } |