When SCOM is showing multiple computer objects in a critical state it can be very time consuming to use the GUI to open Health Explorer for each computer and then drill down into what the individual problems are.

I adapted a portion of Cory Delamarter's amazing work on recursing the state hierarchy to show the monitors which have errors. His code was missing checks for the ExternalRollupMonitoringState. Mine includes that and so is a lot more complete.

In the example below fill in your own management group name, and a computer group to start with.

$ErrorActionPreference = "Stop" Set -StrictMode -Version Latest $scomManagementGroup = "XXX_XXX" $scomGroup = "YYYY YYYY" function Write -SCOMMonitoringState { [ CmdletBinding ()] param ( [ Parameter ( Position = 0,ValueFromPipeline = $True ,ValueFromPipelinebyPropertyName = $True )] [ PSObject] $MonitoringNode , [ Parameter ( Position = 1 )] [ int ] $Depth ) [ string ] $whitespace = " " * $depth if ( $MonitoringNode .Item.HealthState -ne "Uninitialized" ){ switch ( $MonitoringNode .Item.HealthState ) { "Warning" { $color = "Yellow" } "Error" { if ( $MonitoringNode .Item.MonitorName -like "*SQLServer*" ) { $color = "Gray" } else { $color = "Red" } } default { $color = "White" } } if ( $color -ne "White" ) { $message = $whitespace + "[" + $MonitoringNode .Item.HealthState + "] --- " + $MonitoringNode .Item.MonitorDisplayName + " / " + $MonitoringNode .Item.MonitorName Write-Host $message -ForegroundColor $color if ( $MonitoringNode .Item -is [ Microsoft.EnterpriseManagement.Monitoring.ExternalRollupMonitoringState] ) { Write -ScomMonitoringStateHierarchy -MonitoringHierarchy $MonitoringNode .Item.GetExternalMonitoringStateHierarchies () -Depth ( $depth + 2 ) } } } } function Write -SCOMMonitoringStateHierarchy { [ CmdletBinding ()] param ( [ Parameter ( Position = 0,ValueFromPipeline = $True ,ValueFromPipelinebyPropertyName = $True )] [ PSObject] $MonitoringHierarchy , [ Parameter ( Position = 1 )] [ int ] $Depth = 0 ) foreach ( $Node in $MonitoringHierarchy ) { # Only work on nodes that have something in them if ( $Node ){ # Write the state out to the screen for the object that we were handed Write -SCOMMonitoringState -MonitoringNode $Node -Depth $depth # Sort the child nodes in alphabetical order $SortedChildNodes = $Node .ChildNodes | Sort-Object -Property Item # loop through the child node and either recurse or write state foreach ( $ChildNode in $SortedChildNodes ){ if ( $ChildNode .ChildNodes.Count -ne 0 ) { # It has child nodes so recurse Write -SCOMMonitoringStateHierarchy -MonitoringHierarchy $ChildNode -Depth ( $depth + 2 ) } else { # It has no child nodes so write the state to the screen Write -SCOMMonitoringState -MonitoringNode $ChildNode -Depth ( $depth + 2 ) } } } } } Import-Module OperationsManager New-SCOMManagementGroupConnection $scomManagementGroup # SCOM Management Group Name $computers = Get-SCOMGroup $scomGroup | Get-SCOMClassInstance | Where { $_ .HealthState -eq "Error" } | Sort { $_ . "[Microsoft.Windows.Computer].NetbiosComputerName" .Value } foreach ( $computer in $computers ) { $computer . "[Microsoft.Windows.Computer].NetbiosComputerName" .Value Write -SCOMMonitoringStateHierarchy -MonitoringHierarchy $computer .GetMonitoringStateHierarchy () -Depth 2 Write-Host "" # Blank line between computer objects }

In this case I colour errors in red and put (most) SQL Server errors in grey (because they're the ones that I would look at). As you can see from the sample below it's extremely easy to understand and useful to generate daily health check reports.