How to create daily reports with System Center Operations Manager 2012 and PowerShell – Part 2


This second entry in this series of blogs delves into reporting on database health. It also aims to explain how PowerShell can be used to extract information from Operations Manager to show the health state of the operations and data warehouse databases and then output this onto an HTML page.

The focus on the Operations Manager databases reflects its key importance in the running of System Center Operations Manager 2012. The operational database contains all configuration data for the management group, it also stores all monitoring data that is collected and processed for the management group. The data warehouse database stores monitoring and alerting data for historical purposes. Information that is written to the Operations Manager database is also written to the data warehouse for long term storage. For more information on the Operations Manager infrastructure please visit Operations Manager Key Concepts at the link provided below:

http://technet.microsoft.com/library/hh230741.aspx 

Subsequent posts will continue to build on this report to provide key reporting of system health.

 

 

 #Retrieves the operational DB name from the connected management server and writes value to $OpsDatabaseName                                                                                                                                                                                                          

 $OpsDatabaseName = get-itemproperty -path “hklm:\software\Microsoft\Microsoft Operations Manager\3.0\Setup” |% {$_.DatabaseName}

 

 

#Retrieves the operational DB Server name, instance name and writes value to $OpsDatabaseSQLServer                                                                                                                                        

$OpsDatabaseSQLServer = get-itemproperty -path “hklm:\software\Microsoft\Microsoft Operations Manager\3.0\Setup” |% {$_.DatabaseServerName}

 

 

#Connects to the operations DB using value from $OpsDatabaseSQLServer and $OpsDatabaseName, gets the data warehouse DB Server name , instance name and writes value to $DWDBServerNameVar

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection

$SqlConnection.ConnectionString = “Server=$OpsDatabaseSQLServer;Database=$OpsDatabaseName;Integrated Security=True”

$SqlCmd = New-Object System.Data.SqlClient.SqlCommand

 $SqlCmd.CommandText = ‘SELECT MainDatabaseServerName_2C77AA48_DB0A_5D69_F8FF_20E48F3AED0F

 FROM MT_Microsoft$SystemCenter$DataWarehouse WITH (NOLOCK)

 ‘

 $SqlCmd.Connection = $SqlConnection

 $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter

 $SqlAdapter.SelectCommand = $SqlCmd

 $DataSet = New-Object System.Data.DataSet

 $SqlAdapter.Fill($DataSet)

 $SqlConnection.Close()

 $DWDBServerNamevar = $DataSet.Tables[0].Rows[0][0]

 

 

#Queries the operations DB that is still connected, gets the data warehouse database name and writes value to $DWDBNamevar

 $SqlCmd = New-Object System.Data.SqlClient.SqlCommand

 $SqlCmd.CommandText = ‘SELECT MainDatabaseName_F46548FC_0DFA_877A_A52C_BB8731EBD70D

 FROM MT_Microsoft$SystemCenter$DataWarehouse WITH (NOLOCK)

 ‘

 $SqlCmd.Connection = $SqlConnection

 $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter

 $SqlAdapter.SelectCommand = $SqlCmd

 $DataSet = New-Object System.Data.DataSet

 $SqlAdapter.Fill($DataSet)

 $SqlConnection.Close()

 $DWDBNamevar = $DataSet.Tables[0].Rows[0][0]

 

 

#Outputs header and formatting for Operations Manager Database Health State

$ReportOutput += “<br>”

$ReportOutput += “<h6>Operations Manager 2012 Database State</h6>”

$ReportOutput += “<hr size=4 width=50% align=left>”

$ReportOutput += “<h5>Operations Manager Database Health State</h5>”

 

#Checks the operational DB health state through the connection to the management server 

$SQLMonitoringClass = Get-SCOMClass -name “Microsoft.SQLServer.Database”

$ReportOutput += Get-SCOMClassInstance -class:$SQLMonitoringClass | where {$_.DisplayName -eq $OpsDatabaseName} | Select Path, DisplayName,HealthState | ConvertTo-HTML

$ReportOutput += “<br>”

$ReportOutput += “<h5>Operations Manager Data Warehouse Health State</h5>”

$ReportOutput += Get-SCOMClassInstance -class:$SQLMonitoringClass | where {$_.DisplayName -eq $DWDBNamevar} | Select Path, DisplayName,HealthState | ConvertTo-HTML

 

#Outputs header and formatting for maintenance tasks over the last day

$ReportOutput += “<br>”

$ReportOutput += “<hr size=4 width=50% align=left>”

$ReportOutput += “<h5>Maintenance tasks in the last day</h5>”

 

#Connects to operational DB by using values from $OpsDatabaseSQLServer and $OpsDatabaseName and checks maintenance tasks that have occurred in the last day 

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection

$SqlConnection.ConnectionString = “Server=$OpsDatabaseSQLServer;Database=$OpsDatabaseName;Integrated Security=True”

$SqlCmd = New-Object System.Data.SqlClient.SqlCommand

$SqlCmd.CommandText = “SELECT * FROM InternalJobHistory where TimeStarted >= DATEADD(day, -1, GETDATE())

$SqlCmd.Connection = $SqlConnection

 $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter

 $SqlAdapter.SelectCommand = $SqlCmd

 $DataSet = New-Object System.Data.DataSet

 $SqlAdapter.Fill($DataSet)

 $SqlConnection.Close()

 $OpsMTQuery = $dataSet.Tables[0].rows | Select-Object InternalJobHistoryId, TimeStarted ,TimeFinished, StatusCode, Command

if($dataSet.Tables[0].rows.count -gt 0){

    $ReportOutput += $OpsMTQuery | ConvertTo-HTML

     }

else{

     $ReportOutput += “<h3>Maintenance tasks are not running</h3>”

     }

 

The default settings for these jobs can be found below

Task

Description

Schedule

 

Discovery Data Grooming

A rule that deletes aged discovery data from the Operations Manager database.

Every day at 2 AM

Partition and Grooming

A rule that runs workflows to partition and deletes aged data from the Operations Manager database.

Every day at 12 AM

Detect and Fix Object Space Inconsistencies

A rule that repairs data block corruption in database schema objects.

Every 30 minutes

Alert Auto Resolve Execute All

A rule that automatically resolves active alerts after a period of time.

Every day at 4 AM

 

Taken from http://technet.microsoft.com/en-us/library/hh212782.aspx

 

 

#Outputs header and formatting for Operations Manager database

$ReportOutput += “<br>”

$ReportOutput += “<hr size=4 width=50% align=left>”

$ReportOutput += “<h5>Operations Manager Database </h5>”

 

#Connects to the operations DB using the values from $opsDatabaseSQLServer and $OpsDatabaseName then checks current space Usage 

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection

$SqlConnection.ConnectionString = “Server=$OpsDatabaseSQLServer;Database=$OpsDatabaseName;Integrated Security=True”

$SqlCmd = New-Object System.Data.SqlClient.SqlCommand

$SqlCmd.CommandText = “select

[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)) , 

NAME=left(a.NAME,15),

FILENAME=left(a.FILENAME,60)

from dbo.sysfiles a

$SqlCmd.Connection = $SqlConnection

 $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter

 $SqlAdapter.SelectCommand = $SqlCmd

 $DataSet = New-Object System.Data.DataSet

 $SqlAdapter.Fill($DataSet)

 $SqlConnection.Close()

 $OpsDWSQLQuery = $dataSet.Tables[0].rows | Select-Object FILE_SIZE_MB, SPACE_USED_MB, FREE_SPACE_MB, NAME, FILENAME 

 $ReportOutput += $OpsDWSQLQuery | ConvertTo-HTML

 

 

#Outputs header and formatting for Operations Manager data warehouse database

$ReportOutput += “<br>”

$ReportOutput += “<hr size=4 width=50% align=left>”

$ReportOutput += “<h5>Operations Manager DW Database </h5>”

 

#Connects to the data warehouse DB using the values from $DWBServerNamevar and $DWDBNamevar then checks current space usage

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection

$SqlConnection.ConnectionString = “Server=$DWDBServerNamevar;Database=$DWDBNamevar ;Integrated Security=True”

$SqlCmd = New-Object System.Data.SqlClient.SqlCommand

$SqlCmd.CommandText = “select

[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)) , 

NAME=left(a.NAME,15),

FILENAME=left(a.FILENAME,60)

from dbo.sysfiles a

$SqlCmd.Connection = $SqlConnection

 $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter

 $SqlAdapter.SelectCommand = $SqlCmd

 $DataSet = New-Object System.Data.DataSet

 $SqlAdapter.Fill($DataSet)

 $SqlConnection.Close()

 $OpsDWSQLQuery = $dataSet.Tables[0].rows | Select-Object FILE_SIZE_MB, SPACE_USED_MB, FREE_SPACE_MB, NAME, FILENAME 

 $ReportOutput += $OpsDWSQLQuery | ConvertTo-HTML

 

 

#Gets the server names of the SQL servers hosting the operational DB and data warehouse DB without instance names

$SQLclassOpsPrincipal = Get-SCOMClassInstance -class:$SQLMonitoringClass | where {$_.DisplayName -eq $OpsDatabaseName} | Select -ExpandProperty *.PrincipalName

$SQLclassOpsPrincipalValue = $SQLclassOpsPrincipal.value

$SQLclassOpsDWPrincipal = Get-SCOMClassInstance -class:$SQLMonitoringClass | where {$_.DisplayName -eq $DWDBNamevar} | Select -ExpandProperty *.PrincipalName

$SQLclassOpsDWPrincipalValue = $SQLclassOpsDWPrincipal.value

$SQLclassOpsPrincipalValue -eq $SQLclassOpsDWPrincipalValue

  

#Checks alerts for SQL servers from above statement. Note: This will also ensure the process is not repeated if the servers have the same name 

$ReportOutput += “<br>”

$ReportOutput += “<h5>Operational Database Server Open Alerts</h5>”

$ReportOutput += get-SCOMAlert | where {$_.MonitoringObjectPath -eq $_.SQLclassOpsPrincipal -and $_.ResolutionState -ne ‘255’} | select TimeRaised,Name,Description,Severity | ConvertTo-HTML 

$ReportOutput += “<br>”

$ReportOutput += “<h5>Data Warehouse Database Server Open Alerts</h5>” 

 

 if($SQLclassOpsPrincipalValue -eq $SQLclassOpsDWPrincipalValue)

 {      $ReportOutput += “<h4>The Data warehouse database is installed on the same server as the Operational database</h4>”

 } else  {

       ReportOutput += get-SCOMAlert | where {$_.MonitoringObjectPath -eq $_.SQLclassOpsDWPrincipal -and $_.ResolutionState -ne ‘255’} | select TimeRaised,Name,Description,Severity | ConvertTo-HTML

 

 }

 

 

#CSS border has also now been added for display purposes to the HTML headings to signify the start of new sections in the HTML output

h6{BORDER-BOTTOM: #b1babf 1px solid; POSITION: relative; BORDER-LEFT: #b1babf 1px solid; BACKGROUND-COLOR: #0061bd; PADDING-LEFT: 5px; DISPLAY: block; FONT-FAMILY: Tahoma; HEIGHT: 2em; COLOR: #ffffff; MARGIN-LEFT: 0px; FONT-SIZE: 12pt; BORDER-TOP: #b1babf 1px solid; FONT-WEIGHT: bold; MARGIN-RIGHT: 0px; BORDER-RIGHT: #b1babf 1px solid; PADDING-TOP: 8px}

 

 

Now run using the command from the previous blog to start the script, replacing parameters as required, as illustrated in this example:

PowerShell.exe Daily-Report.ps1 MSServerName c:\scripts Daily-Report.htm

 

This will produce a report similar to the below :

Daily-Report.ps1

Comments (2)

  1. Anonymous says:

    I have changed the maintenance task section after feedback received, the statement is now corrected from  $ReportOutput += $OpsIntJobsQuery | ConvertTo-HTML  to $ReportOutput +=  $OpsMTQuery  | ConvertTo-HTML with the output also removed from outside the if statement, the latest version is now attached.

  2. Peter Heslop says:

    Hey Richard,

    I'd like to suggest an alternative way of getting the OPS and DW SQL Instance and database names names….

    # Get the OPS DB details

    $OpsDatabaseName = (Get-SCOMMonitoringObject -DisplayName 'All Management Servers Resource Pool DB Watcher').'[Microsoft.SystemCenter.OpsMgrDBWatcher].DatabaseName'.Value

    $OpsDatabaseSQLServer = (Get-SCOMMonitoringObject -DisplayName 'All Management Servers Resource Pool DB Watcher').'[Microsoft.SystemCenter.OpsMgrDBWatcher].DatabaseServerName'.Value

    # Get the DW DB details

    $DWDBServerNamevar = (Get-SCOMMonitoringObject -Name 'OperationsManagerDW').'[Microsoft.Windows.Computer].PrincipalName'.Value+''+(Get-SCOMMonitoringObject -Name 'OperationsManagerDW').'[Microsoft.SQLServer.ServerRole].InstanceName'.Value

    $DWDBNamevar = (Get-SCOMMonitoringObject -Name 'OperationsManagerDW').'[Microsoft.SQLServer.Database].DatabaseName'.Value

    This replaces the following lines….

    #Connects to the operations DB using value from $OpsDatabaseSQLServer and $OpsDatabaseName, gets the data warehouse DB Server name , instance name and writes value to $DWDBServerNameVar

    $SqlConnection = New-Object System.Data.SqlClient.SqlConnection

    Down to (and including)….

    $SqlAdapter.Fill($DataSet)

    $SqlConnection.Close()

    $DWDBNamevar = $DataSet.Tables[0].Rows[0][0]

    This change also allows you to run the code remotely.

    Regards

    Pete