SCOM 2012 & SCOM 2012 R2 Daily Check PowerShell Script & HTML Report

The SCOM Community is a pretty small group, but there’s always someone out there willing to help which is one of the reasons I enjoy this product so much. There are a few guys out there that have written daily checks for SCOM and they are incredibly useful…so I decided that I would share my own personal version with the world. I’ve gotten inspiration from other guys scripts over the years so credit where it’s due and I’ve tried to mention those people by name where possible.  Feel free to modify it for your own use.

If there is anything else you’d like to see in this report, drop me a line and I’ll see what I can do.

 

What can this report tell you?

  • Your Operational Database & Data Warehouse Servers…how much space they are using, free space, file sizes and locations.
  • The Reporting Server and Web Console Server URL’s and if they are OK.
  • Your Management Server Health, Versions, Server Uptime & the number of Workflows they are running.
  • How many Open Alerts, Groups, Monitors, Rules and State Changes
  • Which Management Packs have been modified in the last 24 Hours.
  • If there are any Overrides in the ‘Default Management Pack’
  • What Agents are in an Uninitialized State.
  • How many Pending Agents you have and in what state they are currently.
  • The number of Open Alerts you have and how many in the last 24 Hours.
  • The Alerts with a Repeat Count higher than a specific value which you choose.
  • If the Action Account has Local Administrator Permissions.
  • What Objects are in Maintenance.
  • Summary of Database Grooming Jobs in the last 24 hours.
  • Validation of your Global SCOM Settings…use this to see if any settings have been changed!

 

Download the Script here: Tims-SCOMHealth-Check

Although I’ve provided the code in this post, I would suggest that you download the script file rather than copy/paste the one you see here, just in case there’s been any funny business when it was formatted by my blog application.

And no, the name of my output file “Timformation” isn’t a typo. It just my bad humor 🙂

So when this script runs, it’ll save a copy of the report to the path you’ve chosen and email you the result in an HTML formatted email. You’ll see output similar to this…

 

 

Timformation Output
Timformation Output 2
Timformation Output 3

 

 

#############################################################################
#
#   Tims-SCOMHealth-Check.ps1
#   Author: Tim Culham
#   Date: 1/10/2014
#   Version: 1.0
#   Website: www.culham.net
#
#   Happy to hear of any bugs, feedback, ideas for improvement.
#   Contact me at the above website.
#
#   I grabbed a couple of ideas from previous scripts on the web, namely
#   Jason Rydstrand and Tao Yang whom have some great scripts available.
#   Some of the SQL Code I honestly can’t remember where it came from, but
#   I suspect I got it from Kevin Holman at some stage. But credit to the
#   original author whomever you are.
#
#   Remember to change the following things:
#   The Database Grooming settings to what ‘YOUR’ environment is set to:
#   (Found in SCOM Console under Administration->Settings->Database Grooming)
#   The path to where you’d like a copy of the HTML Report to be saved.
#   $mailmessage to and from with your correct email addresses & SMTP Server.
#
##############################################################################

$StartTime=Get-Date

# Check if the OperationsManager Module is loaded
if(-not (Get-Module | Where-Object {$_.Name -eq “OperationsManager”}))
{
“The Operations Manager Module was not found…importing the Operations Manager Module”
Import-module OperationsManager
}
else
{
“The Operations Manager Module is loaded”
}

# Connect to localhost when running on the management server or define a server to connect to.
$connect = New-SCOMManagementGroupConnection –ComputerName localhost

# The Name and Location of are we going to save this Report
$ReportLocation = “D:\Tim”
$ReportName = “$(get-date -format “yyyy-M-dd”)-SCOM-Timformation.html”
$ReportPath = “$ReportLocation\$ReportName”

# Create header for HTML Report
$Head = “<style>”
$Head +=”BODY{background-color:#CCCCCC;font-family:Verdana,sans-serif; font-size: small;}”
$Head +=”TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse; width: 98%;}”
$Head +=”TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:#293956;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>”

# Retrieve the name of the Operational Database and Data WareHouse Servers from the registry.
$reg = Get-ItemProperty -Path “HKLM:\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Setup\”
$OperationsManagerDBServer = $reg.DatabaseServerName
$OperationsManagerDWServer = $reg.DataWarehouseDBServerName

# If the value is empty in this key, then we’ll use the Get-SCOMDataWarehouseSetting cmdlet.
If (!($OperationsManagerDWServer))
{$OperationsManagerDWServer = Get-SCOMDataWarehouseSetting | Select -expandProperty DataWarehouseServerName}

$OperationsManagerDBServer = $OperationsManagerDBServer.ToUpper()
$OperationsManagerDWServer = $OperationsManagerDWServer.ToUpper()

$ReportingURL = Get-SCOMReportingSetting | Select -ExpandProperty ReportingServerUrl
$WebConsoleURL = Get-SCOMWebAddressSetting | Select -ExpandProperty WebConsoleUrl

# The number of days before Database Grooming
# These are my settings, I use this to determine if someone has changed something
# Feel free to comment this part out if you aren’t interested

$AlertDaysToKeep = 2
$AvailabilityHistoryDaysToKeep = 2
$EventDaysToKeep = 1
$JobStatusDaysToKeep = 1
$MaintenanceModeHistoryDaysToKeep = 2
$MonitoringJobDaysToKeep = 2
$PerformanceDataDaysToKeep = 2
$StateChangeEventDaysToKeep = 4

# SCOM Agent Heartbeat Settings
$AgentHeartbeatInterval = 180
$MissingHeartbeatThreshold = 3

# SQL Server Function to query the Operational Database Server
function Run-OpDBSQLQuery
{
Param($sqlquery)

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = “Server=$OperationsManagerDBServer;Database=OperationsManager;Integrated Security=True”
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $sqlquery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet) | Out-Null
$SqlConnection.Close()
$DataSet.Tables[0]
}

# SQL Server Function to query the Data Warehouse Database Server
function Run-OpDWSQLQuery
{
Param($sqlquery)

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = “Server=$OperationsManagerDWServer;Database=OperationsManagerDW;Integrated Security=True”
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $sqlquery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet) | Out-Null
$SqlConnection.Close()
$DataSet.Tables[0]
}

# Retrieve the Data for the Majority of the Report
# Truth is we probably don’t need all of this data, but even on a busy environment it only takes a couple of mins to run.

Write-Host “Retrieving Agents”
$Agents = Get-SCOMAgent
Write-Host “Retrieving Alerts”
$Alerts = Get-SCOMAlert
Write-Host “Retrieving Groups”
$Groups = Get-SCOMGroup
Write-Host “Retrieving Management Group”
$ManagementGroup = Get-SCOMManagementGroup
Write-Host “Retrieving Management Packs”
$ManagementPacks = Get-SCOMManagementPack
Write-Host “Retrieving Management Servers”
$ManagementServers = Get-SCOMManagementServer
Write-Host “Retrieving Monitors”
$Monitors = Get-SCOMMonitor
Write-Host “Retrieving Rules”
$Rules = Get-SCOMRule

# Check to see if the Reporting Server Site is OK
$ReportingServerSite = New-Object System.Net.WebClient
$ReportingServerSite = [net.WebRequest]::Create($ReportingURL)
$ReportingServerSite.UseDefaultCredentials = $true
$ReportingServerStatus = $ReportingServerSite.GetResponse() | Select -expandProperty statusCode

# This code can convert the “OK” Result to an Integer, like 200
# (($web.GetResponse()).Statuscode) -as [int]

# Check to see if the Web Server Site is OK
$WebConsoleSite = New-Object System.Net.WebClient
$WebConsoleSite = [net.WebRequest]::Create($WebConsoleURL)
$WebConsoleSite.UseDefaultCredentials = $true
$WebConsoleStatus = $WebConsoleSite.GetResponse() | Select -expandProperty statusCode

# Insert the Database Server details into the Report
$ReportOutput += “<h2>Database Servers</h2>”
$ReportOutput += “<p>Operational Database Server      :  $OperationsManagerDBServer</p>”
$ReportOutput += “<p>Data Warehouse Database Server   :  $OperationsManagerDWServer</p>”

# SQL Server Function to query Size of the Database Server
$DatabaseSize = @”
select a.FILEID,
[FILE_SIZE_MB]=convert(decimal(12,2),round(a.size/128.000,2)),
[SPACE_USED_MB]=convert(decimal(12,2),round(fileproperty(a.name,’SpaceUsed’)/128.000,2)),
[FREE_SPACE_MB]=convert(decimal(12,2),round((a.size-fileproperty(a.name,’SpaceUsed’))/128.000,2)) ,
[GROWTH_MB]=convert(decimal(12,2),round(a.growth/128.000,2)),
NAME=left(a.NAME,15),
FILENAME=left(a.FILENAME,60)
from dbo.sysfiles a

“@

# Run the Size Query against the Operational Database and Data Warehouse Database Servers
$OPDBSize = Run-OpDBSQLQuery $DatabaseSize
$DWDBSize = Run-OpDWSQLQuery $DatabaseSize

# Insert the Size Results for the Operational Database into the Report
$ReportOutput += “<h3>$OperationsManagerDBServer</h4>”
$ReportOutput += $OPDBSize[0] | Select Name, FILE_SIZE_MB, SPACE_USED_MB, FREE_SPACE_MB, FILENAME | ConvertTo-HTML -fragment
$ReportOutput += $OPDBSize[1] | Select Name, FILE_SIZE_MB, SPACE_USED_MB, FREE_SPACE_MB, FILENAME | ConvertTo-HTML -fragment

# Insert the Size Results for the Data Warehouse Database into the Report
$ReportOutput += “<br>”
$ReportOutput += “<h3>$OperationsManagerDWServer</h4>”
$ReportOutput += $DWDBSize[0] | Select Name, FILE_SIZE_MB, SPACE_USED_MB, FREE_SPACE_MB, FILENAME | ConvertTo-HTML -fragment
$ReportOutput += $DWDBSize[1] | Select Name, FILE_SIZE_MB, SPACE_USED_MB, FREE_SPACE_MB, FILENAME | ConvertTo-HTML -fragment

# Insert the Results for the Reporting Server URL into the Report
$ReportOutput += “<h2>Reporting Server</h2>”
$ReportOutput += “<p>Reporting Server URL             :  <a href=`”$ReportingURL/`”>$ReportingURL</a></p>”
$ReportOutput += “<p>The Reporting Server URL Status  :  $ReportingServerStatus</p>”

# Insert the Results for the Web Console URL into the Report
$ReportOutput += “<h2>Web Console Servers</h2>”
$ReportOutput += “<p>Web Console URL                  :  <a href=`”$WebConsoleURL/`”>$WebConsoleURL</a></p>”
$ReportOutput += “<p>The Web Console URL Status       :  $WebConsoleStatus</p>”

# Insert the Results for the Number of Management Servers into the Report
$ReportOutput += “<p>Number of Management Servers     :  $($ManagementServers.count)</p>”

# Retrieve the data for the Management Servers
$ReportOutput += “<br>”
$ReportOutput += “<h2>Management Servers</h2>”

$AllManagementServers = @()

ForEach ($ManagementServer in $ManagementServers)
{

# Find out the Server Uptime for each of the Management Servers
$lastboottime = (Get-WmiObject -Class Win32_OperatingSystem -computername $ManagementServer.Name).LastBootUpTime
$sysuptime = (Get-Date) – [System.Management.ManagementDateTimeconverter]::ToDateTime($lastboottime)
$totaluptime = “” + $sysuptime.days + “ days ” + $sysuptime.hours + “ hours ” + $sysuptime.minutes + “ minutes ” + $sysuptime.seconds + “ seconds”

# Find out the Number of WorkFlows Running on each of the Management Servers
$perfWorkflows = Get-Counter -ComputerName $ManagementServer.Name -Counter “\Health Service\Workflow Count” -SampleInterval 1 -MaxSamples 1

# The Performance Counter seems to return a lot of other characters and spaces…I only want the number of workflows, let’s dump the rest
[int]$totalWorkflows = $perfWorkflows.readings.Split(‘:’)[-1] | Foreach {$_.TrimStart()} | Foreach {$_.TrimEnd()}

$ManagementServerProperty = New-Object psobject
$ManagementServerProperty | Add-Member noteproperty “Management Server” ($ManagementServer.Name)
$ManagementServerProperty | Add-Member noteproperty “Health State” ($ManagementServer.HealthState)
$ManagementServerProperty | Add-Member noteproperty “Version” ($ManagementServer.Version)
$ManagementServerProperty | Add-Member noteproperty “Action Account” ($ManagementServer.ActionAccountIdentity)
$ManagementServerProperty | Add-Member noteproperty “System Uptime” ($totaluptime)
$ManagementServerProperty | Add-Member noteproperty “Workflows” ($totalWorkflows)
$AllManagementServers += $ManagementServerProperty
}

# Insert the Results for the Management Servers into the Report
$ReportOutput += $AllManagementServers | Select “Management Server”, “Health State”, “Version”, “Action Account”, “System Uptime”, “Workflows” | Sort-Object “Management Server” | ConvertTo-HTML -fragment
# SQL Query to find out how many State Changes there were yesterday
$StateChangesYesterday = @”
— How Many State Changes Yesterday?:
select count(*) from StateChangeEvent
where cast(TimeGenerated as date) = cast(getdate()-1 as date)

“@

$StateChanges = Run-OpDBSQLQuery $StateChangesYesterday | Select -ExpandProperty Column1 | Out-String

$AllStats = @()

$StatSummary = New-Object psobject
$StatSummary | Add-Member noteproperty “Open Alerts” (($Alerts | ? {$_.ResolutionState -ne 255}).count)
$StatSummary | Add-Member noteproperty “Groups” ($Groups.Count)
$StatSummary | Add-Member noteproperty “Monitors” ($Monitors.Count)
$StatSummary | Add-Member noteproperty “Rules” ($Rules.Count)
$StatSummary | Add-Member noteproperty “State Changes Yesterday” ($StateChanges | Foreach {$_.TrimStart()} | Foreach {$_.TrimEnd()})

$AllStats += $StatSummary

# Insert the Results for the Stats and State Changes into the Report
$ReportOutput += “<br>”
$ReportOutput += “<h2>Important Stats</h2>”
$ReportOutput += $AllStats | Select “Open Alerts”, “Groups”, “Monitors”, “Rules”, “State Changes Yesterday” | ConvertTo-HTML -fragment

# Retrieve and Insert the Results for the Management Packs that have been modified into the Report
Write-Host “Checking for Management Packs that have been modified in the last 24 hours”

$ReportOutput += “<br>”
$ReportOutput += “<h2>Management Packs Modified in the Last 24 Hours</h2>”
If (!($ManagementPacks | where {$_.LastModified -ge (Get-Date).addhours(-24)}))
{
$ReportOutput += “<p>No Management Packs have been updated in the last 24 hours</p>”
}
Else
{
$ReportOutput += $ManagementPacks | where {$_.LastModified -ge (Get-Date).addhours(-24)} | select Name, LastModified | ConvertTo-HTML -fragment
}

 

# Retrieve and Insert the Results for the Default Management Pack into the Report
# This ‘should be empty’…don’t store stuff here!

Write-Host “Checking for the Default Management Pack for Overrides”
$ReportOutput += “<br>”
$ReportOutput += “<h2>The Default Management Pack</h2>”

# Excluding these 2 ID’s as they are part of the default MP for DefaultUser and Language Code Overrides
$excludedID = “5a67584f-6f63-99fc-0d7a-55587d47619d”, “e358a914-c851-efaf-dda9-6ca5ef1b3eb7”
$defaultMP = $ManagementPacks | where {$_.Name -match “Microsoft.SystemCenter.OperationsManager.DefaultUser”}
If (!($defaultMP.GetOverrides() | ? {$_.ID -notin $excludedID}))
{
$ReportOutput += “<p>There are no Overrides being Stored in the Default Management Pack</p>”
}
Else
{
$foundOverride = Get-SCOMClassInstance -id ($defaultMP.GetOverrides() | ? {$_.ID -notin $excludedID -AND $_.ContextInstance -ne $guid} | select -expandproperty ContextInstance -ea SilentlyContinue)

$ReportOutput += “<p>Found overrides against the following targets: $foundOverride.Displayname</p>”
$ReportOutput += $($defaultMP.GetOverrides() | ? {$_.ID -notin $excludedID} | Select Name, Property, Value, LastModified, TimeAdded) | ConvertTo-HTML -fragment
}

 

# Show all Agents that are in an Uninitialized State
Write-Host “Checking for Uninitialized Agents”

$ReportOutput += “<br>”
$ReportOutput += “<h2>Agents</h2>”
If (!($Agents | where {$_.HealthState -eq “Uninitialized”} | select Name))
{
$ReportOutput += “<p>No Agents are in the Uninitialized State</p>”
}
Else
{
$ReportOutput += $Agents | where {$_.HealthState -eq “Uninitialized”} | select Name | ConvertTo-HTML -fragment
}

 

# Show a Summary of all Agents States
$healthy = $uninitialized = $warning = $critical = 0

Write-Host “Checking Agent States”

$ReportOutput += “<br>”
$ReportOutput += “<h3>Agent Stats</h3>”

switch ($Agents | Select-Object HealthState ) {
{$_.HealthState -like “Success”} {$healthy++}
{$_.HealthState -like “Uninitialized”} {$uninitialized++}
{$_.HealthState -like “Warning”}  {$warning++}
{$_.HealthState -like “Error”} {$critical++}
}
$totalagents = ($healthy + $warning + $critical + $uninitialized)

$AllAgents = @()

$iAgent = New-Object psobject
$iAgent | Add-Member noteproperty “Agents Healthy” ($healthy)
$iAgent | Add-Member noteproperty “Agents Warning” ($warning)
$iAgent | Add-Member noteproperty “Agents Critical” ($critical)
$iAgent | Add-Member noteproperty “Agents Uninitialized” ($uninitialized)
$iAgent | Add-Member noteproperty “Total Agents” ($totalagents)

$AllAgents += $iAgent

$ReportOutput += $AllAgents | Select “Agents Healthy”, “Agents Warning”, “Agents Critical”, “Agents Uninitialized”, “Total Agents” | ConvertTo-HTML -fragment

 

# Agent Pending Management States
Write-Host “Checking Agent Pending Management States”

$ReportOutput += “<br>”
$ReportOutput += “<h3>Agent Pending Management Summary</h3>”

$pushInstall = $PushInstallFailed = $ManualApproval = $RepairAgent = $RepairFailed = $UpdateFailed = 0

$agentpending = Get-SCOMPendingManagement
switch ($agentpending | Select-Object AgentPendingActionType ) {
{$_.AgentPendingActionType -like “PushInstall”} {$pushInstall++}
{$_.AgentPendingActionType -like “PushInstallFailed”} {$PushInstallFailed++}
{$_.AgentPendingActionType -like “ManualApproval”}  {$ManualApproval++}
{$_.AgentPendingActionType -like “RepairAgent”} {$RepairAgent++}
{$_.AgentPendingActionType -like “RepairFailed”} {$RepairFailed++}
{$_.AgentPendingActionType -like “UpdateFailed”} {$UpdateFailed++}

}

$AgentsPending = @()

$AgentSummary = New-Object psobject
$AgentSummary | Add-Member noteproperty “Push Install” ($pushInstall)
$AgentSummary | Add-Member noteproperty “Push Install Failed” ($PushInstallFailed)
$AgentSummary | Add-Member noteproperty “Manual Approval” ($ManualApproval)
$AgentSummary | Add-Member noteproperty “Repair Agent” ($RepairAgent)
$AgentSummary | Add-Member noteproperty “Repair Failed” ($RepairFailed)
$AgentSummary | Add-Member noteproperty “Update Failed” ($UpdateFailed)

$AgentsPending += $AgentSummary

# Produce a table of all Agent Pending Management States and add it to the Report
$ReportOutput += $AgentsPending | Select “Push Install”, “Push Install Failed”, “Manual Approval”, “Repair Agent”, “Repair Failed”, “Update Failed” | ConvertTo-HTML -fragment
$ReportOutput += “<br>”
$ReportOutput += “<h2>Alerts</h2>”

$AlertsAll = ($Alerts | ? {$_.ResolutionState -ne 255}).Count
$AlertsWarning = ($Alerts | ? {$_.Severity -eq “Warning” -AND $_.ResolutionState -ne 255}).Count
$AlertsError = ($Alerts | ? {$_.Severity -eq “Error” -AND $_.ResolutionState -ne 255}).Count
$AlertsInformation = ($Alerts | ? {$_.Severity -eq “Information” -AND $_.ResolutionState -ne 255}).Count
$Alerts24hours = ($Alerts | ? {$_.TimeRaised -ge (Get-Date).addhours(-24) -AND $_.ResolutionState -ne 255}).count

$AllAlerts = @()
$AlertSeverity = New-Object psobject
$AlertSeverity | Add-Member noteproperty “Warning” ($AlertsWarning)
$AlertSeverity | Add-Member noteproperty “Error” ($AlertsError)
$AlertSeverity | Add-Member noteproperty “Information” ($AlertsInformation)
$AlertSeverity | Add-Member noteproperty “Last 24 Hours” ($Alerts24hours)
$AlertSeverity | Add-Member noteproperty “Total Open Alerts” ($AlertsAll)
$AllAlerts += $AlertSeverity

# Produce a table of all alert counts for warning, error, information, Last 24 hours and Total Alerts and add it to the Report
$ReportOutput += $AllAlerts | Select “Warning”, “Error”, “Information”, “Last 24 Hours”, “Total Open Alerts” | ConvertTo-HTML -fragment
# Get the alerts with a repeat count higher than the variable $RepeatCount
$RepeatCount = 200

$ReportOutput += “<br>”
$ReportOutput += “<h3>Alerts with a Repeat Count higher than $RepeatCount</h3>”

# Produce a table of all Open Alerts above the $RepeatCount and add it to the Report
$ReportOutput += $Alerts | ? {$_.RepeatCount -ge $RepeatCount -and $_.ResolutionState -ne 255} | select Name, Category, NetBIOSComputerName, RepeatCount | sort repeatcount -desc | ConvertTo-HTML -fragment

 

 

# Check if the Action Account is a Local Administrator on Each Management Server
# This will only work if the account is a member of the Local Administrators Group directly.
# If it has access by Group Membership, you can look for that Group instead
# $ActionAccount = “YourGrouptoCheck”
# Then replace all 5 occurrences below of $ManagementServer.ActionAccountIdentity with $ActionAccount

Write-Host “Checking if the Action Account is a member of the Local Administrators Group for each Management Server”

$ReportOutput += “<br>”
$ReportOutput += “<h2>SCOM Action Account</h2>”

ForEach ($ms in $ManagementServers.DisplayName | sort DisplayName)
{
$admins = @()
$group =[ADSI]”WinNT://$ms/Administrators”
$members = @($group.psbase.Invoke(“Members”))
$members | foreach {
$obj = new-object psobject -Property @{
Server = $Server
Admin = $_.GetType().InvokeMember(“Name”, ‘GetProperty’, $null, $_, $null)
}
$admins += $obj
}

If ($admins.admin -match $ManagementServer.ActionAccountIdentity)
{
# Write-Host “The user $($ManagementServer.ActionAccountIdentity) is a Local Administrator on $ms”
$ReportOutput += “<p>The user $($ManagementServer.ActionAccountIdentity) is a Local Administrator on $ms</p>”
}
Else
{
# Write-Host “The user $($ManagementServer.ActionAccountIdentity) is NOT a Local Administrator on $ms”
$ReportOutput += “<p><span style=`”color: `#CC0000;`”>The user $($ManagementServer.ActionAccountIdentity) is NOT a Local Administrator on $ms</span></p>”
}
}

# Objects in Maintenance Mode
# If you have a lot of Objects, you might wish to comment out this code, or limit what you put into the Report.

$MMMonitoringObject = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectGenericCriteria(“InMaintenanceMode=1”)
$objectsInMM = $ManagementGroup.GetPartialMonitoringObjects($MMMonitoringObject.Criteria)

$ReportOutput += “<br>”
$ReportOutput += “<h2>Objects in Maintenance</h2>”

If (!($objectsInMM))
{
$ReportOutput += “<p>There are no objects in Maintenance</p>”
}
Else
{
$ReportOutput += $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}}} | ConvertTo-HTML -fragment
}

 

# Database Grooming
$ReportOutput += “<br>”
$ReportOutput += “<h2>Database Grooming</h2>”

# SQL Query to find out what Grooming Jobs have run in the last 24 hours
$DidGroomingRun = @”
— Did Grooming Run?:
SELECT [InternalJobHistoryId]
,[TimeStarted]
,[TimeFinished]
,[StatusCode]
,[Command]
,[Comment]
FROM [dbo].[InternalJobHistory]
WHERE [TimeStarted] >= DATEADD(day, -1, GETDATE())
Order by [TimeStarted]

“@

# Produce a table of Grooming History and add it to the Report
$ReportOutput += Run-OpDBSQLQuery $DidGroomingRun InternalJobHistoryId, TimeStarted, TimeFinished, StatusCode, Command, Comment | Select | ConvertTo-HTML -fragment
# Global Grooming Settings
# Simple comparisons against the values set at the beginning of this script
# I use this to see if anyone has changed the settings. So set the values at the top of this script to match the values that your environment ‘should’ be set to.

$ReportOutput += “<br>”
$ReportOutput += “<h2>SCOM Global Settings</h2>”
$SCOMDatabaseGroomingSettings = Get-SCOMDatabaseGroomingSetting
If ($SCOMDatabaseGroomingSettings.AlertDaysToKeep -ne $AlertDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Alert Days to Keep has been changed! Reset back to $AlertDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Alert Days is correctly set to $AlertDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.AvailabilityHistoryDaysToKeep -ne $AvailabilityHistoryDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Availability History Days has been changed! Reset back to $AvailabilityHistoryDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Availability History Days is correctly set to $AvailabilityHistoryDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.EventDaysToKeep -ne $EventDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Event Days has been changed! Reset back to $EventDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Event Days is correctly set to $EventDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.JobStatusDaysToKeep -ne $JobStatusDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Job Days (Task History) has been changed! Reset back to $JobStatusDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Job Days (Task History) is correctly set to $JobStatusDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.MaintenanceModeHistoryDaysToKeep -ne $MaintenanceModeHistoryDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Maintenance Mode History has been changed! Reset back to $MaintenanceModeHistoryDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Maintenance Mode History is correctly set to $MaintenanceModeHistoryDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.MonitoringJobDaysToKeep -ne $MonitoringJobDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Monitoring Job Data has been changed! Reset back to $MonitoringJobDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Monitoring Job Data is correctly set to $MonitoringJobDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.PerformanceDataDaysToKeep -ne $PerformanceDataDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>Performance Data has been changed! Reset back to $PerformanceDataDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>Performance Data is correctly set to $PerformanceDataDaysToKeep</p>”}

If ($SCOMDatabaseGroomingSettings.StateChangeEventDaysToKeep -ne $StateChangeEventDaysToKeep)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>State Change Data has been changed! Reset back to $StateChangeEventDaysToKeep</span></p>”}
Else {$ReportOutput += “<p>State Change Data is correctly set to $StateChangeEventDaysToKeep</p>”}

# SCOM Agent Heartbeat Settings
$HeartBeatSetting = Get-SCOMHeartbeatSetting

If ($HeartBeatSetting.AgentHeartbeatInterval -ne 180 -AND $HeartBeatSetting.MissingHeartbeatThreshold -ne 3)
{$ReportOutput += “<p><span style=`”color: `#CC0000;`”>The HeartBeat Settings have been changed! Reset back to $AgentHeartbeatInterval and $MissingHeartbeatThreshold</span></p>”}
Else {$ReportOutput += “<p>The HeartBeat Settings are correctly set to Interval: $AgentHeartbeatInterval and Missing Threshold: $MissingHeartbeatThreshold</p>”}

 

# How long did this script take to run?
$EndTime=Get-Date
$TotalRunTime=$EndTime-$StartTime

# Add the time to the Report
$ReportOutput += “<br>”
$ReportOutput += “<p>Total Script Run Time: $($TotalRunTime.hours) hrs $($TotalRunTime.minutes) min $($TotalRunTime.seconds) sec</p>”

# Close the Body of the Report
$ReportOutput += “</body>”

Write-Host “Saving HTML Report to $ReportPath”

# Save the Final Report to a File
ConvertTo-HTML -head $Head -body “$ReportOutput” | Out-File $ReportPath

 

# Send Final Report by email…
Write-Host “Emailing Report”
$SMTPServer =”your.smtpserver.com”
$Body = ConvertTo-HTML -head $Head -body “$ReportOutput”
$SmtpClient = New-Object Net.Mail.SmtpClient($smtpServer);
$mailmessage = New-Object system.net.mail.mailmessage
$mailmessage.from = “sender@company.com”
$mailmessage.To.add(“recipient@company.com”)
# Want more recipient’s? Just add a new line
# $mailmessage.To.add(“anotherrecipient@company.com”)

$MailMessage.IsBodyHtml = $true
$mailmessage.Body = $Body
$smtpclient.Send($mailmessage)

# A bit of Cleanup
Clear-Variable Agents, Alerts, Groups, ManagementGroup, ManagementPacks, ManagementServer, Monitors, Rules, ReportOutput, StartTime, EndTime, TotalRunTime, SCOMDatabaseGroomingSettings

 
Comments

Great Script but can you publish the syntax as well on what needs to be the input for the script

Hello, glad you like it. Just change the script to point to your own path where you want the output (html) file to be created or comment those lines out entirely. At the bottom of the script there is some code for where you want the email to be sent. Just make sure you put in your own email server details and the sender and recipient details.

All you need to do is run it from any SCOM Management Server.

Its a great script. i tested it on my SCOM 2012 R2 sandbox.

Glad you like it, thanks for the feedback 🙂

Hi,
i’m trying to run this script thru a task schduler. when i do that, in the html report, i’m not getting the 3 tables – 2 for databases and 1 for database grooming.
can you pls help me with that.
thanks,
Sameer harpanhalli

Awesome script. I tweaked and extended it a bit– http://www.opsconfig.com/?p=852

Hi and thanks for the positive feedback, I really appreciate it. Looking forward to trying out your version 🙂

Novice to Powershell, I am having trouble with the script to connect to our SCOM server to get the script to run.

Hi Jason, you will need to run this script from one of your SCOM Management Servers. As long as you have permission on that server and your database server/s then the script should run fine and come back with the required information.

Does the script work on SCOM 2007 R2?

Hello. Most of the code would be fine, but anything that uses new SCOM 2012 cmdlets would have to be fixed to work with 2007.

Vladimir Stojkovic

First of all, thanks for the excelent script!
And second, the reason I’m posting this. Please modify the script for backward compatibility with PowerShell 2.0 (eg. Windows Server 2008 R2).

“$_.ID -notin $excludedID” needs to be replaced with “$excludedID -contains $_.ID” everywhere in the script.

This is needed because the “-notin” comparison operator is not available in PowerShell 2.0, only starting from 3.0.

Best regards,
Vladimir

Jason Rydstrand

Just wanted to say thanks for converting this for 2012. I have been meaning to get around too it, but didn’t have the time as I was still working in a 2k7 environment. 🙂

You’re welcome Jason. Thanks for the message 🙂

Fantastic ! makes my job easier 🙂

Hi Tim,

Great Script.

Thanks so much for your comment. Well if you like this one, you’ll love version 2 which I’ll be posting soon…

Trackbacks for this post

Leave a Reply