I was performing some mailbox server health checks recently and was trying to find an easy way to test mail flow latency between a lot of servers at once using Test-MailFlow.

After a little bit of tinkering I came up with a PowerShell script that would do the job, but the report it generated was a bit hard to read because all of the output was just on the screen in a single colour. So I decided to take it a step further and output the results to HTML and use colour-coded table cells to give me a report that is easier to read at a glance.

Create an Exchange Mail Flow Latency Heat Map with PowerShell

So I’m sharing the script with you here as well. Please be aware that I haven’t put a lot of coding effort into adding custom parameters, testing a wide variety of scenarios, or doing complex error handling. Also the script doesn’t troubleshoot for you, it only outputs a result that may indicate a problem for you.

Download the script from Github: MailFlowHeatMap.ps1

How to Run the Script

The script has no parameters, you simply execute it from within PowerShell on a server or computer that has the Exchange 2010 management tools installed.

PS C:\>.MailFlowHeatMap.ps1

You may need to right-click the downloaded file, open the Properties and unblock it. You may also need to set your PowerShell execution policy to allow unsigned scripts to run.

A small amount of output is written in the shell window to let you know that the script is making progress.

Create an Exchange Mail Flow Latency Heat Map with PowerShell

The result is a HTML file showing colour-coded table cells for the mailbox latency values (which are in seconds).

Create an Exchange Mail Flow Latency Heat Map with PowerShell

Customizing the Script

You can customize the heat map colours and the output file name by modifying these variables in the script itself (values are in seconds, cool and cold should be the same).

[int]$hot = 30
[int]$warm = 20
[int]$cool = 10
[int]$cold = 10
$filename = "mailflowheatmap.html"

If you’re looking to scheduled the script to run using Task Scheduler there is an example of how to do that here.

If you’d like to email the report to yourself there is example code here. You would just need to modify that example to use the $htmlreport in this script as the message body.

$message.Body = $htmlreport

Download the script from Github: MailFlowHeatMap.ps1

If you have other questions please leave a comment below.

I’ve closed comments on this post as it’s likely that this script simply won’t work well in most 2013/2016 environments these days. Keep in mind that this script was written in the Exchange 2007 era, when Test-Mailflow worked properly. The cmdlet tends to break in more situations now. But the script was always intended more as a proof of concept for building visual HTML reports from PowerShell scripts, and you’re welcome to take the code and use it or modify it for your own purposes.

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. Dame

    You killed this Paul!!

    Great Job Brother

  2. deepak rai

    Hi This is working fine and generating output in html format , but i am not receiving email although i have put correct sender,recipient and smtp server name.
    could you please help.

  3. Olly

    Hi Paul

    Is it possible to modify the script so that it will run a test against an Exchange box in another org? We want to test mailflow originating from outside (so as to include any internet failures in the mix) so want to run something like this on a box external to the mail servers themselves.

    Olly

  4. Adam Winter

    Hi Paul,
    Great script. unfortunately after for about 2 months something isnt working. i’ve been troubleshooting this for a while and i am about to give up.

    issue: mailflowheatmap comes back with an error – but i cant find where to resolve.
    environment – two exchange 2013 CU13 servers in a dag – exch1, exch2

    let me jump to the end for a minute – Test-Mailflow comes back clean from both sides
    Test-Mailflow exch1 -TargetMailboxServer exch2
    RunspaceId : 0b2dddf7-ae8b-4152-8caf-945d97e13fae
    TestMailflowResult : Success
    MessageLatencyTime : 00:00:00.7563999
    IsRemoteTest : True
    Identity :
    IsValid : True
    ObjectState : New

    error from mailflowheatmap

    [PS] C:installExchange Tools>.MailFlowHeatMap.ps1
    Creating a new session for implicit remoting of “Get-ExchangeServer” command…
    Testing exch1 to exch1
    Testing exch1 to exch2
    WARNING: Message latency from exch1 to exch2 not tested due to an error.
    WARNING: Starting a command on the remote server failed with the following error message : The WinRM client sent a request to the remote WS-Management service and was notified that the request size exceeded the configured MaxEnvelopeSize quota. For more information, see the about_Remote_Troubleshooting Help topic.
    Testing exch2 to exch1
    WARNING: Message latency from exch2 to exch1 not tested due to an error.
    WARNING: Starting a command on the remote server failed with the following error message : The WinRM client sent a request to the remote WS-Management service and was notified that the request size exceeded the configured MaxEnvelopeSize quota. For more information, see the about_Remote_Troubleshooting Help topic.
    Testing exch2 to exch2
    WARNING: Message latency from exch2 to exch2 not tested due to an error.
    WARNING: [Microsoft.Mapi.MapiExceptionSendAsDenied]: MapiExceptionSendAsDenied: Unable to submit message.
    (hr=0x80070005, ec=1244)
    Diagnostic context:
    Lid: 40487 EMSMDBMT.EcDoRpcExt2 called [length=46]
    Lid: 56871 EMSMDBMT.EcDoRpcExt2 returned [ec=0x0][length=304][latency=421]
    Lid: 52176 ClientVersion: 15.0.1210.3
    Lid: 50032 ServerVersion: 15.0.1210.3
    Lid: 23226 — ROP Parse Start —
    Lid: 27962 ROP: ropSubmitMessage [50]
    Lid: 17082 ROP Error: 0x4DC
    Lid: 27745
    Lid: 21921 StoreEc: 0x4DC
    Lid: 27962 ROP: ropExtendedError [250]
    Lid: 1494 —- Remote Context Beg —-
    Lid: 41788
    Lid: 44092
    Lid: 41232
    Lid: 60208
    Lid: 37136
    Lid: 34608
    Lid: 55056
    Lid: 42768
    Lid: 56112
    Lid: 33016 StoreEc: 0x4DC
    Lid: 63016 dwParam: 0x32
    Lid: 39640 StoreEc: 0x4DC
    Lid: 10786 dwParam: 0x0 Msg: 15.00.1210.000:exch2
    Lid: 1750 —- Remote Context End —-
    Lid: 26849
    Lid: 21817 ROP Failure: 0x4DC
    Lid: 60547 StoreEc: 0x4DC
    Lid: 21966
    Lid: 30158 StoreEc: 0x4DC
    Done.

    both servers return 700 for MaxEnvelopeSizekb
    winrm g winrm/config
    Config
    MaxEnvelopeSizekb = 700
    MaxTimeoutms = 60000
    ….

    if i run emtshooter – it always runs against exch2 (cant get it to connect to exch1)

    output – identical on both servers
    Welcome to the Exchange Management Troubleshooter!
    We recommend that you run the troubleshooter after making changes to
    IIS to ensure that connectivity to Exchange Powershell is unaffected.
    Checking IIS Service…
    Checking the Exchange Install Path variable…
    Checking the Powershell Virtual Directory…
    Checking the Powershell vdir SSL setting…
    Checking the Powershell vdir path setting…
    Checking HTTP Port 80…
    Checking HTTP Port 80 Host Name…
    Testing for errors…
    2
    Location ConnectToAnyServer 2
    VERBOSE: Connecting to Exch2.swiftechosting.com (it says this on BOTH servers – never says connecting to exch1)

    The Exchange Management Troubleshooter successfully completed connecting to:
    Exch2.swiftechosting.com

    test get-datghealth comes back clean (and correct)

    we are not experiencing any issues anywhere – i just like the script and it no longer works.
    thoughts?

    thank you

    1. Avatar photo

      Test-Mailflow seems to have had a bumpy ride over the years. Keep in mind this script was written in a 2007 environment, and was mostly a PoC for HTML formatting for me. Since then the server architecture has changed a lot, and I’ve seen all kinds of wacky behavior with the Test-* cmdlets as a result.

      Right now the script throws some errors in my 2016/2013/2010 coexist lab. It might have something to do with where active database copies are sitting at the time, or something else. Haven’t dug any further into it to be honest.

  5. Christian Schroeder

    Hello Paul,

    thanks for the script. I tried it into my enviromnent but something is strange with the result values. They are to high after the conversion in line ‘[double]$latency = “{0:N2}” -f $($result.MessageLatencyTime.TotalSeconds)’. For example, the value of MessageLatencyTime.TotalSeconds is 1,3785 after the conversaion the value is 138.

  6. Rodolfo

    It estimated there is a possibility that you can send me the script please download it and that tells me that there is no 404 page not found
    very thankful

      1. Rodolfo

        Paul, thank you very much for your answer, is very good

        Best regards!!!

  7. Kapil

    Hi Paul

    Is there anyway i can automate this Script to have Message Latency Hourly results.

    Actually, I want to have the Message Latency hourly results for atleast 3 Days.

    Best Regards

  8. Kapil

    Hi Paul

    Thanks for the script. You have made the job much more easier.

    Please let me know what are the factors the message latency depends on.

    Is there any way to reduce the message delivery between two mailbox servers.

  9. Sean`

    Hi Paul,

    Many thanks for so many good scripts. I have been trying to tinker with this script (Exchange Mail Flow Latency Heat Map) to get it to work with Exchange 2013 but it is beyond my PowerShell skills.
    Have you ever been able to get it to work with Exchange 2013?

    Many thanks
    Sean

  10. Joel

    When I run the MailFlowHeathMap I get the following error …. Any idea’s

    Thanks …

    Joel

    Get-Member : No object has been specified to the get-member cmdlet.
    At C:scriptsmailflowheatmapMailFlowHeatMap.ps1:97 char:32
    + $headers = $report | Get-Member <<<< -MemberType NoteProperty | Select Name
    + CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
    + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand

  11. Lalmd

    Hi Paul,

    The script was very helpful and working without any issues. thank you very much.

    Pl. help me to modifiy the script to show only the mailbox server results and skip the non mailbox result in the table.

  12. Ian

    Great tool. You don’t have a catch for a mail flow failure (and failure shows latency of 0 for some reason) so I modified the code in the following way:
    if ($result.testmailflowresult -eq “Success”)
    {
    [double]$latency = “{0:N2}” -f $($result.MessageLatencyTime.TotalSeconds)
    $reportObj | Add-Member NoteProperty -Name “To $targetserver” -Value $latency
    }
    else
    {
    $reportObj | Add-Member NoteProperty -Name “To $targetserver” -Value $result.testmailflowresult
    }

    And down below:
    elseif ($value -eq “*FAILURE*”)
    {
    $htmltablerow = $htmltablerow += “$value”
    }

    Threw in new catch for a -le $suspicious value of 0 and a nice purple hue (#8000FF) for td.suspicious as well, just in case.

    Mail flow and transport queues showed RED in your test-exchangeserverhealth and I tried to use this script to diagnose. Turned out to be insufficient Log disk space. Log Disk Free space in my Exchange Environment Report is showing 96.1% free for all disks likely because it’s checking the mount point rather than the actual LUN space. Doh! Any suggestions for querying that accurately?

    1. Avatar photo

      Nice fix. Glad to see someone using the script 🙂

      Querying mount points in PowerShell is reasonably simple. The complex bit is working out the log or database path for a database and then finding the appropriate mount point. Not impossible, just needs a bit of work.

  13. Erro

    Hi Paul

    Could you help me?

    When I try to execute your script I receive this error:

    Get-Member : No object has been specified to the get-member cmdlet.
    At C:MailFlowHeatMap.ps1:97 char:32
    + $headers = $report | Get-Member <<<< -MemberType NoteProperty | Select Name
    + CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException
    + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand

  14. Vamshi Gupta

    Hi Paul,

    this script check email latency between mailbox server’s, but is there any way to find latency with email send out to internet ?

  15. Sirius Bergman

    Great job Paul,

    I just tested this script in severals environments but in some cases the latency is returned with a comma 1,32 instead of returning 1.32. To solve this I’ve modified the script

    Added this variable: $USCulture = New-Object System.Globalization.CultureInfo(“en-US”)
    and replaced [double]$latency = “{0:N2}” -f $($result.MessageLatencyTime.TotalSeconds) with
    [double]$latency = $result.MessageLatencyTime.TotalSeconds.ToString(“F2”,$USCulture)

    This works fine in all environments

  16. Julian

    Hey Paul,

    Will this heat map PS work in Exchange 2013? Or will this only work for 2007/2010?

  17. Luiz

    Hi Man,

    Thanks for your script, this is perfect.
    I have one question.
    When I execute this script in a physical machine, evertything is all right on report but when i execute this one in one virtual machine the report be all red…

    Do you know why this things happen? did you see it in another environment?

    Regards.

  18. Frank P

    Hi Paul,

    Thanks very much for sharing this. The script is very handy. It worked like charm almost since first run despite a permission error trying to create the html in C: (My bad)

    Thanks again mate.

  19. Amit

    Hi Paul,

    Can you tell me how to verify daily /weekly/monthly email transactions from Exchange servers 2010.
    I there any reporting tool ?

  20. Amit

    Hi Paul,

    Can you tell me how to verify daily /weekly/monthly email transactions from Exchange servers 2010.
    I there any reporting tool ?

    Thanks and Regards,
    Amit Atugade

  21. Simon Craner

    I would like this to email through the results the same way the test-exchangeserverhealth script does.

    please can you help elobrate on the format to get it the same

  22. Shane Bryan

    I meant to say also, for a larger organisation with multiple active servers, scheduling this with another task to copy it to a IIS server where the html file could be viewed in IE by the whole IT team, that would be fantastic.

  23. Shane Bryan

    Thanks for the great script Paul.

    I got a bit worried when I ran it the first time, as it errored everywhere, but then.. yeah.. realised that only one of our DAG servers is active..

    BB-EX02 Error Error Error
    RC-EX02 Error Error Error
    RC-EX03 Error Error 2.77

    I will definitely file this one away for future reference though, thanks again!

  24. MHG

    Question / Suggestion:

    Could this HTML output include a reference legend for the code …..

    td.cold{background: #B3E6B3;}
    td.cool{background: #E2F09C;}
    td.warm{background: #FF6E6E;}
    td.hot{background: #E34949; color: #ffffff;}

    A visual so it is known what the values and color relationships are?

  25. Carl

    Is it right to get errors when running it on servers in a dag?

    I’ve got 3 mailbox databases accross two servers (local and remote) in a DAG. Is it because the active server is LOCAL?

    Seems the only one that didn’t give an error is when it tested LOCAL to LOCAL

    I get …

    Testing REMOTE to REMOTE
    WARNING: Exchange can’t perform the mail flow test because currently there are no mailbox databases on server TRINITY.
    Testing REMOTE to LOCAL
    WARNING: Exchange can’t perform the mail flow test because currently there are no mailbox databases on server TRINITY.
    Testing LOCAL to REMOTE
    WARNING: Exchange can’t perform the mail flow test because currently there are no mailbox databases on server TRINITY.
    Testing LOCAL to LOCAL
    Done.

    1. Tony

      My bet is it will only report on the databases that are active on the server you’re running the script against.
      We’ve got a 2 location, 4 DB DAG, with 2 running in one location, and the other 2 in the other.
      The script should report the 2 that are running on the server they’re currently mounted on.

  26. Joe

    Another fine script to help us Exchange Administrators out! Great work – I’m adding it to my daily scripts.

    1. dan

      Thank you Paul, I use your script daily and i really appreciate you for sharing it.

      Test-mailflow was working fine but i starting getting the below error , any idea?

      Test-Mailflow not working on server1 when mailbox database X is mounted on server1
      Error [Microsoft.Mapi.MapiExceptionADPropertyError]: MapiExceptionADPropertyError: Unable to open message store.
      (hr=0x80004005, ec=2418)

      · Test-Mailflow works fine on server1 , when mailbox database X is ,mounted on server2

Comments are closed.