Shiva asks whether there is a way to determine if the recipients of an email have read the email message or not.

Message tracking searches already gives us the ability to determine whether an email message was delivered, but not whether the item has been read yet. To find that out, we need to look at delivery reports.

Delivery reports are available in Exchange organizations, but read tracking is not enabled by default. You can check whether read tracking is enabled in your organization with the Get-OrganizationConfig cmdlet.

[PS] C:\>Get-OrganizationConfig | Select ReadTrackingEnabled

ReadTrackingEnabled : False

If read tracking isn’t already enabled, then you won’t be able to track the read status of messages that have already been sent. If you’d like to be able to track the messages in future, turn it on now.

[PS] C:\>Set-OrganizationConfig -ReadTrackingEnabled $true

There is also a per-mailbox setting. By default, read tracking is enabled for mailboxes, so once you have turned it on for the organization it will work for all of your mailboxes. But, some mailboxes might be sensitive and need to be excluded from these types of searches. In that case, you can use Set-Mailbox to disable read status tracking for those mailboxes.

[PS] C:\>Set-Mailbox john.smythe@contoso.com -MessageTrackingReadStatusEnabled $false

Now let’s look at an example of reporting on the read status of an email. To start with, it helps to know the message ID. One of the ways you can retrieve this is with a message tracking log search. Consider a scenario in which the user Mike Nash has sent a company newsletter to all staff within the last 48 hours. We can search message tracking logs for that message, and return the message ID.

[PS] C:\>Get-MessageTrackingLog -Sender mike.nash@contoso.com -MessageSubject "Company newsletter" -Start (Get-Date).AddHours(-48) -EventId RECEIVE | Select MessageID

MessageId
---------
<b4699b83d1084712b2b746582ceedc15@contoso.com>

Next, we can perform a message tracking report search for Mike’s mailbox using Search-MessageTrackingReport. For convenience, capture the results into a variable.

[PS] C:\>$msg = Search-MessageTrackingReport -Identity mike.nash@contoso.com -BypassDelegateChecking -MessageId b4699b83d1084712b2b746582ceedc15@contoso.com

[PS] C:\>$msg.count
1

As you can see, by searching for the message ID, we get one result, rather than needing to filter through multiple results. But we’re not there yet, so far the information that has been retrieved doesn’t provide the answer we want.

[PS] C:\>$msg

RunspaceId              : 2dca05a6-56a9-4c0e-9c83-de6bc08bb5f0
MessageTrackingReportId : Message-Id=<b4699b83d1084712b2b746582ceedc15@contoso.com>,Server=ny-exch01.contoso.com,Intern
                          al-Id=0,Sender=1f67d9e7-982c-4b1e-878f-3e55f947b5e1,Domain=contoso.com
SubmittedDateTime       : 6/9/2016 6:31:01 AM
Subject                 : Email to all staff
FromAddress             : Mike.Nash@contoso.com
FromDisplayName         : Mike Nash
RecipientAddresses      : {AllStaff@contoso.com}
RecipientDisplayNames   : {All Staff}
Identity                :
IsValid                 : True
ObjectState             : New

Next, retrieve a delivery report by running Get-MessageTrackingReport for that message tracking report ID that has been returned in the search results. Again, capture the results in a variable for ease of handling.

[PS] C:\>$report = Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking

Now take a look at the report.

[PS] C:\>$report


RunspaceId              : 2dca05a6-56a9-4c0e-9c83-de6bc08bb5f0
MessageTrackingReportId : Message-Id=<b4699b83d1084712b2b746582ceedc15@contoso.com>,Server=ny-exch01.contoso.com,Intern
                          al-Id=0,Sender=1f67d9e7-982c-4b1e-878f-3e55f947b5e1,Domain=contoso.com
SubmittedDateTime       : 6/9/2016 6:31:01 AM
Subject                 : Email to all staff
FromAddress             : Mike.Nash@contoso.com
FromDisplayName         : Mike Nash
RecipientAddresses      : {AllStaff@contoso.com}
RecipientDisplayNames   : {All Staff}
DeliveredCount          : 25
PendingCount            : 0
UnsuccessfulCount       : 0
TransferredCount        : 0
RecipientTrackingEvents : {Apurva.Dalia@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Jim.Daly@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Ryan.Danner@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Mike.Danseglio@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Alex.Darrow@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Shannon.Dascher@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          WillsonRaj.David@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Dan.Jump@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Michael.Jurek@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Reto.Kch@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Dalibor.Kacmar@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Wendy.Kahn@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Sandeep.Kaliyath@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Mandar.Naik@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Ranjith.Narayanan@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Mike.Nash@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z...}
Identity                :
IsValid                 : True
ObjectState             : New

The recipient tracking events are what we can use to determine who has opened the email message. Here’s one of the results from that list.

[PS] C:\>$report.RecipientTrackingEvents[0]


Date                 : 6/9/2016 6:31:10 AM
RecipientAddress     : Apurva.Dalia@contoso.com
RecipientDisplayName : Apurva Dalia
Status               : Delivered
EventType            : Deliver
EventDescription     : The message was successfully delivered.
EventData            :
Server               :
Identity             :
IsValid              : True
ObjectState          : New

So from a reporting perspective, the RecipientDisplayName and Status fields are of most interest.

[PS] C:\>$report.RecipientTrackingEvents | Select RecipientDisplayName,Status

RecipientDisplayName    Status
--------------------    ------
Apurva Dalia         Delivered
Jim Daly             Delivered
Ryan Danner          Delivered
Mike Danseglio       Delivered
Alex Darrow          Delivered
Shannon Dascher      Delivered
WillsonRaj David     Delivered
Dan Jump             Delivered
Michael Jurek        Delivered
Reto Käch            Delivered
Dalibor Kacmar       Delivered
Wendy Kahn           Delivered
Sandeep Kaliyath     Delivered
Mandar Naik          Delivered
Ranjith Narayanan    Delivered
Mike Nash            Delivered
KirkJ. Nason         Delivered
Mrina Natarajan      Delivered
Dave Natsuhara       Delivered
Mark Steele          Delivered
Heidi Steen          Delivered
Kim Steffensen       Delivered
Victor Stehmann      Delivered
Alan Steiner         Delivered
Jaka Stele           Delivered

Now, I know the results above are not correct, because I have read the message using OWA while logged on with at least one of those accounts. And in fact, we can check the read status for a single recipient, by appending the -RecipientFilterPath and -ReportTemplate parameters.

[PS] C:\>Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking -RecipientPathFilter jim.daly@contoso.com -ReportTemplate RecipientPath


RunspaceId              : 2dca05a6-56a9-4c0e-9c83-de6bc08bb5f0
MessageTrackingReportId : Message-Id=<b4699b83d1084712b2b746582ceedc15@contoso.com>,Server=ny-exch01.contoso.com,Intern
                          al-Id=0,Sender=1f67d9e7-982c-4b1e-878f-3e55f947b5e1,Domain=contoso.com
SubmittedDateTime       : 1/1/0001 12:00:00 AM
Subject                 :
FromAddress             :
FromDisplayName         :
RecipientAddresses      : {}
RecipientDisplayNames   :
DeliveredCount          : 0
PendingCount            : 0
UnsuccessfulCount       : 0
TransferredCount        : 0
RecipientTrackingEvents : {AllStaff@contoso.com,Submitted,,2016-06-09T06:31:01.0710000Z,
                          Jim.Daly@contoso.com,Expanded,,2016-06-09T06:31:02.2970000Z,Data=AllStaff@contoso.com;All
                          Staff, Jim.Daly@contoso.com,Delivered,,2016-06-09T06:31:10.1460000Z,
                          Jim.Daly@contoso.com,Read,,0001-01-01T00:00:00.0000000}
Identity                :
IsValid                 : True
ObjectState             : New

The recipient tracking events in that output show that Jim has read the message.

Now what about checking for all of the recipients? Well we can do that as well, by looping through the results with a bit of PowerShell. In my example, the variable $report already exists from when I ran Get-MessageTrackingReport earlier, so I can now run:

$recipienttrackingevents = @($report | Select -ExpandProperty RecipientTrackingEvents)

$recipients = $recipienttrackingevents | select recipientaddress

foreach ($recipient in $recipients) {

    $events = Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking `
    -RecipientPathFilter $recipient.RecipientAddress -ReportTemplate RecipientPath
        
    $events.RecipientTrackingEvents[-1] | Select RecipientAddress,Status,EventDescription
}

The output of those commands looks like this:

RecipientAddress                            Status EventDescription
----------------                            ------ ----------------
Apurva.Dalia@contoso.com                 Delivered Unread - The message is marked as Un...
Jim.Daly@contoso.com                     Delivered Read - The message is marked as Read...
Ryan.Danner@contoso.com                  Delivered Unread - The message is marked as Un...
Mike.Danseglio@contoso.com               Delivered Unread - The message is marked as Un...
Alex.Darrow@contoso.com                  Delivered Unread - The message is marked as Un...
Shannon.Dascher@contoso.com              Delivered Unread - The message is marked as Un...
WillsonRaj.David@contoso.com             Delivered Unread - The message is marked as Un...
Dan.Jump@contoso.com                     Delivered Unread - The message is marked as Un...
Michael.Jurek@contoso.com                Delivered Unread - The message is marked as Un...
Reto.Kch@contoso.com                     Delivered Unread - The message is marked as Un...
Dalibor.Kacmar@contoso.com               Delivered Unread - The message is marked as Un...
Wendy.Kahn@contoso.com                   Delivered Unread - The message is marked as Un...
Sandeep.Kaliyath@contoso.com             Delivered Unread - The message is marked as Un...
Mandar.Naik@contoso.com                  Delivered Unread - The message is marked as Un...
Ranjith.Narayanan@contoso.com            Delivered Unread - The message is marked as Un...
Mike.Nash@contoso.com                    Delivered Read - The message is marked as Read...
KirkJ.?Nason@contoso.com                 Delivered Unread - The message is marked as Un...
Mrina.Natarajan@contoso.com              Delivered Unread - The message is marked as Un...
Dave.Natsuhara@contoso.com               Delivered Unread - The message is marked as Un...
Mark.Steele@contoso.com                  Delivered Unread - The message is marked as Un...
Heidi.Steen@contoso.com                  Delivered Unread - The message is marked as Un...
Kim.Steffensen@contoso.com               Delivered Unread - The message is marked as Un...
Victor.Stehmann@contoso.com              Delivered Unread - The message is marked as Un...
Alan.Steiner@contoso.com                 Delivered Unread - The message is marked as Un...
Jaka.Stele@contoso.com                   Delivered Unread - The message is marked as Un...

Success! Sort of… even though I have the result, it’s not all that convenient. So let’s wrap it all up into a short script, called Get-MessageReadStatusReport.ps1 (GitHub). Here’s how it looks:

exchange-message-tracking-delivery-report-read-status

So there you are, tracking the read status of messages in your organization. This assumes that you’ve turned on read status tracking for the organization, and that it isn’t disabled for one or more of the mailboxes you’re interested. There’s a few other caveats as well:

  • Opening a message and actioning a message are two different things. Someone might open your message, but it doesn’t mean they’ve done what you want them to do (e.g. replied, clicked a link, filled out a form, etc)
  • Outlook and OWA mark messages read when they are selected. So read status doesn’t always mean a human has looked at the message.
  • An unread message sitting in the inbox, and an unread message that has been deleted, will be indistinguishable from each other when you look at delivery reports

About the Author

Paul Cunningham

Paul is a former Microsoft MVP for Office Apps and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul no longer writes for Practical365.com.

Comments

  1. Binosh

    Hi,

    I’m using Exchange server 2019, the public folders are added to outlook Add to favourite and able to see the unread message count. But from the OWA unread messages count not showing for public folders emails.

    Please let me know, how to get unread message count in OWA for public folder items.

    Thanks,
    Binosh

  2. Roger Garcia

    Hi Pual, i´m following the steps in a Exchanger server 2016

    All good with the first steps, but When i set this command i get this error:

    [PS] C:\Windows\system32>$report = Get-MessageTrackingReport -Identity $msgs.e6a7ca2a874145bda0fb29d066301103@domain.com -BypassDelegateChecking

    At line:1 char:85
    + … 0fb29d066301103@domain.com -BypassDelegateChecking
    + ~~~~~~~~~~~~~~
    The splatting operator ‘@’ cannot be used to reference variables in an expression. ‘@domain’ can be used only
    as an argument to a command. To reference variables in an expression use ‘$domain’.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : SplattingNotPermitted

    Any help on how should i run the command properly, is the last step to get the info that i want, that is the delivery count by PS, Thanks in advance

    1. Tony Redmond

      Paul isn’t associated with the site any longer. It looks like one of the PowerShell commands needs a little tweaking. I don’t have an Exchange 2016 server to play with so I can’t help, but maybe someone else can.

      1. Conrad

        Very helpful article.

        Observation:
        Perhaps there is a slight typo. At first a variable named ‘$msg’ is used. However, it seems to be subsequently referenced as msgS when calling it in this line: $report = Get-MessageTrackingReport -Identity $msgs.MessageTrackingReportId -BypassDelegateChecking”

        Maybe there should not be an ‘S’ at the end of $msg

        1. Tony Redmond

          That looks right. Unfortunately, Paul Cunningham doesn’t write for the site any longer, but I have updated the text after checking the code.

    2. Foxynette

      Hi Roger,

      From what you are showing, it appears you’ve put the Message Id inside of the command instead of calling the object value.

      You should keep it to be :

      $report = Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking

      Be aware of the typo made in this tutorial as Paul initially put an extra ‘s’ into this command, that could result into an empty result if you aren’t paying attention.
      (-Identity $msg instead of the initial -Identity $msgs)

  3. Vishnu

    This feature is not available anymore as per my testing.

  4. Chuck

    I cannot get this report to work in my Exchange 2010 SP3 CU22 environment. Any message ID I enter received the following error:

    Microsoft.Exchange.Management.Tracking.MessageTrackingSearchResult.count messages found with that ID. Something is wrong.
    At C:\users\rpts\Get-MessageReadStatusReport.ps1:48 char:10
    + throw <<<< "$($msg).count messages found with that ID. Something is wrong."
    + CategoryInfo : OperationStopped: (Microsoft.Excha…thing is wrong.:String) [], RuntimeException
    + FullyQualifiedErrorId : Microsoft.Exchange.Management.Tracking.MessageTrackingSearchResult.count messages found
    with that ID. Something is wrong.

    I have verified the messageid is correct.

    Any ideas on making this work with my environment?

    1. August

      Hi,

      Run the command as shown by Paul, do not run the ps1 by itself because there is an if statement that says if not equal to 1, write that message “something went wrong.”
      By running individually the commands then a much richer messages will come up, like no permission to search etc.

      August

  5. Carol Ostos

    Hi Seckin,

    I am on the same boat, did you find an alternative for Office 365? We also need to trace read email events

    Cheers
    Carol Ostos

  6. Seckin

    Hi Paul,

    Thanks for this great post. But when i run Search-MessageTrackingReport, I am facing with “WARNING: Sorry, the Delivery Reports feature is no longer available in Office 365”.
    Is there an update or another way to get read status for an email in O365 environment?

    Thank you.

    1. Carol Ostos

      Hi Seckin,

      I am on the same boat, did you find an alternative for Office 365? We also need to trace read email events

      Cheers
      Carol Ostos

    2. August

      Hi Sir,

      Very good and informative. Here is my problem though. I have 16k mailbox users but when running the it is not giving me anything, it is just saying too much recepients. I don’t know if there is a newer version that i can use.. i need exactly this type of report but i cannot pass the search. I am trying to use message tracking log but there is no EventID for read.
      Please help, i am new to company so need to impress my boss. 🙂

      Many thanks in advance.

      August

  7. Jeremy Hagan

    I’ve turned this on and “Get-OrganizationConfig | Select ReadTrackingEnabled” shows that the setting is True. After sending various new messages into the environment I can’t get any results. Are there any service restarts needed to kick this off? EX2016 CU10

  8. ClevelandITGuy

    Great write-up, Paul. Thank your for this. I’ve enabled tracking for the organization, but do you know of a way to verify the timestamp that the message was marked Read? We have a scenario where an employee stated they didn’t have access to their mailbox when asked about a specific message, however when the mailbox was administratively checked, the message in question was already marked as Read. Trying to determine when it was marked Read…tyia!

  9. Bob Gagnon

    Thanks for this article Paul, this was perfect for this exact purpose.

    I’m wondering if anyone is seeing what we are in the report as we get closer to a week after the target email was sent. At the end of day one after the email was sent we ran the report and had a count of 143 with an “EventDescription” of “The message was successfully delivered”. You would expect that number to drop each day (flipping to read or unread) however as we’re now a week after the message was sent, I now see 434 with that status. My initial thought was users changing status of the email in their inbox but that has no impact either way.

    Hopefully someone has run into this scenario and can explain.

  10. Chris Rostron

    Hi Paul, great job and I thought you had provided me the solution I was looking for, but when I run this script I get an warning …

    WARNING: Sorry, the Delivery Reports feature is no longer available in Office 365.

    Looks like this feature has been deprecated, any idea of the “new” way MS want us to do this?

  11. Tim

    Any idea if this can be done in Office 365 admin portal? If this is a default feature or something you have to enable in there as well.? And is it only done through powershell?

  12. Paul Bateman

    Paul,

    This is a very informative article. I’m having a bit of an issue with the power shell script. When I try and run it I get the following errors:

    At C:\src\Get-MessageReadStatusReport.ps1:133 char:19
    +
    + ~
    The ‘<' operator is reserved for future use.

    This is in a exchange 2013 environment. Can you point me in the right direction?

    Thanks

    1. Paul Cunningham

      That error usually means you’ve copied or downloaded a bunch of HTML from GitHub instead of just the PowerShell code.

  13. abdul

    Hi Paul-

    I appreciate this and other posts you have posted throughout the years. They have helped me greatly. For this one I was trying to get read count for emails that our VPs send out. Our mailboxes are in Exchange Online. However I couldn’t get any results. the message deliveredcount was 3. but powershell couldn’t find any. I modified the script to get something working in terms of who received it and who read it. I appreciate any insight..

    PS C:\PSS> $msg = Search-MessageTrackingReport -Identity aqavi@mydomain.com -BypassDelegateChecking -MessageId abc123abc.namprd11.prod.outlook.com
    PS C:\PSS> $msg.count
    PS C:\PSS>
    PS C:\PSS> $report = Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking
    PS C:\PSS> $recipientAddresses= @($report | Select -ExpandProperty RecipientAddresses)
    PS C:\PSS> $recipientadddresses > c:\PSS\readers3.txt
    PS C:\PSS> $readers=Import-Csv -Delimiter ‘|’ -Path c:\PSS\readers3.txt -Header ‘EmailAddress’|export-csv c:\PSS\readers3.csv -nti

    PS C:\PSS> Import-CSV c:\PSS\readers3.csv | Foreach-Object{Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking ` -RecipientPathFilter $_.EmailAddress -ReportTemplate RecipientPath |select recipienttrackingevents}

    RecipientTrackingEvents
    ———————–
    {zj@mydomain.com,Delivered,,2018-03-28T18:36:27.3080000Z, zj@mydomain.com,Read,,0001-01-01T00:00:00.0000000}

  14. Ben Warner

    Hi Paul,

    Does this article apply to EOL with Office 365?

    Thanks,

    Ben Warner

  15. Sebastian

    Hi Paul,
    great article, but i have a problem with the execution:
    I followed your steps and got four different MessageIDs for four different E-Mails (so far so good), but when i use the “Search-MessageTrackingReport command i only get results for two Message IDs, the other two seem to have no “reports” – the result is always $null.

    Any ideas of how to troubleshoot this?

  16. Mary, RN

    I am using Outlook 365, and I am not a techie…I’m a nurse with moderate tech knowledge/skills. Here’s my issue:

    I am setting up the read receipt feature correctly, not sending to a group (I expand the group so the e-mail addresses are listed separately), I request READ receipts, and I send the e-mail. Then, the only recipient that shows up on the tracking as having sent the receipt is the very first one to read it. I get the rest of the read receipts, but they do not show up on the tracking report. I then have to manually compare each receipt with a staff roster to verify they have “read” the e-mail. That’s a lot of work. I want Outlook to do that for me! What am I doing wrong, or is it an IT problem?

  17. Patrick

    Hi Paul, another wonderful help for special demands. Thank you!

    But as I try to go through it I receive the following:
    WARNING: Server VIRT-EXCH01.internaldomain.de in domain internaldomain.de encountered an unexpected error. Diagnostic information for this error: Cross-forest autodiscover failure for domain externaldomain.com.

    Where internaldomain.de is the internal domain, the externaldomain.com is the worldwidely used external mail domain (MX record).

    We have 1 external domain with free/busy share and individual share of calendar information and contacts for 2 external organisations.

    Where should I look first? Nothing found in the protocols.

    Greetings from Germany

  18. Shaheer

    Hi Paul,

    This article is great,Your script is giving exact results.
    One question :I’d like to know,is there any way to make this search using message subject?.If we send the mail to different users with same subject but different content,we may need to find each message ID and apply in your script to get the read status.

    Thanks in advance.

    1. Paul Cunningham

      Each message has a unique ID, so yes you’ll need to search multiple times. Or, you can modify the script to suit your needs.

  19. Daniel Kemper

    Great article: Question, when I set ReadTrackingEnabled to true, it seems like it will permit users to do read tracking from Outlook via File–>Info: “Open Delivery Report” on the message in question. Is there a way to turn off this display while preserving the back-end query capability?

  20. Carlos

    Hi! i am trying to get the read status of message sent to a dynamic distribuion list, but i can´t get it.
    First i get the Message ID, but then the Search-MessageTrackingReport gets nothing.
    My environment is Exchange 2010. Is there a way to get the read status from a distribuion list?
    Thanks in advanced.!

    1. Paul Cunningham

      Not sure what you mean by “from a distribution list”. You should be retrieving the read status for the mailboxes that received the message. A distribution list can’t “read” a message.

      1. Carlos

        Sorry for my explain, i mean that i need to know if the members of a distribuion list had read the message. I can´t get it this information for the members. When i launch
        C:\>Get-MessageTrackingLog -Sender xxxx@xxx.com -MessageSubject “message ” -Start “xxxxx” -End “yyyy” | Select MessageID
        The command give no result.

        1. Paul Cunningham

          Okay, you’re thinking too far ahead. Your immediate problem is you’re trying to find the message ID, but your Get-MessageTrackingLog search is not returning any results. There might be no message tracking log data for those start and end dates that you’re searching on. Try broadening your search by just searching for any message tracking log entries for that sender, with no start or end dates. Then refine your search after that.

  21. Rohit Anand

    Hi Paul,

    I am getting below error while running below command. Please help

    $report = Get-MessageTrackingReport -Identity $msg.MessageTrackingReportId -BypassDelegateChecking

    WARNING: An unexpected error has occurred and a Watson dump is being generated: Object reference not set to an instance
    of an object.
    Object reference not set to an instance of an object.
    + CategoryInfo : NotSpecified: (:) [Get-MessageTrackingReport], NullReferenceException
    + FullyQualifiedErrorId : System.NullReferenceException,Microsoft.Exchange.Management.Tracking.GetMessageTrackingR
    eport

  22. Nithyanandham

    Hello Paul ,

    Thanks a lot for the excellent article .I would like to clarify my following queries .

    From which location these two commands Search-messagetrackingreport and Get-messagetrackingreport is fetching up the logs and showing the results ?

    Was these information’s are collected from the message tracking logs directory ?

    Assume i have read the message and then i made it as unread , The command get-messagetrackingreport will show the current state of the message or the previous state ?

  23. Emad Alqaisi

    I ran this in Exchange 2010 and did not work, my coworker helped me with changing the following inside the script:

    $msg.count ($msg | measure).count

    the new update will look like below:

    #Should be exactly 1 result for the script to work
    if (($msg | measure).count -ne 1) {
    throw “$($msg).count messages found with that ID. Something is wrong.”
    }

  24. MLW

    Hi Paul – I just came across your article today. I’m curious to know if there is way to determine when a message was read, opened or viewed. My goal is to determine what messages in a users mailbox was read, opened or viewed after a specific date. Thanks!

    1. Paul Cunningham

      Not retrospectively, as far as I know. Mailbox audit logging can show that info if you’ve got it enabled and configured properly ahead of time.

      1. MLW

        Paul – Thanks for the reply. I do have mailbox audit logging enabled but I can’t seem to find any date/time stamp that relates to when a messages was opened/viewed/read. Any assistance would be greatly appreciated.

        Thanks again!

          1. MLW

            We enabled all events possible for every role. We created an O365 test environment and enabled all logging.

  25. Deepak

    Hi Paul,
    is there any consequences like storage or performance issue after enabling Read tracking in organization level.

  26. xmdofp

    Does this turn on read receipts for users or is it only tracking E-Mail status? I don’t want popups requesting read receipts every time a user from my company sends an E-Mail, but we’ve had requests to see if E-Mails have been read.

    Also, can this be turned on individually per mailbox?

    1. Paul Cunningham

      No, this is not the same as read receipts. Users don’t see anything.

  27. Jim

    Lovely script 🙂

    how can it work for more than 1000 result??

    1. Paul Cunningham

      Most of the Get-* cmdlets have a -ResultSize parameter you can set to Unlimited if you need to.

      1. Enzo@Up2date

        In this case I found that adding the -resultsize parameter spits out an error.

        1. Tarm

          I am currently in the same boat. I cannot figure out how to get more than 1000 results. Any luck?

  28. Stephen

    Is there a way to do this for Exchange Online as the Get-MessageTrackingLog command doesn’t work there

  29. Kodi

    Hi Paul,

    This was a great read! We ran into an issue where the Search-MessageTrackingReport was giving a null result on a message.

    We figured out the problem and wanted to make sure we listed it here incase anyone had the same problem. If you are not the owner of a mailbox but have send as permissions and you send the message then you must use your own information for the -Identity parameter and not the mailbox you are sending as. If you don’t then the Search-MessageTrackingReport will provide no information.

  30. Mojalefa

    Hi Paul,

    Is it possible to get the time stamp when the email was read? Can you also sent the out to text file for analysis?

  31. Eduardo Carvalho

    Hello Paul, first, great article.
    I tried to get the read message event from Mailbox Audit in Exchange 2010 (my messaging platform) but unfortunately it’s not supported for Delegate and Owner, so I arrived at your article and I’m developing a way to get all daily message read events in an Exchange Organization based on what is written here.
    Is there a better way OR based on what we have this is what we have for now in Exchange 2010?
    Your inputs will be very appreciated.
    Regards, EEOC

    1. Paul Cunningham

      This, and mailbox audit logging, are the only two ways I know of.

  32. Dennnis

    “The recipient tracking events in that output show that Jim has read the message.”
    What am I looking at on the results that tells me it was read?..I’m sorry but I am obviously missing something here…

    Thanks for all that you do!.

  33. Terence

    tested the same script on my exchange 2016 lab with success, just having this deviation above on exchange 2010

    exchange 2016 results

    [PS] C:Scripts>.Get-MessageReadStatusReport.ps1 -Mailbox terences@mrc… -MessageId 77b113f46ca344aa9a2bfae67e65539
    2@mrc….

    RecipientAddress Status EventDescription
    —————- —— —————-
    Marc.Crowther@mrc… Delivered Unread – The message is marked as Un…
    terences@mrc… Delivered Read – The message is marked as Read…

    1. Paul Cunningham

      I haven’t tested it on 2010. Sometimes different versions of the cmdlets return results in different ways though.

  34. Terence

    Hi Paul

    does this work with exchange 2010? I keep getting the error below even though the message ID is correct.

    Microsoft.Exchange.Management.Tracking.MessageTrackingSearchResult.count messages found with that ID. Something is wrong

    At C:scriptsGet-MessageReadStatusReport.ps1:61 char:6
    + throw <<<< "$($msg).count messages found with that ID. Something is wrong."
    + CategoryInfo : OperationStopped: (Microsoft.Excha…thing is wrong.:String) [], RuntimeException
    + FullyQualifiedErrorId : Microsoft.Exchange.Management.Tracking.MessageTrackingSearchResult.count messages found
    with that ID. Something is wrong.

  35. Hussain

    Hello Paul,

    Thank you for the script.

    This script works for those email sent by normal Outlook user, but other emails which are sent by applications such as SAP or other open-source in-house hosted survey email system, which has different messageID for example ends with @server-name.FQDN, then such emails are not able to be search by “Get-MessageReadStatusReport”.

    We tried to search emails based on subject but it takes very long time, and while with specifying sender and subject then nothing returns.

    Could you share some thoughts on how we can achieve this. Thank you,

  36. markus

    I have run into the issue of maximum results of 1000 I have tried to add ResultSize Unlimited with no luck to your script “Get-MessageReadStatusReport.ps1 script “.

    1. Bob Gagnon

      We ran into the same issue. In the end, the script would fail when set to Unlimited but if we just picked a number greater than total recipients like 7000 in our case, script runs fine!

      1. Enzo@Up2date

        Can you explain how you achieved this goal? It seems that the transport has a set limit of 1000 recipient objects. What manipulations have you done?

  37. Moazzam Ali

    Thank you cool scripting.

  38. Yogi

    Hi Paul,

    Hope you are doing great !!

    how we can get Tracking Read Status of Email Messages in Office 365

    i have tried the same code but did not work for me getting below error . Please help

  39. GenericITGuy

    Is there a way to tell if the user read the message, and marked it unread again?

  40. Ahnaf

    Thanks for the info Paul, I am getting below error while trying to track for external sender. Please help.

    + throw “$($msg).count messages found with that ID. Something is wrong.”
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (.count messages…thing is wrong.:String) [], RuntimeException
    + FullyQualifiedErrorId : .count messages found with that ID. Something is wrong.

  41. Shawn May

    Hi Paul, Great article – thank you.

    Wanted to run a few things by you. I’m getting some random information from EOL.

    In many cases, I’m getting SubmittedCrossSite and no delivery information.

    $Msg = Search-MessageTrackingReport “shawn may”
    # Summary info for each recipient address
    $Msg[34] | %{ Get-MessageTrackingReport -Identity $_.MessageTrackingReportId `
    -BypassDelegateChecking -DoNotResolve `
    -ReportTemplate Summary `
    | FL SubmittedDateTime, Subject, FromAddress, RecipientTrackingEvents}

    SubmittedDateTime : 7/28/2016 2:52:49 PM
    Subject : INC:1232739 Incident – Missing Payroll Forms – Archive
    FromAddress : shawn.may@acme.com
    RecipientTrackingEvents : {brian.williamson@acme.com,Delivered,,2016-07-28T14:52:54.8710000Z,
    Tim.Walsh@acme.com,Delivered,,2016-07-28T14:52:53.2740000Z,
    dave.sanphy@acme.com,Delivered,,2016-07-28T14:52:53.3350000Z,
    Patricia.Hampton@google.com,TransferredToForeignOrg,,2016-07-28T14:52:53.4660000Z…}

    SubmittedDateTime : 7/28/2016 4:04:11 PM
    Subject : RE: INC:1233393 Comment has been logged : Tiffany Pass – EBF SJB Lost shared drive access
    FromAddress : shawn.may@acme.com
    RecipientTrackingEvents : {Mark.Gregg@acme.com,SubmittedCrossSite,,2016-07-28T16:04:11.8180000Z,
    Blaine.Sutliff@acme.com,SubmittedCrossSite,,2016-07-28T16:04:11.8180000Z}

    ————–

    When querying for read information (when available), EOL is dropping pertinent information. Messages that are flagged as delivered are the only ones that we can check for “read”. Strangely enough, several attribute values disappear when doing it this way (e.g. subject, from address, submitted etc). Like I said, it seems really buggy.

    $Msg = Search-MessageTrackingReport “shawn may”
    $Msg[22] | %{ Get-MessageTrackingReport -Identity $_.MessageTrackingReportId `
    -BypassDelegateChecking -RecipientPathFilter Franklin.Smith@Acme.com `
    -ReportTemplate RecipientPath `
    | FL SubmittedDateTime, Subject, FromAddress, RecipientTrackingEvents}

    SubmittedDateTime : 1/1/0001 12:00:00 AM
    Subject :
    FromAddress :
    RecipientTrackingEvents : {Franklin.Smith@Acme.com,Submitted,,2016-07-28T20:58:44.9840000Z,
    Franklin.Smith@Acme.com,Delivered,,2016-07-28T20:58:46.9230000Z,
    Franklin.Smith@Acme.com,Read,,0001-01-01T00:00:00.0000000}

    Any thoughts?

    Warmest Regards,
    SHAWN MAY
    DTS Inc. | Principal Architect & CEO
    (888) 589-2999 | http://www.yourDTS.com
    Specializing in Cloud & Infrastructure Solutions

  42. John

    Thanks for the information, Paul. Where are the logs being stored? is it the same location as Message Tracking Logs?

    1. Rollf

      I would also love to know that – I enabled ReadTracking but can not find anything in the raw logs what would help me a lot!

  43. Vinod

    Hi Paul,

    Thanks for the information however we are in dedicated environment and for analysis I would like to know is there a way to find the below information for list of mailboxes for last 30days.

    Number of emails received
    Number of emails sent
    Last email received – Date
    Last email sent – Date

    Have tried many scripts and am going no where. Kindly advise.

    Thanks,
    Vino

  44. Murugan M

    Very Good Article.. Thanks.

Leave a Reply