Use PowerShell to Track Email Messages in Exchange Server

Use PowerShell to Track Email Messages in Exchange Server

  • Comments 2
  • Likes

Summary: Learn how to use Windows PowerShell to evaluate email messages that are sent and received on Microsoft Exchange Server.

Hey, Scripting Guy! Question

Hey, Scripting Guy! I need to be able to create a report that tells my boss information about email messages that are sent and received by our Exchange Server. Can I use Windows PowerShell to do this?

—CR

Hey, Scripting Guy! Answer Hello CR,

Microsoft Scripting Guy, Ed Wilson, here. Our guest blogger today is Rob Campbell. Rob recently was our guest in February, and he is back again today to tell us the answer to your question. You can read Rob’s previous post and his bio in this Hey, Scripting Guy! blog.

The descriptions of the eventids and the sources being used were taken from the online documentation on the message tracking log fields, which can be found in the Exchange Server TechCenter Library.

For the purposes of this exercise, we are will determine how much email is going in and out of our mail stores. This type of information is useful in capacity planning. The two types of events we are going to use are the Receive and Deliver events that have a Source field of STOREDRIVER.

The description of the Deliver events is pretty straightforward, and is shown here.

A message was delivered to a mailbox.

On the other hand, Receive events are a little more complicated. Here is the description field from a receive event.

A message was received and committed to the database. The RECEIVE event can be SMTP receive (Source: SMTP) or mail submitted by STOREDRIVER (Source: STOREDRIVER).

SMTP RECEIVE can be from any source that submits a message by using SMTP. For example, it can be a Hub Transport server role, an Edge Transport server role, a non-Microsoft message transfer agent (MTA), or a POP/IMAP client.

The Edge Transport process logs the STOREDRIVER RECEIVE event. This event directly corresponds to a STOREDRIVER SUBMIT event. The Mail Submission process logs the STOREDRIVER SUBMIT. These events can occur on the same server if both roles are installed locally, or they will be on different servers if the roles are split up due to load considerations and performance reasons.

Note   The process that handles the messages on Hub Transport and Edge Transport servers is referred to in the documentation as the Edge Transport process. It is essentially the same process running on both. It was first written for and used on Edge Transport servers; so on the Hub Transport server, the process inherited the name “Edge Transport process.”

Here are a set of selected properties from the events types from which the script captures information. This set of events shows the events that are involved in the creation of a single email message, its submission and delivery, and the delivery of a subsequent reply.

Note  There are many more fields in the record. Including them all here would make the blog extremely long and rather tedious. However, there is much more information to be had from them, and I urge you to investigate a few of the many thousands you probably have in your very own tracking logs to find the ones you want to focus on in your environment.

Following, is a STOREDRIVER RECEIVE event. A user whose mailbox is on server EXCH-MBX1.MYDOMAIN.COM generated this event. The user created and sent an email. The message has three recipients. One is a single internal recipient, one is a distribution group, and one is an external recipient.

Source : STOREDRIVER

EventId : RECEIVE

MessageId : <68ABA2FDBD44284790D6478BD77800CC1B6FDC7D@EXCH-MBX1.MYDOMAIN.COM>

Recipients : {my_dl@mydomain.com, spooty@mydomain2.com, pinkie@brain.com}

TotalBytes : 12433

RecipientCount : 3

Sender : stewie@mydomain3.com

In the following event, we have an EXPAND event message. You get one of these events when the server expands a distribution list (DL) into its constituent member addresses. The RelatedRecipientAddress is the DL that was expanded, and the Recipients are the members that it expanded to. We capture the addresses of the DLs from these. With this information we can we can report on which DLs are used and which ones are not utilized. The text of the EXPAND event message is shown here.

Source : ROUTING

EventId : EXPAND

MessageId : <68ABA2FDBD44284790D6478BD77800CC1B6FDC7D@EXCH-MBX1.MYDOMAIN.COM>

Recipients : {yakko@mydomain2.com, wakko@mydomain2.com, dot@mydomain2.com}

RelatedRecipientAddress : my_dl@mydomain.com

In the following message, we have our first DELIVER event message. This particular message is for the single, non-DL recipient. The DL to expand is split off, and the transport process will deliver any emails that have been resolved to their primary address and are ready for delivery. This happens even for strictly internal emails that happen to be all on the same mailbox server. One RECEIVE event can result in multiple DELIVER events.

Source : STOREDRIVER

EventId : DELIVER

MessageId : <68ABA2FDBD44284790D6478BD77800CC1B6FDC7D@EXCH-MBX1.MYDOMAIN.COM>

Recipients : {spooty@mydomain2.com }

TotalBytes : 13236

RecipientCount : 1

Sender : stewie@mydomain3.com

Now that the rest of the recipients have been resolved, the messages are ready to be delivered to the mailboxes. The DELIVER message from the STOREDRIVER source is shown here.

Source : STOREDRIVER

EventId : DELIVER

MessageId : <68ABA2FDBD44284790D6478BD77800CC1B6FDC7D@EXCH-MBX1.MYDOMAIN.COM>

Recipients : {{yakko@mydomain2.com, wakko@mydomain2.com, dot@mydomain2.com}

TotalBytes : 13236

RecipientCount : 3

Sender : stewie@mydomain3.com

A bit later, we get a reply from our external recipient. Note here that this message has a different message ID than the rest of the messages. It also has a source name of a mail server in a foreign domain. This can make things a bit confusing at first.

Source : STOREDRIVER

EventId : DELIVER

MessageId : <AANLkTimqe=wdGo4LQHQYWOxZqRfu2BHQAwz2kegEfRLb@mail.brain.com>

Recipients : {stewie@mydomain3.com }

TotalBytes : 7052

RecipientCount : 1

Sender : pinkie@brain.com

An eventid of DELIVER with a source of STOREDRIVER is a delivery of email to a mailbox server message.

The sender can be internal or external. Because it is being delivered to a mailbox server, we know all the recipients are internal. All of the received message stats can be extracted from the Recipients field of these events. DL expansion has already been done, so we can also capture the total number of internal emails and bytes for the senders here.

For sender processing, we start by determining if this is an internal email by checking the domain of the sender and the source of the messageid. If the sender is in one of our accepted domains, and the messageid source matches one of the mailbox servers, we consider it internal. We add the number of recipients to the total internal emails sent by the sender, and we add the number of recipients times the size of the email to his total internal bytes sent.

If it does not pass the tests for being an internal email, we consider it to be external, and we do not use it when accumulating sender statistics. You could add additional counters and logic here, and accumulate statistics on emails from external senders, by sender address.

In either case, we are going to add recipients to our address list, and increment the total number of internal messages received for each recipient, and then add the size of the email to their total internal bytes received. This portion of the Windows PowerShell script is shown here.

if ($_.eventid -eq "DELIVER" -and $_.source -eq "STOREDRIVER"){

      

              if ($_.messageid -match "^.+@.+\..+\..+$" `

                 -and $mbx_servers -contains $_.messageid.split("@")[1].trim(">")`

                 -and $accepted_domains -contains $_.sender.split("@")[1]) {

                    

                     $total_msgsent[$_.sender] += $_.recipientcount

                     $total_bytessent[$_.sender] += ($_.recipientcount *
                     $_.totalbytes)

                     $total_msgsent_exch[$_.sender] += $_.recipientcount

                     $total_bytessent_exch[$_.sender] += ($_.totalbytes *
                     $_.recipientcount)

             

                     foreach ($rcpt in $_.recipients){

                     $exch_addrs[$rcpt] ++

                     $msgrec[$rcpt] ++

                     $bytesrec[$rcpt] += $_.totalbytes

                     $msgrec_exch[$rcpt] ++

                     $bytesrec_exch[$rcpt] += $_.totalbytes

                     }

              }

             

              else {

                           foreach ($rcpt in $_.recipients){

                                  $msgrec[$rcpt] ++

                                  $bytesrec[$rcpt] += $_.totalbytes

                                  $msgrec_smtpext[$rcpt] ++

                                  $bytesrec_smtpext[$rcpt] += $_.totalbytes

                           }     

                     }

                          

       }

An eventid of RECEIVE with a source of STOREDRIVER represents a message that is being submitted by a mailbox server for delivery.

The sender will be an internal user because it came from a mailbox server. The recipients may be internal, external, or both. The message might also be addressed to distribution lists. That means that we do not really know how many final recipients there will be or who the actual recipients are, so we will not be collecting any recipient statistics from these events.

The events with an eventid of RECEIVE and a source of STOREDRIVER represent a user who is composing and sending a unique email, so we will gather the counts of unique emails sent internally and externally from here. By comparing the domain part of the recipient's address to the list of accepted domains, the script will determine whether to increment the internal counters, external counters, or both counters for unique messages and bytes sent.

We will also accumulate the total external messages and bytes sent in this portion of the script. Because addresses are external, they will not show up in the DELIVER or STOREDRIVER events (as previously discussed). This portion of the script is shown here.

if ($_.eventid -eq "RECEIVE" -and $_.source -eq "STOREDRIVER"){

       $exch_addrs[$_.sender] ++

       $unique_msgsent[$_.sender] ++

       $unique_bytessent[$_.sender] += $_.totalbytes

      

       $recpt_domains = $_.recipients |% {$_.split("@")[1]}  

             

              if ($recpt_domains |? {$accepted_domains -contains $_}){

                     $unique_msgsent_exch[$_.sender] ++

                     $unique_bytessent_exch[$_.sender] += $_.totalbytes

                     }

                    

                    

              if ($recpt_domains |? {$accepted_domains -notcontains $_}){

$ext_count = ($recpt_domains |? {$accepted_domains -notcontains $_}).count

                     $unique_msgsent_smtpext[$_.sender] ++

                     $unique_bytessent_smtpext[$_.sender] += $_.totalbytes

                  $total_msgsent[$_.sender] += $ext_count

                     $total_bytessent[$_.sender] += ($ext_count * $_.totalbytes)

                     $total_msgsent_smtpext[$_.sender] += $ext_count

                  $total_bytessent_smtpext[$_.sender] += ($ext_count * $_.totalbytes)

The complete script is posted in the Script Center Script Repository.

CR, that is all there is to using the Exchange message tracking logs to collect performance information. Thank you, Rob, for your words of wisdom, Guest Blogger Week will continue tomorrow when we will have the first of two awesome articles from Windows PowerShell Microsoft MVP, Tome Tanasovski.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Comment reposted from here: http://blogs.technet.com/b/neiljohn/archive/2011/08/09/user-profile-analysis-for-exchange-server-2010.aspx

    Great script. Thank you.

    1
    We are a global environment and only wanted this to run in our region, Americas. to do that I manually populated some fields with our servers.

    #$mbx_servers = Get-ExchangeServer |? {$_.serverrole -match "Mailbox"}|% {$_.fqdn}
    $mbx_servers = "CAPRGWLKWEMBX2.pernod-ricard.group"

    #$hts = get-exchangeserver |? {$_.serverrole -match "hubtransport"} |% {$_.name}
    $hts = "CAPRGWLKWEHUB1.pernod-ricard.group"

    2
    I had trouble running this script due to date formatting. Worked through it.
    When scripts are made that use dates, it should not expect that when it is executed on a system that the date format be in US english. Instead of working the dates even further I modified one line and inserted my dates manually.
    Insert a check before getting to the get-messagetrackinglog command to ensure format is us-english.

    #get-messagetrackinglog -Server $ht -Start "$startdate" -End "$rundate 11:59:59 PM" -resultsize unlimited |
    # The strings were JUST NOT WORKING! Modify the start and end fields below manually.
    #*********************************************************************************************************
    get-messagetrackinglog -Server $ht -Start "03/26/2014" -End "03/27/2014 11:59:59 PM" -resultsize unlimited |

    Error I was getting:
    Cannot convert value "27/03/2014" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
    At D:\root\0841.MessageStats\MessageStats.ps1:12 char:36
    + $outfile_date = ([datetime]$rundate < ).tostring("yyyy_mm_dd")="">
    + CategoryInfo : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException


    Started processing CAPRGWLKWEHUB1.pernod-ricard.group
    Cannot process argument transformation on parameter 'Start'. Cannot convert value "27/03/2014" to type "System.DateTime". Error: "String was not recognized as a valid Date
    Time."
    + CategoryInfo : InvalidData: (:) [Get-MessageTrackingLog], ParameterBindin...mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Get-MessageTrackingLog

  • Mark you can generalize time format conversion trough using .ToString () method:
    $rundate = $(($today).ToString("MM\/dd\/yyyy"))
    $startdate = $((Get-Date).AddDays(-$startoffset).ToString("MM\/dd\/yyyy"))