How do I export daily server statistics of Exchange 2013 to CSV file from SCOM Database?
Hi,
I have taken over SCOM environment where Exchange 2010 was being monitored via SCOM 2007. I upgraded SCOM to 2019 and Exchange to 2016. Also installed the Exchange 2k3 MP.
Now, in their old environment, there was a PowerShell script running that would export Exchange 2010 transport daily statistics to a csv
file. I have looked at the script; it's using the stored procedure called "[Exchange2010].[Transport-Platform-ServerHourly-Report]".
I looked at the stored procedure in SCOM DB, i.e. OperationsManagerDW that is using a table view called "[Exchange2010].[vServerStatisticsDailyV14]"
Now the problem is that new Exchange 2013 Management doesn't come with the old stored procedure and table views. I have already checked the SCOM 2019 database.
I need to convert that PowerShell script for Exchange 2013 but couldn't find the required stored procedure and Table Views.
How can I be sure that required data fields are being monitored by SCOM 2019 for Exchange 2013.
I am tagging both SCOM and SQL community to seek help.
Following are the Column Names of the exported CSV file:
Date
Time
ClientName
ServerName
TotalMessagesDeliveredToLocal
TotalMessagesDeliveredToIntraOrg
TotalMessagesDeliveredToForeignGateway
TotalMessagesDeliveredToForeignAgent
TotalMessagesDeliveredToForeignSmtp
TotalMessagesRoutingFailedForForeign
TotalMessagesAgentFailedForForeign
DSNMessages
EndToEndMessagesInSlaCount
EndToEndMessagesTotalCount
LocalMessagesInSlaCount
LocalMessagesTotalCount
I have pasted the scripts belows.
Following is the PowerShell script that was exporting the data to the csv file:
#==============================================================================
# Exchange 2010 Transport Statistics Daily Export
#==============================================================================
# Read in the parameters (must be first line in the script)
param([string]$cfg = 'DefaultConfig')
# Define the current script revision, for display at the bottom of the report
$CurrentRevision = 1.1
# Get the script path
$CurrentPath = Split-Path $MyInvocation.MyCommand.Path
# Set default variables
$DefaultConfigurationFile = $CurrentPath + "\Configuration.ps1" # For ComputerName and Database Name
$NewLine = [System.Environment]::NewLine
$YesterdaysDate = (Get-Date).AddDays(-1).ToString('yyyyMMdd')
#==========================================================
# Function Locate-FileInHierarchy
#==========================================================
Function Locate-FileInHierarchy
{
Param (
[String]$strFileName,
[Array]$arrAdditionalSubPaths
)
$arrSubPaths = $null
If ($arrAdditionalSubPaths -is [Object]) {
$arrSubPaths = @()
$arrSubPaths += $arrAdditionalSubPaths
$arrAdditionalSubPaths = @()
ForEach ($objSubPath in $arrSubPaths) {
If ($objSubPath -is [String]) {
If ($objSubPath.Length -gt 0) {
$arrAdditionalSubPaths += $objSubPath
}
}
}
$arrAdditionalSubPaths = [Array]($arrAdditionalSubPaths | Sort -unique)
}
$SearchPath = $CurrentPath
$objFile = $null
If ($strFileName -is [String]) {
If ($strFileName.Length -gt 0) {
Do {
$objFile = Get-ChildItem -path $SearchPath -filter $strFileName
If ($objFile -eq $null) {
If ($arrAdditionalSubPaths -is [Array]) {
ForEach ($strSubPath in $arrAdditionalSubPaths) {
$objFile = Get-ChildItem -path (Join-Path $SearchPath $strSubPath) -filter $strFileName
}
}
}
$SearchPath = Split-Path -path $SearchPath -parent
} While (($SearchPath.Length -gt 0) -and ($objFile -eq $null))
}
}
$objFile.FullName
}
#==========================================================
# Load the various include files:
# Common Functions - Generic constants and functions
# General Config - Client/site specific constants
# Script-specific configuration file
#==========================================================
$ConfigFilesLoaded = $true
#==========================================================
# Common Functions - Generic constants and functions
$SearchPath = $CurrentPath
$ConfigFileName = 'CommonFunctions.ps1'
$strConfigFileName = Locate-FileInHierarchy $ConfigFileName
If ($strConfigFileName -ne $null) {
. $strConfigFileName
If ($? -eq $false) {
Write-Host "ERROR: Failed to load Common Functions file `'$ConfigFileName`'" -f Red
$ConfigFilesLoaded = $false
}
} Else {
$ConfigFilesLoaded = $false
}
#==========================================================
# General Configuration - Local configuration variables
$SearchPath = $CurrentPath
$ConfigFileName = 'GeneralConfiguration.ps1'
$strConfigFileName = Locate-FileInHierarchy $ConfigFileName
If ($strConfigFileName -ne $null) {
. $strConfigFileName
If ($? -eq $false) {
Write-Host "ERROR: Failed to load general configuration file `'$ConfigFileName`'" -f Red
$ConfigFilesLoaded = $false
}
} Else {
$ConfigFilesLoaded = $false
}
#==========================================================
# Load script-specific configuration variables
If ($cfg.Length -gt 0) {
$ConfigFile = $cfg.Trim()
If ($ConfigFile -ne 'DefaultConfig') {
If ($ConfigFile.Contains("\") -eq $false) {
$ConfigFile = "$CurrentPath\$ConfigFile"
}
If($ConfigFile.EndsWith(".ps1") -eq $false) {
Write-Host "ERROR: Configuration file $ConfigFile is not a PowerShell script file. Using default Configuration file." -f Red
$ConfigFile = "$CurrentPath\NBN Configuration.ps1"
}
If((Test-Path -path:$ConfigFile) -eq $false) {
Write-Host "ERROR: Configuration file $ConfigFile not found. Using default Configuration file." -f Red
$ConfigFile = "$CurrentPath\NBN Configuration.ps1"
}
} Else {
$ConfigFile = "$CurrentPath\NBN Configuration.ps1"
}
} Else {
$ConfigFile = "$CurrentPath\NBN Configuration.ps1"
}
. $ConfigFile
If ($? -eq $false) {
Write-Host "ERROR: Failed to load script configuration file" -f Red
$ConfigFilesLoaded = $false
}
#==============================================================================
# Functions
#==============================================================================
#==========================================================
# Function Execute-SQLQuery
#==========================================================
# Return Values:
# [Dataset] $Dataset
#
#==========================================================
Function Execute-SQLQuery
{
Param ( [String]$SQLQuery,
[String]$SQLServerInstance,
[String]$Database )
If ($SQLQuery -is [String]) {
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Data Source=$SQLServerInstance;Initial Catalog=$Database;Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
#$SqlCmd.CommandTimeout = 120
$SqlCmd.CommandTimeout = 900
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$nRecs = $SqlAdapter.Fill($DataSet)
If ($? -eq $false) {
out-log "Failed to query database - Connection String : $($SqlConnection.ConnectionString)"
out-log "SQL Query : $SqlQuery"
}
}
If ($nRecs -gt 0) {
Return $DataSet
} Else {
Return $null
}
}
#==========================================================
# Internal Script Variables
#==========================================================
$ScriptUserName = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).Name
$ProcessName = "Exch2010Stats"
#==========================================================
# Log path
#==========================================================
Prepare-Log
If ($LogFile -eq $null) {
Write-Host "Logging not prepared."
} Else {
out-log "--- Script Started ---"
}
#==========================================================
# Define the output path and files
#==========================================================
$OutputPath = "$CurrentPath\Reports"
# Create the output directory, if it doesn't exist
$OutputPathExists = $false
If ((Test-Path -path $OutputPath -pathType container) -eq $true)
{
$OutputPathExists = $true
} Else {
New-Item -type Directory -path $OutputPath | Out-Null
If (($?) -eq $false) {
Write-Host "ERROR: Failed to create the 'Reports' directory."
} Else {
$OutputPathExists = $true
}
}
#==========================================================
# Check all of the Prerequisites
#==========================================================
$Prerequisites = $false
If (($ConfigFilesLoaded -eq $true) -and ($OutputPathExists -eq $true)) {
$Prerequisites = $true
}
#==============================================================================
# Main Routine
#==============================================================================
If ($Prerequisites -eq $true) {
out-log "Prerequisites OK - Starting"
# blnMonthlyReport is set to false in the "NBN Configuration.ps1" file so this will default to daily.
If ($blnMonthlyReport) {
$dteEndDate = New-Object DateTime(((Get-Date).Year), ((Get-Date).Month), 1)
} Else {
$dteEndDate = New-Object DateTime(((Get-Date).Year), ((Get-Date).Month), ((Get-Date).Day))
}
$dteStartDate = $dteEndDate.AddDays(-1 * $intNumOfDays)
$arrFilesForCompression = @()
#------------------------------------------------------------------------------
# Exchange2010 : Transport-Platform-ServerDaily-Report
#------------------------------------------------------------------------------
$strReportName = 'AU-NBN Daily Exchange Transport Report - Date'
$strCSVFileName = "$($OutputPath)\$($strReportName) $($YesterdaysDate).csv"
out-log "Performing query for $strReportName report"
$blnOutputFileStarted = $falses
$intNumOfDays = ($dteEndDate - $dteStartDate).Days
For ($i=0; $i -le ($intNumOfDays - 1); $i++) {
$dteReportDate = $dteStartDate.AddDays($i)
$strReportDate = $dteReportDate.ToString('yyyyMMdd HH:mm:ss')
$strQuery = "exec [Exchange2010].[Transport-Platform-ServerHourly-Report] @ReportDateTime='$strReportDate'"
$objDataset = Execute-SQLQuery $strQuery $strDataWarehouseServer $strDataWarehouseDB
# $arrDataTable += select "TimeStamp,Date,Time,ClientName, ServerName, TotalMessagesDeliveredtoLocal,TotalMessagesDeliveredToIntraOrg,TotalMessagesDeliveredToForeignGateway,TotalMessagesDeliveredToForeignAgent,TotalMessagesDeliveredToForeignSmtp,TotalMessagesRoutingFailedForForeign,TotalMessagesAgentFailedForForeign,DSNMessages,EndToEndMessagesInSlaCount,EndToEndMessagesTotalCount,LocalMessagesInSlaCount,LocalMessagesTotalCount`n"
# $arrDataTable += Select TimeStamp,Date,Time,ClientName, ServerName, TotalMessagesDeliveredtoLocal,TotalMessagesDeliveredToIntraOrg,TotalMessagesDeliveredToForeignGateway,TotalMessagesDeliveredToForeignAgent,TotalMessagesDeliveredToForeignSmtp,TotalMessagesRoutingFailedForForeign,TotalMessagesAgentFailedForForeign,DSNMessages,EndToEndMessagesInSlaCount,EndToEndMessagesTotalCount,LocalMessagesInSlaCount,LocalMessagesTotalCount `
# $arrDataTable += select "Time Stamp","Date1","Time1","Client Name", "Server Name", "Total Messages Delivered to Local","Total Messages Delivered to IntraOrg","Total Messages Delivered to ForeignGateway","Total Messages Delivered to ForeignAgent","TotalMessagesDeliveredTo ForeignSmtp","Total MessagesRoutingFailedForForeign","Total MessagesAgentFailedForForeign","DSN Messages","End to End Messages InSlaCount","End to End Messages TotalCount","Local Messages InSlaCount","Local MessagesTotalCount", "`n"
# Write-Host $strQuery -f Green
If ($objDataset -ne $null) {
$objDataTable = $objDataset.Tables[0]
$arrDataTable = $objDataTable | Select @{Name='Timestamp'; Expression={ $_.Hour.ToString('dd-MMM-yyyy HH:mm') } }, `
@{Name='Date'; Expression={ $_.Hour.ToString('dd-MMM-yyyy') } }, `
@{Name='Time'; Expression={ $_.Hour.ToString('HH:mm') } }, `
@{Name='ClientName'; Expression={ $_.ClientName.Trim().Trim('.') } }, `
@{Name='ServerName'; Expression={ $_.ServerName.Trim().Trim('.') } }, `
@{Name='TotalMessagesDeliveredToLocal';Expression={$_.TotalMessagesDeliveredToLocal}}, `
@{Name='TotalMessagesDeliveredToIntraOrg';Expression={$_.TotalMessagesDeliveredToIntraOrg}}, `
@{Name='TotalMessagesDeliveredToForeignGateway';Expression={$_.TotalMessagesDeliveredToForeignGateway}}, `
@{Name='TotalMessagesDeliveredToForeignAgent';Expression={$_.TotalMessagesDeliveredToForeignAgent}}, `
@{Name='TotalMessagesDeliveredToForeignSmtp';Expression={$_.TotalMessagesDeliveredToForeignSmtp}}, `
@{Name='TotalMessagesRoutingFailedForForeign';Expression={$_.TotalMessagesRoutingFailedForForeign}}, `
@{Name='TotalMessagesAgentFailedForForeign';Expression={$_.TotalMessagesAgentFailedForForeign}}, `
@{Name='DSNMessages';Expression={$_.DSNMessages}}, `
@{Name='EndToEndMessagesInSlaCount';Expression={$_.EndToEndMessagesInSlaCount}}, `
@{Name='EndToEndMessagesTotalCount';Expression={$_.EndToEndMessagesTotalCount}}, `
@{Name='LocalMessagesInSlaCount';Expression={$_.LocalMessagesInSlaCount}}, `
@{Name='LocalMessagesTotalCount';Expression={$_.LocalMessagesTotalCount}}
#$strDataTableCSV = $arrDataTable | ConvertTo-CSV -NoTypeInformation
$strDataTableCSV = $arrDataTable
$strDataTableCSV | Export-CSV $strCSVFileName -NoType
}
If (Test-Path $strCSVFileName) {
$arrFilesForCompression += ('"' + $strCSVFileName + '"')
}}
}
#--- Email the CSV file.
"Emailing CSV file..."
$MessageSubject = "AU-NBN Daily Exchange Transport Report - Date $YesterdaysDate"
$MessageBodyType = 'Text'
$MessageBody = 'Data files attached'
$MessageAttachment = $strCSVFileName
Send-Email
Out-Log ("Email sent. Exiting...")
#------------------------------------------------------------------------------
# If logging is configured, then output any script errors and close off the log
#------------------------------------------------------------------------------
If ($LogFile -ne $null) {
Log-Errors
out-log "--- Script Completed ---"
}
Here is the Exchange 2010 Stored Procedure:
USE [OperationsManagerDW]
GO
/****** Object: StoredProcedure [Exchange2010].[Transport-Platform-ServerStatistics-Report] Script Date: 09/07/2020 21:26:56 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [Exchange2010].[Transport-Platform-ServerStatistics-Report]
@IntervalStartDateTime datetime = NULL,
@IntervalEndDateTime datetime = NULL,
@EndToEndSlaTarget int = 90,
@LocalSlaTarget int = 30
AS
BEGIN
SET NOCOUNT ON
DECLARE
@ErrorInd bit
,@ErrorMessage nvarchar(4000)
,@ErrorNumber int
,@ErrorSeverity int
,@ErrorState int
,@ErrorLine int
,@ErrorProcedure nvarchar(256)
,@ErrorMessageText nvarchar(4000)
,@DebugLevel tinyint
SET @ErrorInd = 0
BEGIN TRY
IF @IntervalStartDateTime IS NULL
BEGIN
SET @IntervalStartDateTime = DATEADD(year, - 100, GETUTCDATE())
END
IF @IntervalEndDateTime IS NULL
BEGIN
SET @IntervalEndDateTime = GETUTCDATE()
END
IF @EndToEndSlaTarget IS NULL
BEGIN
SET @EndToEndSlaTarget = 90
END
IF @LocalSlaTarget IS NULL
BEGIN
SET @LocalSlaTarget = 30
END
IF OBJECT_ID('tempdb..#ServerStatisticsTemp') IS NOT NULL
DROP TABLE #ServerStatisticsTemp
CREATE TABLE #ServerStatisticsTemp
(
[DateTime] DateTime,
[ClientName] nvarchar(256),
[ServerName] nvarchar(256),
[TotalMessagesDeliveredToLocal] BigInt,
[TotalMessagesDeliveredToIntraOrg] BigInt,
[TotalMessagesDeliveredToForeignGateway] BigInt,
[TotalMessagesDeliveredToForeignAgent] BigInt,
[TotalMessagesDeliveredToForeignSmtp] BigInt,
[TotalMessagesRoutingFailedForForeign] BigInt,
[TotalMessagesAgentFailedForForeign] BigInt,
[DSNMessages] BigInt,
[EndToEndMessagesInSlaCount] BigInt,
[EndToEndMessagesTotalCount] BigInt,
[LocalMessagesInSlaCount] BigInt,
[LocalMessagesTotalCount] BigInt
)
INSERT INTO #ServerStatisticsTemp
SELECT
CONVERT(DateTime,SUBSTRING(CONVERT(nvarchar(26),[DateTime],120),1,10),120)
,
CASE
WHEN CHARINDEX('.',[ClientHostName]) != 0 THEN SUBSTRING([ClientHostName], 1, CHARINDEX('.',[ClientHostName]))
ELSE [ClientHostName]
END AS [ClientName]
,
CASE
WHEN CHARINDEX('.',[ServerHostName]) != 0 THEN SUBSTRING([ServerHostName], 1, CHARINDEX('.',[ServerHostName]))
ELSE [ServerHostName]
END AS [ServerName]
,SUM(DeliverStoreDriverMessages) AS [TotalMessagesDeliveredToLocal]
,SUM(DeliverSmtpIntraOrgMessages) AS [TotalMessagesDeliveredToIntraOrg]
,SUM(DeliverGatewayMessages)AS [TotalMessagesDeliveredToForeignGateway]
,SUM(DeliverAgentMessages) AS [TotalMessagesDeliveredToForeignAgent]
,SUM(DeliverSmtpForeignMessages) AS [TotalMessagesDeliveredToForeignSmtp]
,SUM(RoutingFailForeignMessages) AS [TotalMessagesRoutingFailedForForeign]
,SUM(AgentFailForeignMessages) AS [TotalMessagesAgentFailedForForeign]
,SUM(DSNMessages) AS [DSNMessages]
,
SUM(
CASE
WHEN [EndToEndLatencyPercentileCount] = 0 THEN 1
WHEN [EndToEndLatencyPercentile100] < @EndToEndSlaTarget THEN 1
WHEN [EndToEndLatencyPercentile99] < @EndToEndSlaTarget THEN .99
WHEN [EndToEndLatencyPercentile95] < @EndToEndSlaTarget THEN .95
WHEN [EndToEndLatencyPercentile90] < @EndToEndSlaTarget THEN .9
WHEN [EndToEndLatencyPercentile80] < @EndToEndSlaTarget THEN .8
WHEN [EndToEndLatencyPercentile70] < @EndToEndSlaTarget THEN .7
WHEN [EndToEndLatencyPercentile60] < @EndToEndSlaTarget THEN .6
WHEN [EndToEndLatencyPercentile50] < @EndToEndSlaTarget THEN .5
WHEN [EndToEndLatencyPercentile25] < @EndToEndSlaTarget THEN .25
ELSE 0
END
* [EndToEndLatencyPercentileCount]
) AS [EndToEndMessagesInSlaCount]
,SUM([EndToEndLatencyPercentileCount]) AS [EndToEndMessagesTotalCount]
,SUM(
CASE
WHEN [LocalLatencyPercentileCount] = 0 THEN 1
WHEN [LocalLatencyPercentile100] < @LocalSlaTarget THEN 1
WHEN [LocalLatencyPercentile99] < @LocalSlaTarget THEN .99
WHEN [LocalLatencyPercentile95] < @LocalSlaTarget THEN .95
WHEN [LocalLatencyPercentile90] < @LocalSlaTarget THEN .9
WHEN [LocalLatencyPercentile80] < @LocalSlaTarget THEN .8
WHEN [LocalLatencyPercentile70] < @LocalSlaTarget THEN .7
WHEN [LocalLatencyPercentile60] < @LocalSlaTarget THEN .6
WHEN [LocalLatencyPercentile50] < @LocalSlaTarget THEN .5
WHEN [LocalLatencyPercentile25] < @LocalSlaTarget THEN .25
ELSE 0
END
* [LocalLatencyPercentileCount]
) AS [LocalMessagesInSlaCount]
,SUM([LocalLatencyPercentileCount]) AS [LocalMessagesTotalCount]
FROM [Exchange2010].[vServerStatisticsDailyV14]
WHERE ([DateTime] >= @IntervalStartDateTime) AND [DateTime] <= @IntervalEndDateTime
GROUP BY SUBSTRING(CONVERT(nvarchar(26),[DateTime],120),1,10),[ClientHostName],[ServerHostName]
ORDER BY SUBSTRING(CONVERT(nvarchar(26),[DateTime],120),1,10)
SELECT *
FROM #ServerStatisticsTemp
WHERE ([TotalMessagesDeliveredToLocal] > 0
OR [TotalMessagesDeliveredToIntraOrg] > 0
OR [TotalMessagesDeliveredToForeignGateway] > 0
OR [TotalMessagesDeliveredToForeignAgent] > 0
OR [TotalMessagesDeliveredToForeignSmtp] > 0)
END TRY
BEGIN CATCH
SELECT
@ErrorNumber = ERROR_NUMBER()
,@ErrorSeverity = ERROR_SEVERITY()
,@ErrorState = ERROR_STATE()
,@ErrorLine = ERROR_LINE()
,@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-')
,@ErrorMessageText = ERROR_MESSAGE()
SET @ErrorInd = 1
END CATCH
IF (@ErrorInd = 1)
BEGIN
DECLARE @AdjustedErrorSeverity int
SET @AdjustedErrorSeverity = CASE
WHEN @ErrorSeverity > 18 THEN 18
ELSE @ErrorSeverity
END
RAISERROR (777971002, @AdjustedErrorSeverity, 1
,@ErrorNumber
,@ErrorSeverity
,@ErrorState
,@ErrorProcedure
,@ErrorLine
,@ErrorMessageText
)
END
END
GO