Remote Exchange Monitoring and Reporting using Email (Part 1)

by [Published on 18 Feb. 2016 / Last Updated on 18 Feb. 2016]

In this two-part article series we will develop two scripts to monitor and report on Exchange remotely using email.

If you would like to read the next part in this article series please go to Remote Exchange Monitoring and Reporting using Email (Part 2).

Introduction

We all know how crucial a messaging service is to most organizations. With the exception of maybe telephones, businesses today rely on email and messaging systems more than any other piece of infrastructure. Every Exchange administrator knows the importance of continuously monitoring Exchange, not only to prevent downtime and quickly fix problems after they occur, but also to be aware of the health of the infrastructure and to help identify potential problems and performance degradations before they turn into problems and cause downtime.

Monitoring solutions like Microsoft’s System Center Operations Manager, SolarWinds, Nagios, MailScape, etc., are just some examples of monitoring tools for Exchange. However, some organizations do not provide access to these tool’s consoles or dashboards outside the internal network. So what happens if an administrator is out and about without anything other than his/her phone and needs to check if this user has gone over their quota and cannot send emails, on which server a particular database is mounted, or even if ServerA has just been rebooted?

These type of situations might be rare, but I have personally been there and it would have been extremely useful if I could send an email to my mailbox with a particular PowerShell cmdlet and get the output of that cmdlet back. And this is what this article is about. We will develop two basic scripts that will monitor incoming emails between two users (for security reasons), run the cmdlet(s) present in the email’s subject and reply with the output from that same cmdlet. The first script will use Message Tracking Logs while the second Exchange Web Services (EWS).

Obviously this does not help if Exchange itself is experiencing problems with its Transport services and unable to receive/send emails (queue problems), if the entire network is down, and so on.

Message Tracking Logs

The first script we will develop will search the Message Tracking Logs every X number of minutes to look out for any emails arriving at a particular mailbox named monitoring. If it finds any, it will get the subject of the email, run it in the Exchange Management Shell (EMS) and compose a new email to the original sender with the output of the script/cmdlet.

For security reasons, the script will only run Get-* cmdlets, so no settings can be changed using this process. Obviously this can easily be changed to allow us to make changes to our Exchange environment remotely. However, I am certain the Security policies for most organizations would not allow this...

Additionally, we will only process emails that are sent from a particular sender (nuno@outlook.com in this case) to avoid any rogue users or hackers to gain unauthorized information about our environment.

First, we start by defining the parameters this script will use. We can specify the recipient/monitoring mailbox (in this case monitoring@nunomota.pt) and the allowed sender (nuno@outlook.com):

Param (

[Parameter(Position = 0, Mandatory =$False)]

[String] $Recipient="monitoring@nunomota.pt",

 

 

[Parameter(Position = 1, Mandatory =$False)]

[String] $Sender="nuno@outlook.com"

)

In this example we will be searching the Message Tracking Logs every 15 minutes, so we save the start date of our search in a variable so we can use later:

$strStartFrom= (Get-Date).AddMinutes(-15)

Then we perform our search. We are interested in emails from $Sender delivered to $Recipient since $strStartFrom:

Get-TransportService | Get-MessageTrackingLog -ResultSize Unlimited -Start $strStartFrom -Sender $Sender -Recipients $Recipient -EventID DELIVER

For each email we find, we will call a function named runCmdlet to process that email and send an appropriate reply. The search itself will look like this:

Get-TransportService | Get-MessageTrackingLog -ResultSize Unlimited -Start $strStartFrom -Sender $Sender -Recipients $Recipient -EventID DELIVER | % {

      runCmdlet$_.MessageSubject

}

Now onto the last part, the runCmdlet function. Here we will start by checking if the cmdlet to run is or includes a Set-* cmdlet. If it does not, than we try to run it and capture any errors that it might throw (in case there is a typo for example):

FunctionrunCmdlet ([String] $cmdlet) {

      If ($cmdlet-match"set-") {

            $output="Cmdlet not allowed!"

      } Else {

            Try {

                $output=Invoke-Expression$cmdlet-ErrorActionStop-ErrorVariableErr

            } Catch {

                $output=$Err

            }

      }

If the cmdlet runs successfully, then we start creating our response, which will be an HTML email. First, the HTML header and titles:

       If ($output) {

       $reportBody="<!DOCTYPE html>

                     <HTML>

                     <head>

                     <title>Monitoring Report</title>

 

                     <style>

                     body {

                           font-family:Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;

                           font-size: 10pt;

                           background-color: white;

                           color: #000000;

                     }

 

                     </style></head>

 

                     <BODY>

                     <h2 align=""center"">Monitoring Exchange Report</h2>

                     <h4 align=""center"">$((Get-Date).ToString())</h4>

                     <br>"

Next we save the cmdlets’ output into a file so we can also send it as an attachment. This is done mainly for formatting reasons. Unfortunately I have not yet found an easy way of putting the output of a cmdlet into a nice HTML format (even when using ConvertTo-Html the result is not good for most cmdlets):

$output | Out-FileRemoteMonitoring.txt

For information purposes, we also include in the email body the cmdlet we ran:

$reportBody+="<b>$cmdlet</b><br>"

And then we finally place the cmdlet’s output into the body of our email response. Here we will be putting some new lines, otherwise the output would be a continuous single line:

$reportBody+= [String]::Join("<br>", (Get-ContentRemoteMonitoring.txt))

$reportBody+="</BODY> </HTML>"

The last step is to send the email itself with the body we have been composing:

Send-MailMessage-From$Recipient-To$Sender-Subject"Monitoring Result - $(Get-Date -f ""yyyyMMdd hh:mm"")"-Body$reportBody-BodyAsHTML-SMTPservermail.nunomota.pt-AttachmentsRemoteMonitoring.txt

The final complete script will look like this:

Param (

       [Parameter(Position = 0, Mandatory =$False)]

       [String] $Recipient=monitoring@nunomota.pt,

 

       [Parameter(Position = 1, Mandatory =$False)]

       [String] $Sender=nuno@outlook.com

)

 

FunctionrunCmdlet ([String] $cmdlet) {

       If ($cmdlet-match"set-") {

              $output="Cmdlet not allowed!"

       } Else {

              Try {

                     $output=Invoke-Expression$cmdlet-ErrorActionStop-ErrorVariableErr

              } Catch {

                     Write-Verbose"Error running cmdlet!"

                     $output=$Err

              }

       }

 

       If ($output) {

       Write-Verbose"Composing response"

       $reportBody="<!DOCTYPE html>

                     <HTML>

                     <head>

                     <title>Monitoring Report</title>

 

                     <style>

                     body {

                           font-family:Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;

                           font-size: 10pt;

                           background-color: white;

                           color: #000000;

                     }

 

                     </style></head>

 

                     <BODY>

                     <h2 align=""center"">Monitoring Exchange Report</h2>

                     <h4 align=""center"">$((Get-Date).ToString())</h4>

                     <br>"

 

              $output | Out-FileRemoteMonitoring.txt

              $reportBody+="<b>$cmdlet</b><br>"

              $reportBody+= [String]::Join("<br>", (Get-ContentRemoteMonitoring.txt))

              $reportBody+="</BODY> </HTML>"

 

              Send-MailMessage-From$Recipient-To$Sender-Subject"Monitoring Result - $(Get-Date -f ""yyyyMMdd hh:mm"")"-Body$reportBody-BodyAsHTML-SMTPservermail.nunomota.pt-AttachmentsRemoteMonitoring.txt

              $reportBody=$null

       }

}

 

Write-Verbose"Searching Message Tracking Logs"

$strStartFrom= (Get-Date).AddMinutes(-15)

 

Get-TransportService | Get-MessageTrackingLog -ResultSize Unlimited -Start $strStartFrom -Sender $Sender -Recipients $Recipient -EventID DELIVER | % {

       Write-Verbose"Running $($_.MessageSubject)"

       runCmdlet$_.MessageSubject

}

Testing the Script

It is now time we test our script! First, let us see how it handles errors by running a cmdlet with a typo:

Image
Figure 1

We can see the script runs fine even though the cmdlet throws an error:

Image
Figure 2

But what exactly do we get back? The script returns exactly what we expected: the error returned by the cmdlet:

Image
Figure 3

We also get an attachment with the same output:

Image
Figure 4

What about if we try to run a Set-* cmdlet? Easy, “cmdlet not allowed”!    :)

Image
Figure 5

Ok, let us try a cmdlet that actually returns something useful:

Image
Figure 6

Image
Figure 7

The format of output returned by the script might not be ideal as we can see from the next screenshot:

Image
Figure 8

Image
Figure 9

This is why we also include the output as an attachment as in the file attached the output is exactly formatted as we are used to see it on the EMS:

Image
Figure 10

While we have only ran Exchange cmdlets, the possibilities are huge here. We can tell it to run non-Exchange cmdlets, or even trigger other scripts by sending something like this:

Image
Figure 11

Conclusion

This basic script provides a method of gathering information about our Exchange infrastructure in situations where we would normally not be able to. However, using Message Tracking Logs we are a bit limited as we cannot look into the message body or attachments, only its subject. In the next and final part of this article series, we will develop a similar script but that uses EWS instead, further expanding the script’s capabilities.

If you would like to read the next part in this article series please go to Remote Exchange Monitoring and Reporting using Email (Part 2).

See Also


The Author — Nuno Mota

Nuno Mota avatar

Nuno is an Exchange MVP working as a Senior Microsoft Messaging Consultant for a UK IT Services Provider in London. He specializes in Exchange, Lync, Active Directory and PowerShell.

Advertisement

Featured Links