Introduction
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="
body {
font-family:Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size: 10pt;
background-color: white;
color: #000000;
}
Monitoring Exchange Report
$((Get-Date).ToString())
"
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+="$cmdlet
"
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("
", (Get-ContentRemoteMonitoring.txt))
$reportBody+="
"
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="
body {
font-family:Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
font-size: 10pt;
background-color: white;
color: #000000;
}
Monitoring Exchange Report
$((Get-Date).ToString())
"
$output | Out-FileRemoteMonitoring.txt
$reportBody+="$cmdlet
"
$reportBody+= [String]::Join("
", (Get-ContentRemoteMonitoring.txt))
$reportBody+="
"
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:We can see the script runs fine even though the cmdlet throws an error:
But what exactly do we get back? The script returns exactly what we expected: the error returned by the cmdlet:
We also get an attachment with the same output:
What about if we try to run a Set-* cmdlet? Easy, “cmdlet not allowed”! :)
Ok, let us try a cmdlet that actually returns something useful:
The format of output returned by the script might not be ideal as we can see from the next screenshot:
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:
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: