Share via


Daily SCOM Health Check with Powershell

While working with an environment that was having issues with their Reporting Services, it was brought up that they had no insight into the Health Of SCOM. What it really turned into, was not having the data in email.

So, I have created the following script that can be ran on a Management Server.

First, example output

As you can see, it covers quite a bit of information.

 

Here is a version of the script that you need to edit for your environment.

##############################################################################
#
#   SCOMHealth-Check.ps1
#
#   Script by: Jason Rydstrand
#
##############################################################################

#Call SCOM powershell plugin and connect to Root Management Server
$rootMS = "RMS-SERVER"
Add-PSSnapin "Microsoft.EnterpriseManagement.OperationsManager.Client" -ErrorVariable errSnapin;
Set-Location "OperationsManagerMonitoring::" -ErrorVariable errSnapin;
new-managementGroupConnection -ConnectionString:$rootMS -ErrorVariable errSnapin;
New-PSDrive -Name: Monitoring -PSProvider: OperationsManagerMonitoring -Root: \ -ErrorAction SilentlyContinue -ErrorVariable Err
set-location $rootMS -ErrorVariable errSnapin;

# Create header for HTML Report
$Head = "<style>"
$Head +="BODY{background-color:#CCCCCC;font-family:Verdana,sans-serif; font-size: x-small;}"
$Head +="TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width: 100%;}"
$Head +="TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:green;color:white;padding: 5px; font-weight: bold;text-align:left;}"
$Head +="TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:#F0F0F0; padding: 2px;}"
$Head +="</style>"

# Get status of Management Server Health and input them into report
write-host "Getting Management Health Server States" -ForegroundColor Yellow
$ReportOutput = "To enable HTML view, click on `"This message was converted to plain text.`" and select `"Display as HTML`""
$ReportOutput += "<p><H2>Management Servers not in Healthy States</H2></p>"

$Count = Get-ManagementServer | where {$_.HealthState -ne "Success"} | Measure-Object

if($Count.Count -gt 0) {
 $ReportOutput += Get-ManagementServer | where {$_.HealthState -ne "Success"} | select Name,HealthState,IsRootManagementServer,IsGateway | ConvertTo-HTML -fragment
} else {
 $ReportOutput += "<p>All management servers are in healthy state.</p>"
}

# Get status of Maintenance Mode for Root Management Server
write-host "Getting RMS Maintenance Mode" -ForegroundColor Yellow
$RMS = Get-ManagementServer | where {$_.IsRootManagementServer -eq $True}
$criteria = new-object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGenericCriteria("InMaintenanceMode=1")
$objectsInMM = (Get-ManagementGroupConnection).ManagementGroup.GetPartialMonitoringObjects($criteria)
$is = "is not"
foreach ($MM in $objectsInMM){
 if($MM.Displayname -eq $RMS.Name){
   $is = "is"
 }
}

$ReportOutput += "<h2>RMS in Maintenance Mode</h2><p>"+ $RMS.Name +" "+$is+" in maintenance mode</p>"

# Get Agent Health Status and put none healthy ones into report
write-host "Getting Agent Health Status" -ForegroundColor Yellow
$criteria = new-object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGenericCriteria("InMaintenanceMode=1")
$objectsInMM = (Get-ManagementGroupConnection).ManagementGroup.GetPartialMonitoringObjects($criteria)
$ObjectsFound = $objectsInMM | select-object DisplayName, @{name="Object Type";expression={foreach-object {$_.GetLeastDerivedNonAbstractMonitoringClass().DisplayName}}},@{name="StartTime";expression={foreach-object {$_.GetMaintenanceWindow().StartTime.ToLocalTime()}}},@{name="EndTime";expression={foreach-object {$_.GetMaintenanceWindow().ScheduledEndTime.ToLocalTime()}}},@{name="Path";expression={foreach-object {$_.Path}}},@{name="User";expression={foreach-object {$_.GetMaintenanceWindow().User}}},@{name="Reason";expression={foreach-object {$_.GetMaintenanceWindow().Reason}}},@{name="Comment";expression={foreach-object {$_.GetMaintenanceWindow().Comment}}}

$ReportOutput += "<h2>Agents where Health State is not Green</h2>"
#$ReportOutput += Get-Agent | where {$_.HealthState -ne "Success"} | Sort-Object HealthState -descending | select Name,HealthState | ConvertTo-HTML -fragment

$Agents = Get-Agent | where {$_.HealthState -ne "Success"} | Sort-Object HealthState -descending | select Name,HealthState

$AgentTable = New-Object System.Data.DataTable "$AvailableTable"
$AgentTable.Columns.Add((New-Object System.Data.DataColumn Name,([string])))
$AgentTable.Columns.Add((New-Object System.Data.DataColumn HealthState,([string])))
$AgentTable.Columns.Add((New-Object System.Data.DataColumn MM,([string])))
$AgentTable.Columns.Add((New-Object System.Data.DataColumn MMUser,([string])))
$AgentTable.Columns.Add((New-Object System.Data.DataColumn MMReason,([string])))
$AgentTable.Columns.Add((New-Object System.Data.DataColumn MMComment,([string])))
$AgentTable.Columns.Add((New-Object System.Data.DataColumn MMEndTime,([string])))

foreach ($Agent in $Agents)
    {
        $FoundObject = $null
    $MaintenanceModeUser = $null
    $MaintenanceModeComment = $null
    $MaintenanceModeReason = $null
    $MaintenanceModeEndTime = $null
        $FoundObject = 0
        $FoundObject = $objectsFound | ? {$_.DisplayName -match $Agent.Name -or $_.Path -match $Agent.Name}
        if ($FoundObject -eq $null)
            {
                $MaintenanceMode = "No"
                $MaintenanceObjectCount = 0
            }
        else
            {
                $MaintenanceMode = "Yes"
                $MaintenanceObjectCount = $FoundObject.Count
        $MaintenanceModeUser = (($FoundObject | Select User)[0]).User
        $MaintenanceModeReason = (($FoundObject | Select Reason)[0]).Reason
        $MaintenanceModeComment = (($FoundObject | Select Comment)[0]).Comment
        $MaintenanceModeEndTime = ((($FoundObject | Select EndTime)[0]).EndTime).ToString()
            }
        $NewRow = $AgentTable.NewRow()
        $NewRow.Name = ($Agent.Name).ToString()
        $NewRow.HealthState = ($Agent.HealthState).ToString()
        $NewRow.MM = $MaintenanceMode
    $NewRow.MMUser = $MaintenanceModeUser
        $NewRow.MMReason = $MaintenanceModeReason
        $NewRow.MMComment = $MaintenanceModeComment
        $NewRow.MMEndTime = $MaintenanceModeEndTime
        $AgentTable.Rows.Add($NewRow)
    }
   
$ReportOutput += $AgentTable | Sort-Object MM | Select Name, HealthState, MM, MMUser, MMReason, MMComment, MMEndTime | ConvertTo-HTML -fragment

# Also put into the report agents that have a state of "Not Monitored" and/or "Unavailable" - Grey Agents
$ReportOutput += "<h2>Agents where the Monitoring Class is not available</h2>"
$AgentMonitoringClass = get-monitoringclass -name "Microsoft.SystemCenter.Agent"
$NotAvailable = Get-MonitoringObject -monitoringclass:$AgentMonitoringClass | where {$_.IsAvailable -eq $false} | select DisplayName
$AvailableTable = New-Object System.Data.DataTable "$AvailableTable"
$AvailableTable.Columns.Add((New-Object System.Data.DataColumn DisplayName,([string])))
$AvailableTable.Columns.Add((New-Object System.Data.DataColumn MM,([string])))
$AvailableTable.Columns.Add((New-Object System.Data.DataColumn MMUser,([string])))
$AvailableTable.Columns.Add((New-Object System.Data.DataColumn MMReason,([string])))
$AvailableTable.Columns.Add((New-Object System.Data.DataColumn MMComment,([string])))
$AvailableTable.Columns.Add((New-Object System.Data.DataColumn MMEndTime,([string])))
foreach ($NotAvailableAgent in $NotAvailable)
    {
        $FoundObject = $null
    $MaintenanceModeUser = $null
    $MaintenanceModeComment = $null
    $MaintenanceModeReason = $null
    $MaintenanceModeEndTime = $null
        $FoundObject = 0
        $FoundObject = $objectsFound | ? {$_.DisplayName -match $NotAvailableAgent.DisplayName -or $_.Path -match $NotAvailableAgent.DisplayName}
        if ($FoundObject -eq $null)
            {
                $MaintenanceMode = "No"
                $MaintenanceObjectCount = 0
            }
        else
            {
                $MaintenanceMode = "Yes"
                $MaintenanceObjectCount = $FoundObject.Count
        $MaintenanceModeUser = (($FoundObject | Select User)[0]).User
        $MaintenanceModeReason = (($FoundObject | Select Reason)[0]).Reason
        $MaintenanceModeComment = (($FoundObject | Select Comment)[0]).Comment
        $MaintenanceModeEndTime = ((($FoundObject | Select EndTime)[0]).EndTime).ToString()
            }
        $NewRow = $AvailableTable.NewRow()
        $NewRow.DisplayName = ($NotAvailableAgent.DisplayName).ToString()
        $NewRow.MM = $MaintenanceMode
    $NewRow.MMUser = $MaintenanceModeUser
        $NewRow.MMReason = $MaintenanceModeReason
        $NewRow.MMComment = $MaintenanceModeComment
        $NewRow.MMEndTime = $MaintenanceModeEndTime
        $AvailableTable.Rows.Add($NewRow)
    }
$ReportOutput += $AvailableTable | Sort-Object MM | Select DisplayName, MM, MMUser, MMReason, MMComment, MMEndTime | ConvertTo-HTML -fragment

# Get Alerts specific to Management Servers and put them in the report
write-host "Getting Management Server Alerts" -ForegroundColor Yellow
$ReportOutput += "<h2>Management Server Alerts</h2>"
$ManagementServers = Get-ManagementServer
foreach ($ManagementServer in $ManagementServers){
 $ReportOutput += "<h3>Alerts on " + $ManagementServer.ComputerName + "</h3>"
 $ReportOutput += get-alert -Criteria ("NetbiosComputerName = '" + $ManagementServer.ComputerName + "'") | where {$_.ResolutionState -ne '255' -and $_.MonitoringObjectFullName -Match 'Microsoft.SystemCenter'} | select TimeRaised,Name,Description,Severity | ConvertTo-HTML -fragment
}

write-host "Getting all alerts"
$Alerts = Get-Alert -Criteria 'ResolutionState < "255"'

# Get alerts for last 24 hours
write-host "Getting alerts for last 24 hours"
$ReportOutput += "<h2>Top 10 Alerts With Same Name - 24 hours</h2>"
$ReportOutput += $Alerts | where {$_.LastModified -le (Get-Date).addhours(-24)} | Group-Object Name | Sort-object Count -desc | select-Object -first 10 Count, Name | ConvertTo-HTML -fragment

$ReportOutput += "<h2>Top 10 Repeating Alerts - 24 hours</h2>"
$ReportOutput += $Alerts | where {$_.LastModified -le (Get-Date).addhours(-24)} | Sort-Object -desc RepeatCount | select-Object -first 10 RepeatCount, Name, MonitoringObjectPath, Description | ConvertTo-HTML -fragment

# Get the Top 10 Unresolved alerts still in console and put them into report
write-host "Getting Top 10 Unresolved Alerts With Same Name - All Time" -ForegroundColor Yellow
$ReportOutput += "<h2>Top 10 Unresolved Alerts</h2>"
$ReportOutput += $Alerts  | Group-Object Name | Sort-object Count -desc | select-Object -first 10 Count, Name | ConvertTo-HTML -fragment

# Get the Top 10 Repeating Alerts and put them into report
write-host "Getting Top 10 Repeating Alerts - All Time" -ForegroundColor Yellow
$ReportOutput += "<h2>Top 10 Repeating Alerts</h2>"
$ReportOutput += $Alerts | Sort -desc RepeatCount | select-object –first 10 Name, RepeatCount, MonitoringObjectPath, Description | ConvertTo-HTML -fragment

# Get list of agents still in Pending State and put them into report
write-host "Getting Agents in Pending State" -ForegroundColor Yellow
$ReportOutput += "<h2>Agents in Pending State</h2>"
$ReportOutput += Get-AgentPendingAction | sort AgentPendingActionType | select AgentName,ManagementServerName,AgentPendingActionType | ConvertTo-HTML -fragment

# Find Overrides that have been stored in the default management pack and put them into the report
write-host "Getting Overrides in Default Management Pack" -ForegroundColor Yellow
$ReportOutput += "<h2>Overrides in Default Management Pack</h2>"
$OverrideCount = Get-ManagementPack | where {$_.DisplayName -match "Default Management Pack"} | get-override | measure-object
if($OverrideCount.Count -gt 2){
 foreach ($monitor in Get-ManagementPack | where {$_.DisplayName -match "Default Management Pack"} | get-override | where {$_.monitor}) {
  $ReportOutput += get-monitor | where {$_.Id -eq $monitor.monitor.id} | select-object DisplayName,Description | ConvertTo-HTML -fragment
  $ReportOutput += "<br />"
 }
 foreach ($rule in Get-ManagementPack | where {$_.DisplayName -match "Default Management Pack"} | get-override | where {$_.rule}) {
  $ReportOutput += get-rule | where {$_.Id -eq $rule.rule.id} | select-object DisplayName,Description | ConvertTo-HTML -fragment
  $ReportOutput += "<br />"
 }
} else {
 $ReportOutput += "<p>There are no unexpected overrides in the Default Management Pack</p>"
}

# List number of MM Objects that have been found.
$ReportOutput += "<h2>Count of objects in Maintanence Mode</h2>"
$CountTable = New-Object System.Data.DataTable "$CountTable"
$CountTable.Columns.Add((New-Object System.Data.DataColumn ObjectCount,([int])))
$NewRow = $CountTable.NewRow()
$NewRow.ObjectCount = $ObjectsFound.Count
$CountTable.Rows.Add($NewRow)
$ReportOutput += $CountTable |Select @{n='ObjectCount';e={$_.ObjectCount}}, @{Name="Date";Expression={Get-Date -Format F}} | ConvertTo-Html -fragment

# List Management Packs updated in last 24 hours
$ReportOutput += "<h2>Management Packs Updated</h2>"
$MPDates = (Get-Date).adddays(-1)
$ReportOutput += Get-ManagementPack | Where {$_.LastModified -gt $MPDates} | Select-Object DisplayName, LastModified | Sort LastModified | ConvertTo-Html -fragment

# Take all $ReportOutput and combine it with $Body to create completed HTML output
$Body = ConvertTo-HTML -head $Head -body "$ReportOutput"

#$Body | Out-File C:\users\adm.j.rydstrand\desktop\HealthCheck-11-14-2012.html

# Setup and send output as email message.
$smtpServer = "outgoing.server"
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = "SCOM.REPORTS.DONOTREPLY@microsoft.com"
$msg.To.Add("jason.rydstrand@microsoft.com")
$msg.Subject = "SCOM Daily Healthcheck Report"
$msg.IsBodyHtml = 1
$msg.Body = $Body
$smtp.Send($msg)

 

SANITIZED-SCOMHealth-Check.ps1

Comments

  • Anonymous
    January 01, 2003
    Excellent Script Jason, I am now using it to report on my 2012 Environment.  I did have a few scripting issues which involved me changing/adding a few things: I run two management groups which caused errors performing 'Get-ManagementGroup' due to two results being returned.   I have changed this to only get the management group that relates to the management group of the RMS server that I specify. Placed a Null value check around the objects in grey state to ensure the code block only run if these existed. Placed a -gt 0 check on the count section to ensure it only fires when objects exist. Changed a cpl of console colors from yellow as I am going blind in my old age :D Great work, cheers Robert

  • Anonymous
    January 01, 2003
    Nice script! Works great, however I've had issues with this on SCOM 2012.  If I have a server and all its contained objects in Maintenance Mode the server doesn't show up in "Agents where Health State is not Green" It does show the # of objects in maintenance mode under "ObjectCount" though.  If I place a single object into MM like a website, I actually get an error in the Script: Exception setting "ObjectCount": "Cannot set Column 'ObjectCount' to be null. Please use DBNull instead." At C:scriptsSCOMHealthCheck.ps1:210 char:1

  • $NewRow.ObjectCount = $ObjectsFound.Count

   + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException    + FullyQualifiedErrorId : CatchFromBaseAdapterSetValue

  • Anonymous
    January 01, 2003
    AnthonyBaileyCDW: Thank you for pointing this out. I should have mentioned that this script was created for SCOM 2007 R2.

  • Anonymous
    January 01, 2003
    Rober: Do you have a copy of the changes you made to work on SCOM 2012? I have changed all my cmdlets but have a few issues with those null values and would love some help if possible, thanks

  • Anonymous
    May 14, 2013
    Great script, provides so much value

  • Anonymous
    July 01, 2013
    I am a newbie on SCOM 2012.. Can you tell me how to setup / deploy the script into SCOM console .... Many thank regards Jerent Chao

  • Anonymous
    July 22, 2013
    I am running the script against 2007 R2 and I've also run into "Exception setting "ObjectCount": "Cannot set Column 'ObjectCount' to be null. Please use DBNull instead." At C:scriptsSCOM_Daily_StatusSCOMHealth-Check.ps1:210 char:9 + $NewRow. <<<< ObjectCount = $ObjectsFound.Count.

  • Anonymous
    November 04, 2013
    The comment has been removed

  • Anonymous
    February 27, 2014
    I'm trying to update this PS Script to make it work with 2012 but still failing, please share if you have one It will be really nice to have it. Thanks for all the hard work

  • Anonymous
    April 09, 2014
    Awesome script! Updated the cmdlets to use the 2012 versions (see http://bit.ly/1egnH1u) and had to adjust the Management Pack Overrides section based on suggestions from this post: http://bit.ly/1n0Poes

  • Anonymous
    May 23, 2014
    Getting following error while running this script on SCOM 2012

    At C:UsersDesktopops_report.ps1:183 char:118
    + $OverrideCount = Get-SCOMManagementPack | where {$.DisplayName -match "Default Management Pack"} | Get-SCOMOverride
    <<<< | measure-object
    + CategoryInfo : InvalidArgument: ([Microsoft.Syst...er.DefaultUser]:PSObject) [Get-SCOMOverride], Parame
    terBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.SystemCenter.OperationsManagerV10.Commands.GetSCOverrideCo
    mmand

    Exception setting "ObjectCount": "Cannot set Column 'ObjectCount' to be null. Please use DBNull instead."
    At C:UsersDesktopops_report.ps1:202 char:9
    + $NewRow. <<<< ObjectCount = $ObjectsFound.Count
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException Desktopops_report.ps1:183 char:118
    + $OverrideCount = Get-SCOMManagementPack | where {$
    .DisplayName -match "Default Management Pack"} | Get-SCOMOverride
    <<<< | measure-object
    + CategoryInfo : InvalidArgument: ([Microsoft.Syst...er.DefaultUser]:PSObject) [Get-SCOMOverride], Parame
    terBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.SystemCenter.OperationsManagerV10.Commands.GetSCOverrideCo
    mmand

    Exception setting "ObjectCount": "Cannot set Column 'ObjectCount' to be null. Please use DBNull instead."
    At C:Users Desktopops_report.ps1:202 char:9
    + $NewRow. <<<< ObjectCount = $ObjectsFound.Count
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    Could you please suggest me.

  • Anonymous
    February 05, 2016
    thank you .
    As Applies to UNIX??