Creating SCOM Alert Count Dashboard using the PowerShell Grid Widget
By Steve Bowman
Published October 2, 2015
Estimated Reading Time: 4 minutes

Creating dashboard using the PowerShell Grid widget can offer a lot of flexibility in the way you want to consume the data out of Operations Manager. In my experience, however, it can be much more difficult to configure than the other widgets.
This post will detail the steps for configuring the dashboard to suit your environment. I will also explain each section of the script, and what it is used for.
The first step is to initialize a table variable in the following manner. The table name does not matter and is not used. The object type has to be the one listed. This table will be where we will add any Alerts we wish to display in the dashboard.

$OutputTable = New-Object System.Data.DataTable "Total Alerts"

Once the table variable is created, any script can be utilized to insert data into it. For example, in the script below we are creating an Alert Count for three specific monitors, targeted at a specific group. The group we use for this example is called “Dashboard Target Group”.

## Define the variable which will hold the group class instances
$Groups = Get-SCOMGroup -DisplayName 'Dashboard Target Group'

#Names of the desired Alerts
$LogicalDiskAlertName = 'Logical Disk Free Space is low'
$MemoryAlertName = 'Available Megabytes of Memory is too low'
$CPUAlertName = 'Total CPU Utilization Percentage is too high'

## Retrieve the objects nested in the selected groups
$ClassInstances = $Groups.GetRelatedMonitoringObjects()
$OutputTable = New-Object System.Data.DataTable "Total Alerts"

## Loop through the nested objects and check whether or not they are groups
## If they are groups, grab their contained items.
Do {
    $CIGroups = @()
    $FilteredClassInstances = @()
    Foreach ($CI in $ClassInstances) {
        If ($CI.GetType().Name -eq "MonitoringObjectGroup") {
            $CIGroups += $CI
            $FilteredClassInstances += $CI.GetRelatedMonitoringObjects()
        }
        Else {
            $FilteredClassInstances += $CI
        }
    }
    $ClassInstances = $FilteredClassInstances | Select-Object -Unique
}
Until ($CIGroups.Count -eq 0)

## Get and process the active SCOM alerts to identify ones generated by the selected groups' contents
$SCOMAlerts = @()
$ActiveAlerts = Get-SCOMAlert -ResolutionState 0
foreach ($Alert in $ActiveAlerts) {
    #$i++
    If ($ClassInstances.Id -contains $Alert.MonitoringObjectId) {
        $SCOMAlerts += $Alert
    }
    ElseIf ($Alert.MonitoringObjectPath -notmatch '^$') {
        If ($ClassInstances.DisplayName -contains ($Alert.MonitoringObjectPath.Split(';') | Select-Object -First 1)) {
            $SCOMAlerts += $Alert
        }
    }
}

[array]$DiskAlerts = $SCOMAlerts | ?{$_.Name -eq $LogicalDiskAlertName}
$MemoryAlerts = @($SCOMAlerts | ?{$_.Name -eq $MemoryAlertName})
$CPUAlerts = $SCOMAlerts | ?{$_.Name -eq $CPUAlertName}

Once the script is working as desired we can begin formatting the output table using the Alert variables (In the example using $DiskAlerts, $MemoryAlerts, and $CPUAlerts). In our example, we will be adding the name of the Alert and the Alert Count into the Output Table.

$DiskTable = New-Object System.Object
$DiskTable | Add-Member -type NoteProperty -Name 'count' -Value $DiskAlertCount
$DiskTable | Add-Member -type NoteProperty -Name 'name' -Value "Logical Disk Free Space is low"

$MemoryTable = New-Object System.Object
$MemoryTable | Add-Member -type NoteProperty -Name 'count' -Value $MemoryAlertCount
$MemoryTable | Add-Member -type NoteProperty -Name 'name' -Value "Available Megabytes of Memory is too low"

$CPUTable = New-Object System.Object
$CPUTable | Add-Member -type NoteProperty -Name 'count' -Value $CPUAlertCount
$CPUTable | Add-Member -type NoteProperty -Name 'name' -Value "Total CPU Utilization Percentage is too high"

$OutputTable += $DiskTable
$OutputTable += $MemoryTable
$OutputTable += $CPUTable

Now that the $OutputTable variable contains all data that we want on the dashboard, the next step is to format the data into a form that the SCOM console will accept, which the following portion of the script will perform.
Some notes on this portion of the script. The $ScriptContext variable is a global variable using in SCOM, which will prevent the script from running successfully outside of a PowerShell Grid Widget. The xsd://foo!bar/baz definition does not matter and can be named anything after the XSD:// declaration.

## Output the alert counts
    foreach ($Table in $OutputTable) {
        $i++
        $dataObject = $ScriptContext.CreateInstance("xsd://foo!bar/baz")
        $dataobject["Id"] = $i.tostring()
        $dataObject["Alert Count"] = ($Table.Count).ToString()
        $dataObject["Alert Name"] = ($Table.Name).ToString()
        $ScriptContext.ReturnCollection.Add($dataObject)
    }

The full script is listed below:


# Define the variable which will hold the group class instances
$Groups = Get-SCOMGroup -DisplayName 'Dashboard Target Group'

#Names of the desired Alerts
$LogicalDiskAlertName = 'Logical Disk Free Space is low'
$MemoryAlertName = 'Available Megabytes of Memory is too low'
$CPUAlertName = 'Total CPU Utilization Percentage is too high'

# Retrieve the objects nested in the selected groups
$ClassInstances = $Groups.GetRelatedMonitoringObjects()
$OutputTable = New-Object System.Data.DataTable "Total Alerts"
# Retrieve the objects nested in the selected groups
$ClassInstances = $Groups.GetRelatedMonitoringObjects()
$OutputTable = New-Object System.Data.DataTable "Total Alerts"

# Loop through the nested objects and check whether or not they are groups
# If they are groups, grab their contained items.
Do {
    $CIGroups = @()
    $FilteredClassInstances = @()
    Foreach ($CI in $ClassInstances) {
        If ($CI.GetType().Name -eq "MonitoringObjectGroup") {
            $CIGroups += $CI
            $FilteredClassInstances += $CI.GetRelatedMonitoringObjects()
        }
        Else {
            $FilteredClassInstances += $CI
        }
    }
    $ClassInstances = $FilteredClassInstances | Select-Object -Unique
}
Until ($CIGroups.Count -eq 0)

# Get and process the active SCOM alerts to identify ones generated by the selected groups' contents
$SCOMAlerts = @()
$ActiveAlerts = Get-SCOMAlert -ResolutionState 0
foreach ($Alert in $ActiveAlerts) {
    #$i++
    If ($ClassInstances.Id -contains $Alert.MonitoringObjectId) {
        $SCOMAlerts += $Alert
    }
    ElseIf ($Alert.MonitoringObjectPath -notmatch '^$') {
        If ($ClassInstances.DisplayName -contains ($Alert.MonitoringObjectPath.Split(';') | Select-Object -First 1)) {
            $SCOMAlerts += $Alert
        }
    }
}

[array]$DiskAlerts = $SCOMAlerts | ?{$_.Name -eq $LogicalDiskAlertName}
$MemoryAlerts = @($SCOMAlerts | ?{$_.Name -eq $MemoryAlertName})
$CPUAlerts = $SCOMAlerts | ?{$_.Name -eq $CPUAlertName}


# Save the output into a table
#$OutputTable = $SCOMAlerts | Group-Object Name | Sort-Object Count -Descending | Select-Object -Property Count,Name

$DiskAlertCount = $DiskAlerts | Measure-Object | Select-Object -ExpandProperty Count
$MemoryAlertCount = $MemoryAlerts.Count
$CPUAlertCount = $CPUAlerts.Count

$DiskTable = New-Object System.Object
$DiskTable | Add-Member -type NoteProperty -Name 'count' -Value $DiskAlertCount
$DiskTable | Add-Member -type NoteProperty -Name 'name' -Value "Logical Disk Free Space is low"

$MemoryTable = New-Object System.Object
$MemoryTable | Add-Member -type NoteProperty -Name 'count' -Value $MemoryAlertCount
$MemoryTable | Add-Member -type NoteProperty -Name 'name' -Value "Available Megabytes of Memory is too low"

$CPUTable = New-Object System.Object
$CPUTable | Add-Member -type NoteProperty -Name 'count' -Value $CPUAlertCount
$CPUTable | Add-Member -type NoteProperty -Name 'name' -Value "Total CPU Utilization Percentage is too high"


$OutputTable += $DiskTable
$OutputTable += $MemoryTable
$OutputTable += $CPUTable

# Return the results
If ($OutputTable.count -eq 0) {
    # If there are no alerts to output, say so
    $i++
    [string]$Zero = "0"
    $dataObject = $ScriptContext.CreateInstance("xsd://foo!bar/baz")
    $dataobject["Id"] = $i.tostring()
    $dataObject["Alert Count"] = $Zero
    $dataObject["Alert Name"] = "No alerts present for selected objects"
    $ScriptContext.ReturnCollection.Add($dataObject)
}
Else {
    # Output the alert counts
    foreach ($Table in $OutputTable) {
        $i++
        $dataObject = $ScriptContext.CreateInstance("xsd://foo!bar/baz")
        $dataobject["Id"] = $i.tostring()
        $dataObject["Alert Count"] = ($Table.Count).ToString()
        $dataObject["Alert Name"] = ($Table.Name).ToString()
        $ScriptContext.ReturnCollection.Add($dataObject)
    }
}
Post Tags:
Article By Steve Bowman
Steve Bowman is a Partner at Model Technology as well as their Vice President of Sales and Marketing. Steve is a father, husband, Franciscan, and lover of technology. He's bilingual in business and technology and have over 30 years of experience in selling enterprise technology solutions in a variety of industries.

Related Posts

No more cyber insurance forms!

Tired of filling out endless cyber insurance forms?

  • Download accurate security reports about your environment with a single click.
  • Avoid costly reporting errors.
  • Use your time in better ways.
  • Prove your cyber readiness with confidence.

Download our vCISO platform brochure to learn more.