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).
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)
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
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
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
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.
The new version of the script has been posted and linked at the top of this page.
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”}
Is it possible to report on all mailboxes on an Exchange server?
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.
Eric, Can you modify this to view all mailboxes sent and received in the last 30 days?
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….
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.
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
Error message has your answer. You need to define $emailTo.
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?
Reverse -1 and -180. You want to START 180 days ago and END yesterday.
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)
}
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
You might want to try posting this on StackExchange or Technet for scripting help.
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
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.
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.
The script as-is should report on e-mail sent via your on-premises Exchange via SMTP.
If you are using a 3rd party SMTP relay server other than your Exchange I can’t really help you there.
I will check tomorrow to see if we’ve updated this script and post an update if we have.
Oh wait, did you check the newer version of this script?
https://www.pickysysadmin.ca/2017/08/16/powershell-script-to-report-on-total-sendreceived-e-mails-in-exchange-v2-0/
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.