AceMagic K1 Perfect Proxmox Test Box

Exchange Server Health Check: The Ultimate PowerShell Checklist for Admins

This article may contain affiliate links. If you buy through them, I earn a small commission at no extra cost to you.

Introduction

When the Exchange Server isn’t working as expected, admins need a quick and effective way to diagnose issues. This guide covers both routes I use. First, the Exchange Health Checker script, which collects everything in one pass. Then a manual PowerShell checklist to verify Exchange Server health, troubleshoot mail flow, and spot problems fast.

  • Run these checks from the Exchange Management Shell (EMS) on the Exchange Server.
Exchange Server Health Check

Quick Answer:

To check Exchange Server health, run Microsoft’s free Health Checker script from the Exchange Management Shell:

.\HealthChecker.ps1

One pass collects configuration, patch level and performance data, then writes a colour coded HTML report. For mail flow, add Test-ServiceHealth, Test-Mailflow and Get-Queue.

If you run Exchange 2016 or 2019, expect the report to flag your server as out of support and unpatched. Support ended on 14 October 2025.

Start With the Exchange Health Checker Script

Microsoft ships a free diagnostic script called Health Checker. It is part of the CSS-Exchange repository. One run collects the configuration, patch level, and performance data of a server, then flags common configuration issues against Microsoft’s best practices.

Run it before anything else. It often shows that the problem is a configuration setting rather than a fault.

Download the Health Checker script

Grab the latest version straight from the GitHub release:

Switch first in the PowerShell section to the directory where you want to download the script file.

PowerShell

Invoke-WebRequest -Uri "https://github.com/microsoft/CSS-Exchange/releases/latest/download/HealthChecker.ps1" -OutFile "HealthChecker.ps1"

The script has an auto-update built in. It checks for a newer release each time it starts. If you want to refresh it without running a health check, use the ScriptUpdateOnly switch:

PowerShell

.HealthChecker.ps1 -ScriptUpdateOnly

That switch does not need elevated permissions or the Exchange Management Shell. It only pulls the latest version.

On a server with no internet connectivity, the version check will fail. Skip it with -SkipVersionCheck, or download the script on a non-Exchange system and copy it over.

Permissions you need

Before you run Health Checker, confirm your group membership. You need:

  • Local Administrator group on the target server
  • Organization Management group in Exchange
  • Domain Admins only if you use the DCCoreRatio parameter

Missing one of the following groups is the usual reason the script stops early with an access error. Organization Management plus Domain Admins is not a substitute for local administrator rights, so check all three.

Supported Exchange Server versions

Health Checker supports the following Exchange Server versions:

  • Exchange Server 2016
  • Exchange Server 2019
  • Exchange Server SE

Older builds are out of support and the script no longer targets them. Fact worth knowing: Exchange Server 2013 reached end of support in April 2023, so if you are still on it, the health check is not your biggest problem.

Building a server from scratch instead of checking an existing one? The functional level and prerequisite traps are worth reading first, and I collected mine in Install Exchange Server 2019, lessons learned.

Run Health Checker against the local server

The simplest run analyses the local server:

PowerShell

.HealthChecker.ps1

To point it at a specified server instead, pass the Server parameter:

PowerShell

PS C:> .HealthChecker.ps1 -Server EX01

The main data collection writes a detailed output to the screen, a .txt log file, and an XML file per server. The default location is the current directory. Change the output location with -OutputFilePath:

PowerShell

The Output location should already exist; the script will not run.

PS C:> .HealthChecker.ps1 -Server EX01 -OutputFilePath C:HealthCheck

Run Health Checker against all the servers

To collect data from every Exchange server in the Exchange environment, pipe them in:

PowerShell

PS C:> Get-ExchangeServer | Where-Object {$_.AdminDisplayVersion -Match "^Version 15"} | .HealthChecker.ps1

Each server produces its own HealthChecker XML file in the same location. The script serverlist is built from the pipeline, so you can filter it however you like.

Build an HTML report from the XML files

Once all the XML files are located in one folder, turn them into a single consolidated HTML report:

PowerShell

PS C:> .HealthChecker.ps1 -BuildHtmlServersReport -XMLDirectoryPath C:HealthCheck

The BuildHtmlServersReport switch reads the existing HealthChecker XML files and writes an HTML output file. The default name is ExchangeAllServersReport-yyyyMMddHHmmss.html. Override it with -HtmlReportFile.

The report is color coded. Red rows are errors, yellow are warnings, and those are the rows worth reading first.

Check load balancing across your Exchange CAS

The LoadBalancingReport switch shows how client connections are distributed across all the Exchange CAS endpoints:

PowerShell

PS C:> .HealthChecker.ps1 -LoadBalancingReport -SiteName SiteA

Without SiteName, it uses the current site. You can also target specific servers with -ServerList EX01,EX02. Use it to confirm a namespace is actually balanced, rather than sending every backend connection to one node.

Generate a Health Checker mailbox report

The mailbox report returns mailbox statistics for whichever server you point it at:

PowerShell

PS C:> .HealthChecker.ps1 -Server EX01 -MailboxReport

Useful on MBX servers when you want database distribution and mailbox counts without writing your own query. For who can open what once you have that list, see Set-Mailbox permissions in Exchange Server.

Collect vulnerability information

The VulnerabilityReport switch helps collect known CVE exposure across the environment:

PowerShell

PS C:> .HealthChecker.ps1 -VulnerabilityReport

It exports a JSON file alongside the usual output, which makes it easy to feed into a ticket or a compliance sheet.

Other useful switches

Switch

What it does

-AnalyzeDataOnly

Reprocesses XML you already collected, without touching the servers again

-ForceLegacy

Collects serially instead of spawning background jobs

-SkipVersionCheck

Bypasses the script version check

-SaveDebugLog

Retains the debug log after a clean run instead of discarding it

-DCCoreRatio

Compares your Exchange core count against DC and GC cores

AnalyzeDataOnly is the one worth remembering. If you already ran the collection, there is no reason to hit the servers again just to reread the results.

Use ForceLegacy on slow or heavily loaded systems, where the background jobs cause trouble. The run takes longer in exchange.

Turn on -SaveDebugLog before you raise a support case. Without it the debug log is discarded when the script finishes cleanly.

What Health Checker Reports on Exchange 2016 and 2019 Now

This is the part nobody warned me about, so it belongs in this guide.

I ran Health Checker on an Exchange 2019 CU15 server in September 2026 expecting the usual list of configuration warnings. What came back was a red error at the top of the report:

Your Exchange server is out of support and no longer receives SUs. It is now considered persistently vulnerable and it should be decommissioned ASAP.

Underneath it, a long list of CVEs.

The reason is simple. Exchange Server 2016 and 2019 reached end of support on 14 October 2025. If you are running either one, Health Checker will tell you the same thing.

Here is the part that surprised me most. I could not just download the latest fix. Security updates released after that date are only available through the paid Extended Security Update program, which you arrange with a Microsoft account representative. Windows Update offers you nothing. There is no warning on the server. Patching simply goes quiet, and months pass without anyone noticing.

Two things are worth knowing before you plan around this:

  • ESU is not a long-term answer. Period 2 ends in October 2026, and Microsoft has said there will be no further extension.
  • Exchange security updates are cumulative per CU. You only need the latest SU for your CU, not every one you missed.

Use Health Checker before you upgrade

I have since upgraded three Exchange 2019 servers to Subscription Edition. One standalone, and two DAG members that I did one at a time. CU15 is an in-place upgrade that installs much like a normal cumulative update, so the server and hardware remain unchanged. On my own server it took about 35 minutes.

Health Checker earned its place here a second time. I ran it before starting and it flagged the prerequisites worth clearing first: page file size, outdated Visual C++ runtimes, a power plan still set to Balanced, and .NET strong crypto settings. None of them took long, and clearing them beforehand made for a clean upgrade.

Put the server into maintenance mode before you begin, exactly as you would for a cumulative update. And if setup stops partway through complaining about a certificate, that is a known trap I wrote up separately in Exchange setup fails with a certificate error.

Here is the rest of my prep before each upgrade: a fresh Veeam backup, a Proxmox snapshot of the VM, antivirus paused, and a check that the databases and queues are healthy. For the two DAG members I moved the databases to the other node first, then worked through one server at a time.

One thing worth knowing afterward: Windows Update starts working again. Once the servers were on SE I installed the current August 2026 security update the normal way, which is exactly what Exchange 2019 could no longer do.

So run Health Checker twice. Once to find out how long you have been unpatched, and again as a pre-flight check before you begin the upgrade.

Step-by-Step Exchange Server Health Check

Health Checker is thorough, but it does not test mail flow. These eleven checks cover the live behavior of the Exchange Server. Run them from the main PowerShell session in EMS.

1. Check Exchange Services

Ensure all critical Exchange services are running:

PowerShell

Test-ServiceHealth

✅ Expected: All required services should be Running.

2. Check Server Component Status

Verify if essential Exchange components are active:

PowerShell

Get-ServerComponentState -Identity $env:COMPUTERNAME

✅ Expected: Most components should be Active, except hybrid-only features like ForwardSyncDaemon.

3. Verify Mail Flow

Send a test email to check internal mail routing:

PowerShell

Test-Mailflow

✅ Expected: Success with reasonable latency.

4. Read the Message Tracking Log

Test-Mailflow only proves that a probe message made it through. The message tracking log shows what happened to real mail. Use it when a user reports that a message never arrived.

PowerShell

Get-MessageTrackingLog -Start (Get-Date).AddMinutes(-30) |
    Where-Object { $_.EventId -in "RECEIVE", "DELIVER", "SEND" } |
    Sort-Object Timestamp |
    Format-Table Timestamp, EventId, Source, Sender, Recipients, MessageSubject -AutoSize

✅ Expected: every message shows a RECEIVE followed by a DELIVER or a SEND, with no gap in between.

Three things to know before you trust the output:

  • It reads the local server only. Each server keeps its own tracking log, so add -Server EX02 to look at another node. In a DAG, a message can easily be received on one server and delivered by another.
  • ResultSize defaults to 1000. On a busy server a 30 minute window will hit that ceiling and truncate without telling you. Add -ResultSize Unlimited when the count matters.
  • Add FAIL and DEFER when you are chasing a problem. RECEIVE, DELIVER, and SEND show you the mail that worked. FAIL and DEFER show you the mail that did not, which is usually the reason you opened the log in the first place.

5. Check Exchange Database Status

Ensure mailbox databases are online and healthy:

PowerShell

Get-MailboxDatabase -Status | Select Name, Mounted, OnlineMaintenanceRunning

✅ Expected: Mounted = True.

6. Check Mail Queues

Identify stuck emails in transport queues:

PowerShell

Get-Queue

✅ Expected: Low message count, no long-standing retries.

Force a retry if needed:

PowerShell

Get-Queue | Where-Object {$_.Status -eq "Retry"} | Retry-Queue -Resubmit $true

Delete problematic messages:

PowerShell

Get-Queue | Get-Message | Remove-Message -WithNDR $false

7. Verify DNS Resolution for Mail Delivery

Ensure Exchange can resolve external domains:

PowerShell

Resolve-DnsName example.com -Type MX

✅ Expected: Successful resolution with valid MX records.

8. Test SMTP Connectivity

Check if Exchange can send emails to external servers:

PowerShell

Test-NetConnection -ComputerName example.com -Port 25

✅ Expected: TCP connection succeeds.

9. Check Event Logs for Errors

Identify critical errors related to mail flow and databases:

PowerShell

Get-EventLog -LogName Application -EntryType Error -After (Get-Date).AddDays(-3) | Where-Object {$_.Source -match "MSExchange"}

✅ Expected: No recent critical errors.

10. Verify Outlook Web Access & ECP

Ensure web-based access is functional:

PowerShell

Test-OutlookWebServices | FL

✅ Expected: All tests return Success.

11. Check SSL Certificate Expiry

Ensure Exchange certificates are valid:

PowerShell

Get-ExchangeCertificate | Select Thumbprint, FriendlyName, Subject, NotAfter

✅ Expected: NotAfter should be in the future.

If one is close to expiring, replace it before it bites. I walk through the whole process in Renew an Exchange certificate in 7 steps.

Frequently Asked Questions

What is the command to check Exchange server health?

Test-ServiceHealth is the fastest single command. It lists all required Exchange services and flags those that are not running. For a complete picture, run the Health Checker script instead, as it covers configuration, patch level, and performance issues in a single pass.

How to check server health using PowerShell?

Work outward from the services. Start with Test-ServiceHealth, then Get-ServerComponentState for component status, then Get-Queue for transport. Each command targets one layer, so a failure tells you where to look next.

How to check Exchange database health?

Run Get-MailboxDatabase -Status and confirm Mounted is True on every database. For a DAG, add Get-MailboxDatabaseCopyStatus * to check the copy queue length and replication state on each copy.

How do I trace a single message through Exchange?

Use Get-MessageTrackingLog with -Sender or -Recipients to narrow it down, then read the EventId column in timestamp order. A message that stops after RECEIVE never left the server. One that reaches SEND left, and the answer is on the other end.

Why does Windows Update not offer Exchange security updates anymore?

Because Exchange 2016 and 2019 reached end of support on 14 October 2025. Updates released after that date go only to servers covered by the Extended Security Update program, and they are not pushed through Windows Update. Nothing on the server tells you this has happened.

Final Thoughts

This checklist covers what to look at when an Exchange Server misbehaves. Run Health Checker for the configuration baseline, then use the PowerShell commands to confirm mail flow actually works. Between the two you can pinpoint most problems and keep email services running smoothly.

If the report indicates the server is out of support, treat that as the finding that takes precedence over everything else on the page.

I’d love to hear from you. Was this article helpful? Share your thoughts in the comments below. If you prefer, you can also reach me by email or connect with me on Reddit at Navigatetech.

Related Articles

Full Disclosure

Any purchases made from clicks on links to products on this page may result in an affiliate commission for me. 

Please keep in mind that the quantity or price of items can change at any time.

As an Amazon  Associate, I earn from qualifying purchases.

Als Amazon-Partner verdiene ich an qualifizierten Verkäufen

As an eBay Partner, I earn from qualifying purchases.

Als eBay-Partner verdiene ich an qualifizierten Verkäufen.

About the author

information

Amazon affiliate Program

Edy Werder is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to Amazon.com.

AlieXpress affiliate Program

Edy Werder is a participant in the ALIEXPRESS Affiliate Network, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to ALIEXPRESS properties and associated sub-sites.

Site map

Blogs