Powershell script to report on total send/received e-mails in Exchange

 

Update: 2017-08-16 – I have published a new version of this script here

 

This is a simple PowerShell script that pulls all of the send/received e-mails from the Message Tracking log in Exchange 2010 and counts the unique header IDs.

It’s fairly accurate but I don’t think it’s 100% bang on. My testing showed the numbers generated to be off by about 10%.

This script was built for generic mailboxes but should work on individual mailboxes as well.

####################################
# Exchange 2010 send/receive weekly report generator
# Created by: Eric Schewe
# Created on: 2014-10-02
#
####################################
# Summary:
#   This script will count the unique message IDs send and recieved by a mailbox
#   on a weekly basis and e-mail a report to the designated recipients. We are
#   specifically using this script to track mailflow to and from a generic account
#   in our enviroment.
#
#   This script is meant to be run on a Monday so it can gather the previous weeks
#   e-mails (Monday - Sunday).
#
####################################
# Instructions:
#   1. Set $mailbox to the generic account you want stats from
#   2. Set $emailFrom to the e-mail address that the report will be sent from
#   3. Set $emailTo to the people who will receive the report
#   4. Set $smtpServer to a SMTP server you can send e-mail from
#   
####################################

#Powershell Garbage
$nl = [Environment]::NewLine

#Mailbox to gather stats on
$mailbox=""

#Get todays date twice
$startDate=Get-Date
$endDate=Get-Date

#Subtract 1 day from todays date (report ending day) and 7 days from todays date (report starting day)
$startDateFormatted=$startDate.AddDays(-7).ToShortDateString()
$endDateFormatted=$endDate.AddDays(-1).ToShortDateString()


#Who to send the e-mail report to.
#Multiple e-mail addresses should be in this format "<[email protected]>, <[email protected]>"
$emailFrom = "[email protected]"
$emailTo = ""
$subject = "Weekly e-mail report for $mailbox for $startDateFormatted - $endDateFormatted"
$smtpServer = ""


# Sent e-mails
$sendCount = Get-TransportServer | Get-MessageTrackingLog -Start "$startDateFormatted 00:00:00" -End "$endDateFormatted 23:59:59" -Sender $mailbox -resultsize unlimited | select-object -unique MessageId

# Received e-mails - This works but not on generic accounts
$receiveCount = Get-TransportServer | Get-MessageTrackingLog -Start "$startDateFormatted 00:00:00" -End "$endDateFormatted 23:59:59" -Recipients $mailbox -resultsize unlimited | select-object -unique MessageId

$sendCountString = $sendCount.count
$receiveCountString = $receiveCount.count

$body = "Mailbox stats for: $mailbox $nl
Report date range: $startDateFormatted 00:00:00 - $endDateFormatted 23:59:59 $nl
Total e-mails sent: $sendCountString $nl
Total e-mails received: $receiveCountString"

$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)

I have a scheduled task configured on one of our Exchange servers that runs this every Monday morning and provide me the stats on a generic mailbox for the previous week (Monday – Sunday).

25 thoughts on “Powershell script to report on total send/received e-mails in Exchange”

  1. This will give you the accurate number you are looking for. I used your code as a base and adjusted to filter on EventID as well as making it report daily. We were also only concerned with received emails. With some of the changes I also needed to add the exchange snapins prior to executing any other work. Works like a charm, thanks to the starting point.

     

    Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
    $nl = [Environment]::NewLine

    $mailbox=””

    $startDate=Get-Date
    $endDate=Get-Date

    $startDateFormatted=$startDate.AddDays(-1).ToShortDateString()
    $endDateFormatted=$endDate.AddDays(-1).ToShortDateString()

    $emailFrom = “[email protected]
    $emailTo = “”
    $subject = “Daily e-mail report for $mailbox for $startDateFormatted”
    $smtpServer = “smtpServer”

    $receiveCount = Get-TransportServer | Get-MessageTrackingLog -Recipients $mailbox -Start “$startDateFormatted 00:00:01” -End “$EndDateFormatted 23:59:59” -EventID “receive” | Measure-Object

    #$sendCountString = $sendCount.count
    $receiveCountString = $receiveCount.count

    $body = “Mailbox stats for: $mailbox $nl
    Report date: $startDateFormatted $nl
    Total e-mails received: $receiveCountString”

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $smtp.Send($emailFrom, $emailTo, $subject, $body)

    Reply
    • Paul –

      At the risk of asking a dumb question, what is the reason/need to “add the exchange snapins prior to executing any other work”?

      I ask because I am a nube to EMS usage, so don’t know the reason for this. Is there something not in place with a “standard” Exchange (2010 in my case) installation?

      Thanks for not throwing something sharp at the new guy on the block :-)

      Brett

      Reply
      • Adding the lines in means you can run the script from a regular PowerShell prompt instead of having to call the EMS instead.

        EMS is just PowerShell that automatically the Exchange Snapins for you

        Reply
        • Thanks Eric, I was able to get this script tweaked and working wonderfully!

          As a follow-up question, is it possible to EXCLUDE from the daily count any emails that are EITHER from a specific sender’s address OR with maybe a specific term in the subject line?

          Thanks again for your expert help,

          Brett

          Reply
          • I’m actually working on a v2 of this script right now for Exchange 2016 which will more accurately count only outbound e-mail AND allow for an exclusion list. I’ll make a new blog post and link it in this one once I’m done.

            I’m hoping to be done in a week or less.

        • I tried this, but I just don’t seem to be getting it right…

          $receiveCount = Get-TransportServer | Get-MessageTrackingLog -Recipients $mailbox -Start “$startDateFormatted 00:00:01” -End “$EndDateFormatted 23:59:59” -EventID “receive” | Measure-Object | Where-Object {$_.Sender -NotMatch “UnwantedSenderEmailAddress.com”}

          Reply
    • Yes actually. We’re using this script to count how many e-mails people send in 24 hours:


      # Users who sent more than 'x' e-mails yesterday
      #
      # Created by: Eric Schewe
      # Created on: 2015-09-02
      #

      # Set the minimum sent e-mails count you care about. Anyone who sends more than this number will appear in the report
      $minimumEmails = 500

      #How we define yesterday
      $startTime = (get-date -Hour 00 -Minute 00 -Second 00).AddDays(-1)
      $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-1)

      #E-mail stuff
      #Multiple e-mail addresses should be in this format ", "
      $to = ""
      $from = "[email protected]"
      $subject = "Staff who sent over $minimumEmails e-mails for $startTime to $endTime"
      $smtpServer = ""

      $yesterdayStats = Get-TransportServer | Get-MessageTrackingLog -Start $startTime -End $endTime -EventID "SEND" -ResultSize Unlimited | Group-Object -Property Sender | %{ New-Object psobject -Property @{Sender=$_.Name;Recipients=($_.Group | Measure-Object RecipientCount -Sum).Sum}} | Where-Object {$_.Recipients -gt $minimumEmails} | Sort-Object -Descending Recipients | Format-Table -AutoSize Sender,Recipients | Out-String

      if ($yesterdayStats.Contains("Sender")) {

      $smtp = new-object Net.Mail.SmtpClient($smtpServer)
      $smtp.Send($from, $to, $subject, $yesterdayStats)

      }

      Just change $minimumEmails to 1 and you should see everyone in the report.

      Reply
        • I think you just need to change this line:

          $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-1)

          to

          $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-30)

          That should do the last 30 days. It’s going to take a long time though….

          Reply
  2. Hi Eric,

     

    Thank you for sharing this script. We have a multipal domain like a.com,b.com and c.com and I want to get the report domain wide so how can i segregate it.

    Reply
  3. Hi,

    I am getting below exception while running the script .please help

     

     

    Exception calling “Send” with “4” argument(s): “The parameter ‘to’ cannot be an empty string.
    Parameter name: to”
    At C:\Users\deepak_e\Desktop\MessageStats.ps1:63 char:11
    + $smtp.Send <<<< ($emailFrom, $emailTo, $subject, $body)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Reply
  4. Hi All,

    I tried

    $startTime = (get-date -Hour 01 -Minute 00 -Second 00).AddDays(-1)
    $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-180)

    but I get

    The search time range was incorrectly specified. The End and Start parameter values can’t be the same.

    Any ideas?

     

    Reply
  5. Afternoon Eric,

    Loving your website and scripts :) Are you able to help be with the scrip that outputs from all mailboxes.

    I’ve adjusted it a little bit but I would really love if it could output both sent and received from all mailboxes in a one week period.

    Also the formatting is a little off, I can’t seen to set fixed columns :(

    Thank you in advanced

    Peter

    <#
    Users who sent more than ‘x’ e-mails to be included in the report.
    Created by: Eric Schewe
    Created on: 2015-09-02
    #>

    # Set the minimum sent e-mails to be included in the report.
    $minimumEmails = 1

    #Define date range.
    $startTime = (get-date -Hour 00 -Minute 00 -Second 00).AddDays(-7)
    $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-1)

    #E-mail Config.
    #Multiple e-mail addresses should be in this format “, ”
    $to = “INPUT EMAIL”
    $from = “INPUT EMAIL”
    $subject = “Staff e-mail stats for $startTime to $endTime”
    $smtpServer = “INPUT SMTP SERVER”

    $Stats = Get-TransportServer | Get-MessageTrackingLog -Start $startTime -End $endTime -EventID “SEND” -ResultSize Unlimited | Group-Object -Property Sender | %{ New-Object psobject -Property @{Sender=$_.Name;Recipients=($_.Group | Measure-Object RecipientCount -Sum).Sum}} | Where-Object {$_.Recipients -gt $minimumEmails} | Sort-Object -Descending Recipients | Format-Table @{Expression={$_.Sender};Label=”Sender”;Width=150; Alignment=”left”},@{Expression={$_.Recipients};Label=”Recipients”; Width=150; Alignment=”left”} |Out-String -width 300
    if ($Stats.Contains(“Sender”)) {

    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $smtp.Send($from, $to, $subject, $Stats)
    }

    Reply
  6. add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010
    $e=0;
    $i=0;
    $recexternal=0;
    $recinternal=0;
    $array=@(“[email protected]”)
    Foreach($user in $array)
    {

    $sender=get-transportserver | Get-MessageTrackingLog -ResultSize unlimited -Sender $user

    $sender |%{

    if(($_.source -eq “SMTP”) -and ($_.EventId -eq “SEND”)){$e++}
    if(($_.source -eq “STOREDRIVER”) -and ($_.eventid -eq “DELIVER” )){$i++}

    }
    $recipient=get-transportserver | Get-MessageTrackingLog -ResultSize unlimited -recipients $user

    $reipient |%{

    if(($_.source -eq “SMTP”) -and ($_.EventId -eq “RECEIVE”)){$recexternal++}
    if(($_.source -eq “STOREDRIVER”) -and ($_.eventid -eq “DELIVER” )){$recinternal++}

    }
    }
    write-host “$user has sent $i emails internally”
    write-host “$user has sent $e emails externally”

    Write-host “$user has received $recexternal emails from outside organization”
    Write-host “$user has received $recinternal emails from inside the organization `n”

    This is a test script created by me.i see the first block with IF statement works perfectly and i get $e and $i count

    However the second block doesnt return anything $recexternal and $recinternal always shows zero

    Pls help

     

    Reply
  7. Hello Eric,

     

    Your script works super. My issue is we have some automated systems to generate emails for alerts,TTs etc and few users received day notification emails for the same which is approx. 1000+ in count but when i run the script, this count does not shows the same.

     

    THanks

    AMit Harne

    Reply
    • Thank you. I believe my script only counts emails that leave the organization. Internal emails are excluded. That might explain the discrepancy you are seeing.

      Reply
  8. Hello, I have a question, and I can’t find the information to clarify it.
    My infrastructure has an on premises part with mailboxes, but it also has SMTP published so that applications and devices can send mails.
    What events do I have to look at to be able to make calculations:
    – sent mails
    – received mails
    in this infrastructure?

    If for example I use:
    eventid: DELIVER
    source: STOREDRIVER
    I get the emails delivered to the exchange mailboxes, but that is not all the emails received.

    Reply
  9. My script reads, line by line, the logs that Exchange 2016 saves to file.
    I’m counting now:
    eventid : SEND -> mail sent
    eventid : DELIVER + STOREDRIVE -> mail delivered to mailbox

    I have read in many places that when you get SMTP + RECEIVE, the received ones are taken into account, but I have this doubt because normally a SEND appears after this event.

    Reply

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.