Exchange 2007 / Exchange 2010: Daily or Hourly Messagestatistc and Warning


Messagetracking Statistics and Warning



#################################

Update: 28.02.14

A new Version is available. V1.2

I did some performance improvement by changing from Group-Object to a foreach Loop.
The Script also checks now if a DAG is present and runs only on the PrimaryActiveManager.
I also did some minor bugfixing (Debug Switch,...)

#################################


I wanted to know how many mails are going thru my Exchange 2010 System.
So I searched for a powershell script and found a very interesting article from Roland Ehle at http://www.roland-ehle.de/archives/997. He wrote a little script to get all the worth knowing e-mail counters like incomming and outgoing SMTP Mails and also which size they have summarized. To get all this infomation the get-messagetracking command from Exchange will be called.
I got the HTML formatting from Sean Duffy at http://www.simple-talk.com/sysadmin/powershell/building-a-daily-systems-report-email-with-powershell/.

But i'd not wanted only an informational summary. I extendet the script with parameters to set a treshold and collect all information on a hourly or daily basis. 
Now I can set a treshold for all incomming and outgoing mails. If this treshold is exeeded I will get a warning email. Top # email sender and receiver are also in this report.

This two report modes are available:
  • Daily: 
    • counts all incomming, outgoing and internal E-Mails form all Transportservers
    • Report timeframe from 00h:00min to 23h:59min one day before
    • sends an E-mail if E-Mail parameters are set
    • logs alle information in to a logfile on the server
    • generates a piecart for incomming, outgoing and internal E-mails
    • accepts also number of Top users
  • Hourly:
    • counts all incomming, outgoing and internal E-Mails form all Transportservers
    • Report timeframe from 00min:00sec to 59min:59sec one hour before
    • sends a warning E-Mail if E-Mail parameters are set and treshold is exeeded
    • logs alle information in a logfile on the server
    • generates a piecart for incomming, outgoing and internal mails
    • accepts also number of Top users
I have created a schedule task to repeat treshhold check every hour. From now, I am informed if there is a E-Mail problem in my company.

For the piechart to be created you have to install the MS-Chart dot.net extension on the Exchange Server or where this script is running.

Will work with Exchange 2007 and 2010 (Tested on Exchange 2010 with DAG)

Here is a sample command for a daily report which will be sent via E-Mail:

.\Get-MessageStatistic.ps1 -Daily -MailServer mail.domain.com -MailRecipient user@domain.com -TargetFolder c:\temp

Here is an example html daily report:


And an example of a warning report:

And here comes the script:

<# 
.SYNOPSIS
    Collects statistic information from Messagetracking Log.

.DESCRIPTION
    This script gets Messagetracking statistic information from all HubTransport servers.
    It gernerates a HTML formated E-Mail with a Chart of incomming, outgoing and internal E-Mails.
    You can create a Sched Task to get hourly alarms or daily Reports per E-Mail.
    This script generates also an Report File to generate a Chart off all incomming and outgoing E-Mails.

.NOTES 
    Version             : 1.2.2702.1 public
    WishList            : adding "DELIVERD" statistic (all internal Mails)
    Rights Required     : Local admin on Exchange Server
    Sched Task Req'd    : Yes
    Exchange Version    : Exchange 2010 tested, Exchange 2007 probably
    Author              : Manfred Peer, Exchange System Engineer
    Email/Blog          : maeffy@gmail.com http://peerfect.blogspot.com
    Disclaimee          : You running this script means you won't blame me if this breaks your stuff.
                        : Use at your own risk!
    Info got from       : Roland Ehle http://www.roland-ehle.de/archives/997
                        : Sean Duffy http://www.simple-talk.com/sysadmin/powershell/building-a-daily-systems-report-email-with-powershell/
    Fixes               : Fixed to run in a Singel Exchange Server Environment (No DAG present)
                        : Changed the sorting Methode; performance has improved dramatically!! (Grouping big hash data is blasting the Group-Object Cmdlet)

.LINK 
    http://peerfect.blogspot.com

.PARAMETER Daily
    Specifies the Report mode. Report will start from -1:00:00:00 (dd:hh:mm:ss) to -1:23:59:59 (dd:hh:mm:ss)

.PARAMETER Hourly
    Specifies the Report mode. Report will start from -1:00:00 (hh:mm:ss) to -1:59:59 (hh:mm:ss)

.PARAMETER Treshold
    Specifies a treshold value. After exeeding the treshold, you will get a warning report.
    You also have to set -MailServer and -MailRecipient value. Treshold will work only in Hourly mode

.PARAMETER MailServer
    Specifies the E-Mail Server. You can use IP (192.168.1.11) or FQDN (server.domain.com)

.PARAMETER MailRecipient
    Specifies the E-Mail Recipient. Example: administrator@domain.com

.PARAMETER TargetFolder
    Specifies the path of the Message Satistic File.
    For Daily: file name is exchange_MessageStatistic_daily.csv
    For Hourly: the filename is exchange_MessageStatistic_hourly.csv

.PARAMETER Top
    Specifies the counter of Top Users.

.EXAMPLE
    .\Get-MessageStatistic.ps1 -Daily -TargetFolder c:\temp

    Statistic Output goes to statistik file only. No E-Mail
.EXAMPLE
    .\Get-MessageStatistic.ps1 -Daily -MailServer mail.domain.com -MailRecipient user@domain.com -TargetFolder c:\temp

    Statistic Output goes to statistik file and you will get an E-Mail.
.EXAMPLE
    .\Get-MessageStatistic.ps1 -Hourly -TargetFolder c:\temp

    Statistic Output goes to statistik file only. No E-Mail
.EXAMPLE
    .\Get-MessageStatistic.ps1 -Hourly -Treshold 1000 -MailServer mail.domain.com -MailRecipient user@domain.com -TargetFolder c:\temp -Top 5

    Statistic Output goes to statistik file and you will get an E-Mail. If treshold is not exeeded, you will not get an E-Mail. Top 5 Users are displayed.
.INPUTS
    None. You cannot pipe objects to this script.
#>
#Requires -Version 2.0
param(
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)][ValidateNotNullOrEmpty()]
   [switch] $Daily = $false,
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)][ValidateNotNullOrEmpty()]
   [switch] $Hourly = $false,
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)][ValidateNotNullOrEmpty()]
   [int] $Treshold = 2100,
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)][ValidateNotNullOrEmpty()]
   [string] $MailServer,
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)][ValidateNotNullOrEmpty()]
   [string] $MailRecipient,
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)][ValidateNotNullOrEmpty()]
   [int] $Top = 10,
   [parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$true)][ValidateScript({Test-Path $_})][ValidateNotNullOrEmpty()]
   [string] $TargetFolder = "c:\temp"
)

set-psdebug -strict
if ($PSCmdlet.MyInvocation.BoundParameters["Debug"].IsPresent)
{
   $MyDebug = $true
   $DebugPreference = "Continue"
}
else {$MyDebug = $false}
####################################################
# Checking if DAG present or not

if (Get-Command Get-DatabaseAvailabilityGroup -errorAction SilentlyContinue)
{
   # If current Server is not the PrimaryActiveManager, quit..
   if ((Get-DatabaseAvailabilityGroup -Status).PrimaryActiveManager -ne $env:Computername)
   {
      if ($MyDebug) {write-host "Not PrimaryActiveManager"}
      exit 100
   }
  
}
else {if ($MyDebug){write-host "No DAG present"}}

if ($Daily -and $Hourly)
{
 write-error "Wrong option selected! Choose `"Daily`" or `"Hourly`""
 exit 100
}

if ($Daily)
{
   # Get the start date for the tracking log search
 $Start = (Get-Date -Hour 00 -Minute 00 -Second 00).AddDays(-1)
 # Get the end date for the tracking log search
 $End = (Get-Date  -Hour 23 -Minute 59 -Second 59).AddDays(-1)
 $TimeStart = $Start.ToShortTimeString()
 $TimeEnd = $End.ToShortTimeString()
 $Date = $Start.ToShortDateString()
 $Subjet_Text = “MessageStatistic daily report for $Date”
}
elseif ($Hourly)
{
 # Get the start date for the tracking log search
 $Start = (Get-Date -Minute 00 -Second 00).AddHours(-1)
 # Get the end date for the tracking log search
 $End = (Get-Date -Minute 59 -Second 59).AddHours(-1)
 $TimeStart = $Start.ToShortTimeString()
 $TimeEnd = $End.ToShortTimeString()
 $Date = $Start.ToShortDateString()
 $Subjet_Text = “MessageStatistic hourly report for $TimeStart”
}
else
{
 write-error "No option selected! Choose `"Daily`" or `"Hourly`""
 exit 99
}

$Hubs = Get-TransportServer
if ($MyDebug) {write-host "Start time: $Start"}
if ($MyDebug) {write-host "End Time:   $End"}
if ($MyDebug) {write-host "Mode:       Daily: $Daily Hourly: $Hourly"}

#exit
$Script:Receive = $Hubs | Get-MessageTrackingLog -Start $Start -End $End -EventID "RECEIVE" -ResultSize Unlimited | select Recipients,TotalBytes
$Script:Send = $Hubs | Get-MessageTrackingLog -Start $Start -End $End -EventID "SEND" -ResultSize Unlimited | select Sender,RecipientCount,TotalBytes,Recipients
$Script:Internal = $Hubs | Get-MessageTrackingLog -Start $Start -End $End -EventID "DELIVER" -ResultSize Unlimited | select Recipients,RecipientCount,TotalBytes
$Internal += $Hubs | get-messagetrackinglog -Start $Start -End $End -EventID "SUBMIT" -ResultSize Unlimited | select Sender,Recipients,RecipientCount,TotalBytes
$Script:Mreceive = $Receive | Measure-Object TotalBytes -maximum -minimum -average -sum
$Script:Msend = $Send | Measure-Object TotalBytes -maximum -minimum -average -sum

$Script:Quantity = $Mreceive.count + $Msend.count
if ($MyDebug) {write-host "Mail Counter Receive: $($Mreceive.count)"}
if ($MyDebug) {write-host "Mail Counter Send: $($Msend.count)"}
$Script:Volume = ($Mreceive.sum + $Msend.sum) / (1024 * 1024)

$Volume = "{0:N2}" -f $Volume + " MB"

$Script:Msendmb = $Msend.sum / (1024 * 1024)
$Script:Vsend = "{0:N2}" -f $Msendmb + " MB"
$Script:Bigsend = $Msend.maximum / (1024 * 1024)
$Script:Avsend = $Msend.average / 1024

$Script:Bigsendmb = "{0:N2}" -f $Bigsend + " MB"
$Script:Avsendkb = "{0:N2}" -f $Avsend + " KB"

$Script:Mreceivemb = $Mreceive.sum / (1024 * 1024)
$Script:Vreceive = "{0:N2}" -f $Mreceivemb + " MB"
$Script:Bigreceive = $Mreceive.maximum / (1024 * 1024)
$Script:Avreceive = $Mreceive.average / 1024

$Script:Bigreceivemb = "{0:N2}" -f $Bigreceive + " MB"
$Script:Avreceivekb = "{0:N2}" -f $Avreceive + " KB"

####################################################
# Preparing Sender
if ($MyDebug) {write-host "trying to sort Senders"}
$Measure_senders = Measure-Command {$senders = $Send | Group-Object Sender | Sort-Object Count -Descending | Select-Object -first $Top}
if ($MyDebug) {write-host "sorting Senders done. Running for $Measure_Senders"}

$Script:TopSender_file = ""
$Script:TopSender = "`n`n"

foreach ($SenderEntry in $senders)
{
 $TopSender_file += $SenderEntry.Name + "," + $SenderEntry.Count + ","
 $TopSender += "<tr>"
 $TopSender += "<td>$($SenderEntry.Name)</td>"
 $TopSender += "<td>$($SenderEntry.Count)</td>"
 $TopSender += "</tr>"
}

if ($MyDebug) {write-host "trying to sort Receivers"}
####################################################
# Preparing receiver
$Script:Userarray = @()
$Measure_Receivers = Measure-Command `
{
   $Receive | foreach {$_.Recipients -split(" ") | foreach {$Userarray += $_} | Out-Null}
   $Userarray = $Userarray | group
   $Receivers = $Userarray | Sort-Object Count -Descending | Select-Object -first $Top
}
$Script:TopRec_file = ""
$Script:TopReceiver = ""
$Script:TopRec = "`n`n"
if ($Receivers.count -lt $Top) {$Top = $Receivers.count}
foreach ($User in $Receivers)
{
 $TopRec_file += $($User.Name)+ "," + $($User.Count) + ","
 $TopRec += "<tr>"
 $TopRec += "<td>$($User.Name)</td>"
 $TopRec += "<td>$($User.Count)</td>"
 $TopRec += "</tr>"
}
if ($MyDebug) {write-host "sorting Receivers done. Running for $Measure_Receivers"}

if (($Quantity) -gt $Treshold -and (!($Daily))) {$HeaderTitle = '<h3 style="color:red">Message Counters Exeeded !!</h3>'}
else {$HeaderTitle = "<h3>Message Counters</h3>"}

####################################################
# Assemble the HTML Header and CSS for our Report
$HTMLHeader = @"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html><head><title>Exchange Systems Report from $env:Computername</title>
<style type="text/css">
<!--
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}

    #report { width: 835px; }
    table{
       border-collapse: collapse;
       border: none;
       font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
       color: black;
       margin-bottom: 10px;
}

    table td{
       font-size: 12px;
       padding-left: 0px;
       padding-right: 20px;
       text-align: left;
}

    table th {
       font-size: 12px;
       font-weight: bold;
       padding-left: 0px;
       padding-right: 20px;
       text-align: left;
}

h2{ clear: both; font-size: 130%; }
h3{
       clear: both;
       font-size: 115%;
       margin-left: 20px;
       margin-top: 30px;
}

p{ margin-left: 20px; font-size: 12px; }
table.list{ float: left; }
    table.list td:nth-child(1){
       font-weight: bold;
       border-right: 1px grey solid;
       text-align: right;
}

table.list td:nth-child(2){ padding-left: 7px; }
table tr:nth-child(even) td:nth-child(even){ background: #CCCCCC; }
table tr:nth-child(odd) td:nth-child(odd){ background: #F2F2F2; }
table tr:nth-child(even) td:nth-child(odd){ background: #DDDDDD; }
table tr:nth-child(odd) td:nth-child(even){ background: #E5E5E5; }
div.column { width: 320px; float: left; }
div.first{ padding-right: 20px; border-right: 1px  grey solid; }
div.second{ margin-left: 30px; }
table{ margin-left: 20px; }
-->
</style>
</head>
<body>

"@
# Create HTML Report for the current System being looped through
#CCCCCC
$CurrentSystemHTML = @"
       <hr noshade size=3 width="100%">
       <div id="report">
       <p><h2>MessageStatistic Report</p></h2>
       $HeaderTitle
       <table class="list">
       <tr>
       <td>Date</td>
       <td>$Date</td>
       </tr>
       <tr>
       <td>Time</td>
       <td>$TimeStart - $TimeEnd</td>
       </tr>
       <tr>
       <td>Counter Sent E-Mails</td>
       <td>$($Msend.Count)</td>
       </tr>
       <tr>
       <td>Capacity Sent E-Mails</td>
       <td>$Vsend</td>
       </tr>
       <tr>
       <td>Size biggest E-Mail Out</td>
       <td>$Bigsendmb</td>
       </tr>
       <tr>
       <td>Average Size Out</td>
       <td>$Avsendkb</td>
       </tr>
       <tr>
       <td>Counter Recipient E-Mails</td>
       <td>$($Mreceive.Count)</td>
       </tr>
       <tr>
       <td>Volume Recipient E-Mails</td>
       <td>$Vreceive</td>
       </tr>
       <tr>
       <td>Size biggest E-Mail In</td>
       <td>$Bigreceivemb</td>
       </tr>
       <tr>
       <td>Average Size In</td>
       <td>$Avreceivekb</td>
       </tr>
       <tr>
       <td>Overall Counter</td>
       <td>$Quantity</td>
       </tr>
       <tr>
       <td>Volume Overall</td>
       <td>$Volume</td>
       </tr>
       </table>
      
       <IMG SRC="chart-MessageStatistic.png" ALT="MessageStatistic Chart">
      
       <h3>Top $Top Sender</h3>
       <p>User(s) listed below are Top $Top. User(s) under this threshold will not be listed.</p>
       <table class="normal">$TopSender</table>
       <br></br>
     
       <h3>Top $Top Recipients</h3>
       <p>User(s) listed below are Top $Top. User(s) under this threshold will not be listed.</p>
       <table class="normal">$TopRec</table>
       </div>

"@
# Assemble the closing HTML for our report.
$HTMLEnd = @"
</div>
</body>
</html>
"@
# Assemble the final report from all our HTML section
$HTMLmessage = $HTMLHeader + $CurrentSystemHTML + $HTMLEnd
$HTMLmessage | Out-File ($TargetFolder + "\report.html")
###################################
# Creates a statistic file 
# We need a Header

$out_header = 'Date;Time;Mail_Send.count;Volume_Send;Mail_Receive.count;Volume_Receive;Overall Count;Overall_Volume;Top_Sender;Top_Receiver'
$out = $Date + ";" + $TimeStart + ";" + $Msend.count + ";" + $Vsend + ";" + $Mreceive.count + ";" + $Vreceive + ";" + $Quantity + ";" + $Volume + ";" + $TopSender_file + ";" + $TopRec_file

if ($Daily)
{
   if ((Test-path $TargetFolder\exchange_MessageStatistic_daily.csv) -like "False")
   {
      $out_header | out-file $TargetFolder\exchange_MessageStatistic_daily.csv -encoding default -force
 }
 $out | out-file $TargetFolder\exchange_MessageStatistic_daily.csv -encoding default -append
}
if ($Hourly)
{
 if ((Test-path $TargetFolder\exchange_MessageStatistic_hourly.csv) -like "False")
 {
  $out_header | out-file $TargetFolder\exchange_MessageStatistic_hourly.csv -encoding default -force
 }
 $out | out-file $TargetFolder\exchange_MessageStatistic_hourly.csv -encoding default -append
}

#####################################
Function Create-PieChart()
{
   param([string]$FileName,$PieArgs)
      
   [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
   [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")
  
   #Create our chart object
   $Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
   $Chart.Width = 300
   $Chart.Height = 290
   $Chart.Left = 10
   $Chart.Top = 10
   [string]$ChartText = " "
   #Create a chartarea to draw on and add this to the chart
   $ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
   $Chart.ChartAreas.Add($ChartArea)
   [void]$Chart.Series.Add("Data")
  
   #Add a datapoint for each value specified in the arguments (args)
   foreach ($value in $PieArgs.keys)
   {
      if ($MyDebug) {Write-Host "Now processing chart value: "$value + $($PieArgs.item($value))}
      $datapoint = new-object System.Windows.Forms.DataVisualization.Charting.DataPoint(0, $($PieArgs.item($value)))
      $datapoint.AxisLabel = "$value " + $($PieArgs.item($value))
      $Chart.Series["Data"].Points.Add($datapoint)
      $ChartText += $value + " "
   }
  
   $Chart.Series["Data"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Pie
   $Chart.Series["Data"]["PieLabelStyle"] = "Outside"
   $Chart.Series["Data"]["PieLineColor"] = "Black"
   $Chart.Series["Data"]["PieDrawingStyle"] = "Concave"
   ($Chart.Series["Data"].Points.FindMaxByValue())["Exploded"] = $true
  
   #Set the title of the Chart to the current date and time
   $Title = New-Object System.Windows.Forms.DataVisualization.Charting.Title
   $Chart.Titles.Add($Title)
   $Chart.Titles[0].Text = "Mail Usage Chart ($ChartText)"
  
   #Save the chart to a file
   $Chart.SaveImage($FileName + ".png","png")
}

# Create the chart using our Chart Function
$PieArgs = @{"Incomming" = $Mreceive.count; "Outgoing" = $Msend.count; "Internal" = $Internal.count}
Create-PieChart -FileName ($TargetFolder + "\chart-MessageStatistic") $PieArgs
$Attachments = $TargetFolder + "\chart-MessageStatistic.png"
###################################

if ($MyDebug) {write-host "Treshold:   $Treshold"}
if ($MyDebug) {write-host "Mail Count: $Quantity"}
if ((($MailServer -and $MailRecipient) -and ($Daily)) -or (($MailServer -and $MailRecipient) -and ($Hourly) -and ($Quantity -gt $Treshold)))
{
 if ($MyDebug) {write-host "Send Info: yes"}
 send-mailmessage -from "MessageStatistic@$env:computername" -to $MailRecipient -subject $Subjet_Text -Attachments $Attachments -BodyAsHTML -body $HTMLmessage -priority Normal -smtpServer $MailServer
}
else {if ($MyDebug) {write-host "Send Info:  no"}}

##############################################################################
Have fun!
Maeffy

Comments

  1. I have been working with this script for a few days now. It hangs at
    Start time: 10/08/2013 00:00:00
    End Time: 10/08/2013 23:59:59
    Mode: Daily: True Hourly: False
    Mail Counter Receive: 90902
    Mail Counter Send: 13591
    trying to sort Internals
    sorting Internals done. Running for 00:00:09.7065272
    trying to sort Senders
    sorting Senders done. Running for 00:00:02.2214675
    trying to sort Receivers

    Any suggestions?

    ReplyDelete
    Replies
    1. Hello Tim,

      I did an update on the script. Now its much faster than before...

      Maeffy

      Delete
  2. The report will run but it takes 19 hours...

    ReplyDelete
  3. Same issue as above. How come it takes so long?

    ReplyDelete
  4. This is because of trying to check how many mails an user received. So the routine iterates every line from Transaction Log and tries to find a user and add it to the counter. This is be done for every recipient, which takes the amount of time...

    ReplyDelete
  5. Getting the following error when running the Script...

    Exception calling "FindMaxByValue" with "0" argument(s): "Object reference not set to an instance of an object."
    At C:\Scripts\Message usage\Get-MessageStatistic.ps1:422 char:52
    + ($Chart.Series["Data"].Points.FindMaxByValue <<<< ())["Exploded"] = $true
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "SaveImage" with "2" argument(s): "Object reference not set to an instance of an object."
    At C:\Scripts\Message usage\Get-MessageStatistic.ps1:430 char:24
    + $Chart.SaveImage <<<< ($FileName + ".png","png")
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException


    Also you should update code for Non-Dag environments.

    ReplyDelete
    Replies
    1. Have you installed the Microsoft Chart Control ?

      Delete
  6. Running it on a CAS/HT server, it stops after 10 seconds and produces no output. No errors, no file in -TargetFolder, no messages on the screen.

    What can be wrong?

    ReplyDelete
    Replies
    1. Have you tried running the "debug" switch ?

      Delete
    2. Use the -debug switch to generate debuging information...

      Delete

Post a Comment

Popular posts from this blog

Re-Blog Exchange 2007 Powershell remoting solution

Out of Office Admin Tool