Exchange 2010 Health Check Script

A few years ago when I was leading my first Exchange team, one of the very first lessons I learned was that everyone has a different idea for how to validate a change. When I started my IT career, I was taught after any change you reboot, and when the system comes back up you check the event logs, services, and any relevant application logs to make sure everything is running as expected. However, as I found out after delegating a patching change to a subordinate, everyone didn’t learn that same lesson. Hence, the Exchange 2010 Health Check script.

Start PowerShell to run the Exchange 2010 Health Check script
Server restart – friend or foe?

The Change

During that change window, three out of four mailbox servers didn’t start their Mail Submission service. This was on a weekend of course, and IIRC no one really noticed until Sunday evening. Once I got the call though, I knew exactly what the issue was. I remoted in to the network as soon as I could, started the Mail Submission services, and dreaded going to work the next morning.

The Aftermath

That night I thought about the two questions that were going to come my way the next morning- how did this happen, and how do we prevent it in the future? The first question is relatively easy. Sometimes, for some reason, Exchange services just don’t want to start after a reboot. This is something of a cop-out of course, but I’ve seen this service hang at reboot in most of the environments I’ve worked on. The other question of how we prevent this in the future, was going to take a bit of work.

The Solution

Training is of course the simple answer, but that isn’t the answer that I would want to hear. Especially after an outage of that magnitude.

The solution I came up with was the following Exchange 2010 Health Check Script. This script was to be run against any Exchange 2010 server post-change, and would gather the following server stats and perform the relevant tests:

  • All Servers
    • Status of Services set to startup automatically
    • All of the Error and Warning logs in the last 100 Application log entries
    • All of the Error and Warning logs in the last 100 System log entries
  • For CASes
    • All active connections to all services (based off this function)
    • Test POP connectivity
    • Test IMAP connectivity
    • Test OWA connectivity
    • Test EWS connectivity
  • For Hub Transports
    • Test SMTP connectivity
    • Report on messages in queue
  • For Mailbox Servers
    • Test DAG health
    • Report on the mailbox database copy statuses
    • Test MAPI connectivity
    • Test mailflow

In situations where we had multi-role servers, namely CAS/HTs, the script would run the tests and generate reports for each role. And once run, the admin will be prompted to email the Messaging team’s distribution group so everyone can have eyes on the results.

#################################################################
# Exchange 2010 Health Check Script                             #
# This script generates an Exchange Server health check report  #
# And can email it to a specified recipient			#
#								#
# Created by Eric Kukkuck 3/3/2015				#
# https://MSExchangeHelp.com					#
#################################################################

$OrgName = "MyCo"
$TranscriptPath = "D:\ABZ\Reports\E2010HealthCheck_" + (Get-Date -f yyyy-MM-dd_HH-mm) + ".txt"

$POPEnabled = "$False"
$IMAPEnabled = "$True"

$SMTPServer = "smtp.domain.intranet"
$SMTPSender = "postmaster@domain.net"
$SMTPRecipient = "MessagingTeamDG@domain.net"

start-transcript -Path $TranscriptPath

    Write-Host ""
write-host "Welcome to the $OrgName Exchange Server Health Check script. This script is to be run upon completion of any changes made to an Exchange sever and the results emailed to $SMTPRecipient." -foregroundcolor "green"

    write-host ""
Write-Host "Enter server name to validate:" -ForegroundColor "Yellow"

    write-host ""
$Server = Read-Host

    write-host ""
$ServerRole = get-ExchangeServer $Server | Select ServerRole

function Get-CASActiveUsers {
  [CmdletBinding()]
    param(
    [Parameter(Position=1, ParameterSetName="Value", Mandatory=$true)]
    [String[]]$ComputerName,
    [Parameter(Position=0, ParameterSetName="Pipeline", ValueFromPipelineByPropertyName=$true, Mandatory=$true)]
    [String]$Name
  )

  process {
    switch($PsCmdlet.ParameterSetName) {
      "Value" {$servers = $ComputerName}
      "Pipeline" {$servers = $Name}
    }
    $servers | %{
      $RPC = Get-Counter "\MSExchange RpcClientAccess\User Count" -ComputerName $_
      $OWA = Get-Counter "\MSExchange OWA\Current Unique Users" -ComputerName $_
      $AS = Get-Counter “\MSExchange ActiveSync\Current Requests” -ComputerName $_
      $AB = Get-Counter "\MSExchangeAB\NSPI Connections Current" -ComputerName $_
      $IMAP =  If($IMAPEnabled -eq 'True'){get-counter "\MSExchangeimap4(1)\current connections" -ComputerName $_}Else{'Disabled'}
      $POP = If($POPEnabled -eq 'True'){Get-Counter "\MSExchangePOP3(_Total)\Connections Current" -ComputerName $_}Else{'Disabled'}
      $EWS = Get-Counter "\W3SVC_W3WP(*msexchangeservicesapppool)\Active Requests" -ComputerName $_
      New-Object PSObject -Property @{
        Server = $_
        "RPC Client Access" = $RPC.CounterSamples[0].CookedValue
        "Outlook Web App" = $OWA.CounterSamples[0].CookedValue
	"ActiveSync" = $AS.CounterSamples[0].CookedValue
	"Address Book" = $AB.CounterSamples[0].CookedValue
	"IMAP" = If($IMAP -eq'Disabled'){'Disabled'}Else{$IMAP.CounterSamples[0].CookedValue}
	"POP" = If ($POP -eq 'Disabled'){'Disabled'}Else {$POP.CounterSamples[0].CookedValue}
	"EWS" = $EWS.CounterSamples[0].CookedValue
      } | Select-Object Server,"RPC Client Access","Outlook Web App",ActiveSync,IMAP,POP,"Address Book",EWS | FT -Auto
    }
  }
}

if ($ServerRole -like '@{ServerRole=ClientAccess}'){write-host 'Dedicated CAS Server Detected...' -foregroundcolor "magenta"
write-host ""
write-host 'Service Status (Displays Services that are configured for Automatic startup but are in a stopped state):' -foregroundcolor "green"
Get-WmiObject Win32_Service -computername $Server |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } |
# process them; in this example we just show them:
Format-Table -AutoSize @(
    'DisplayName'
    @{ Expression = 'State'; Width = 9 }
    @{ Expression = 'StartMode'; Width = 9 }
    'StartName'
)
write-host "All 'Error' & 'Warning' Application log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName application | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host "All 'Error' & 'Warning' System log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName System | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host 'Active Client Connections:' -foregroundcolor "green"

get-casactiveusers -computername $Server

write-host 'IMAP Connectivity Validation:' -foregroundcolor "green"

If($IMAPEnabled -eq 'False'){Write-Host ""}

If($IMAPEnabled -eq 'True'){Test-ImapConnectivity -ClientAccessServer $Server | fl Result,scenariodescription}Else{'IMAP Service disabled, continuing to next test...'}

If($IMAPEnabled -eq 'False'){Write-Host ""}

write-host 'POP Connectivity Validation:' -foregroundcolor "green"

If($POPEnabled -eq 'False'){Write-Host ""}

If($POPEnabled -eq 'True'){Test-PopConnectivity -ClientAccessServer $Server | fl Result,ScenarioDescription}Else{'POP Service disabled, continuing to next test...'}

If($POPEnabled -eq 'False'){Write-Host ""}

write-host 'Outlook Web Services Connectivity Validation:' -foregroundcolor "green"

Test-OutlookWebServices -ClientAccessServer $Server | ft Type,Message -auto -wrap

write-host 'OWA Validation:' -foregroundcolor "green"

Test-OwaConnectivity -ClientAccessServer $Server | fl Result,URL,AuthenticationMethod,ScenarioDescription

write-host 'Web Services Validation:' -foregroundcolor "green"

Test-WebServicesConnectivity -ClientAccessServer $Server | ft Result,Scenario,ScenarioDescription -auto

}

if ($ServerRole -like '@{ServerRole=HubTransport}'){write-host 'Dedicated Hub Transport Server Detected...' -foregroundcolor "magenta"
write-host ""
write-host 'Service Status (Displays Services that are configured for Automatic startup but are in a stopped state):' -foregroundcolor "green"
Get-WmiObject Win32_Service -computername $Server |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } |
# process them; in this example we just show them:
Format-Table -AutoSize @(
    'DisplayName'
    @{ Expression = 'State'; Width = 9 }
    @{ Expression = 'StartMode'; Width = 9 }
    'StartName'
)
write-host "All 'Error' & 'Warning' Application log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName application | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host "All 'Error' & 'Warning' System log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName System | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host 'SMTP Connectivity Validation:' -foregroundcolor "green"

Test-SmtpConnectivity $Server | FT

write-host 'Transport Queue Status:' -foregroundcolor "green"

get-queue -server $Server | FT

}

if ($ServerRole -like '@{ServerRole=ClientAccess, HubTransport}'){write-host 'CAS\HT Multirole Server Detected...' -foregroundcolor "magenta"
write-host ""
write-host 'Service Status (Displays Services that are configured for Automatic startup but are in a stopped state):' -foregroundcolor "green"
Get-WmiObject Win32_Service -computername $Server |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } |
# process them; in this example we just show them:
Format-Table -AutoSize @(
    'DisplayName'
    @{ Expression = 'State'; Width = 9 }
    @{ Expression = 'StartMode'; Width = 9 }
    'StartName'
)
write-host "All 'Error' & 'Warning' Application log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName application | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host "All 'Error' & 'Warning' System log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName System | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host 'Active Client Connections:' -foregroundcolor "green"

get-casactiveusers -computername $Server

write-host 'IMAP Connectivity Validation:' -foregroundcolor "green"

If($IMAPEnabled -eq 'False'){Write-Host ""}

If($IMAPEnabled -eq 'True'){Test-ImapConnectivity -ClientAccessServer $Server | fl Result,scenariodescription}Else{'IMAP Service disabled, continuing to next test...'}

If($IMAPEnabled -eq 'False'){Write-Host ""}

write-host 'POP Connectivity Validation:' -foregroundcolor "green"

If($POPEnabled -eq 'False'){Write-Host ""}

If($POPEnabled -eq 'True'){Test-PopConnectivity -ClientAccessServer $Server | fl Result,ScenarioDescription}Else{'POP Service disabled, continuing to next test...'}

If($POPEnabled -eq 'False'){Write-Host ""}

write-host 'Outlook Web Services Connectivity Validation:' -foregroundcolor "green"

Test-OutlookWebServices -ClientAccessServer $Server | ft Type,Message -auto -wrap

write-host 'OWA Validation:' -foregroundcolor "green"

Test-OwaConnectivity -ClientAccessServer $Server | fl Result,URL,AuthenticationMethod,ScenarioDescription

write-host 'Web Services Validation:' -foregroundcolor "green"

Test-WebServicesConnectivity -ClientAccessServer $Server | ft Result,Scenario,ScenarioDescription -auto

write-host 'SMTP Connectivity Validation:' -foregroundcolor "green"

Test-SmtpConnectivity $Server | FT

write-host 'Transport Queue Status:' -foregroundcolor "green"

get-queue -server $Server | FT

}

if ($ServerRole -like '@{ServerRole=Mailbox, ClientAccess, HubTransport}'){write-host 'CAS\HT\MB Multirole Server Detected...' -foregroundcolor "magenta"
write-host ""
write-host 'Service Status (Displays Services that are configured for Automatic startup but are in a stopped state):' -foregroundcolor "green"
Get-WmiObject Win32_Service -computername $Server |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } |
# process them; in this example we just show them:
Format-Table -AutoSize @(
    'DisplayName'
    @{ Expression = 'State'; Width = 9 }
    @{ Expression = 'StartMode'; Width = 9 }
    'StartName'
)
write-host "All 'Error' & 'Warning' Application log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName application | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host "All 'Error' & 'Warning' System log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName System | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host 'Active Client Connections:' -foregroundcolor "green"

get-casactiveusers -computername $Server

write-host 'IMAP Connectivity Validation:' -foregroundcolor "green"

If($IMAPEnabled -eq 'False'){Write-Host ""}

If($IMAPEnabled -eq 'True'){Test-ImapConnectivity -ClientAccessServer $Server | fl Result,scenariodescription}Else{'IMAP Service disabled, continuing to next test...'}

If($IMAPEnabled -eq 'False'){Write-Host ""}

write-host 'POP Connectivity Validation:' -foregroundcolor "green"

If($POPEnabled -eq 'False'){Write-Host ""}

If($POPEnabled -eq 'True'){Test-PopConnectivity -ClientAccessServer $Server | fl Result,ScenarioDescription}Else{'POP Service disabled, continuing to next test...'}

If($POPEnabled -eq 'False'){Write-Host ""}

write-host 'Outlook Web Services Connectivity Validation:' -foregroundcolor "green"

Test-OutlookWebServices -ClientAccessServer $Server | ft Type,Message -auto -wrap

write-host 'OWA Validation:' -foregroundcolor "green"

Test-OwaConnectivity -ClientAccessServer $Server | fl Result,URL,AuthenticationMethod,ScenarioDescription

write-host 'Web Services Validation:' -foregroundcolor "green"

Test-WebServicesConnectivity -ClientAccessServer $Server | ft Result,Scenario,ScenarioDescription -auto

write-host 'SMTP Connectivity Validation:' -foregroundcolor "green"

Test-SmtpConnectivity $Server | FT

write-host 'Transport Queue Status:' -foregroundcolor "green"

get-queue -server $Server | FT

write-host 'DAG Node Status:' -foregroundcolor "green"

test-replicationhealth $Server | ft

write-host 'Database Status:' -foregroundcolor "green"

get-mailboxdatabasecopystatus -server $Server |select Identity,Status,ActiveDatabaseCopy,ContentIndexState,CopyQueueLength,ReplayQueueLength | sort Identity | ft

write-host 'MAPI Connectivity Validation:' -foregroundcolor "green"

Test-MAPIConnectivity -Server $Server | FT

write-host 'Mailflow Validation:' -foregroundcolor "green"

Test-mailflow -Identity $Server | FT TestMailflowResult,MessageLatencyTime

}

if ($ServerRole -like '@{ServerRole=Mailbox}'){write-host 'Dedicated Mailbox Server Detected...' -foregroundcolor "magenta"
write-host ""
write-host 'Service Status (Displays Services that are configured for Automatic startup but are in a stopped state):' -foregroundcolor "green"
Get-WmiObject Win32_Service -computername $Server |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } |
# process them; in this example we just show them:
Format-Table -AutoSize @(
    'DisplayName'
    @{ Expression = 'State'; Width = 9 }
    @{ Expression = 'StartMode'; Width = 9 }
    'StartName'
)
write-host "All 'Error' & 'Warning' Application log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName application | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host "All 'Error' & 'Warning' System log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName System | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host 'DAG Node Status:' -foregroundcolor "green"

test-replicationhealth $Server | ft

write-host 'Database Status:' -foregroundcolor "green"

get-mailboxdatabasecopystatus -server $Server |select Identity,Status,ActiveDatabaseCopy,ContentIndexState,CopyQueueLength,ReplayQueueLength | sort Identity | ft

write-host 'MAPI Connectivity Validation:' -foregroundcolor "green"

Test-MAPIConnectivity -Server $Server | FT

write-host 'Mailflow Validation:' -foregroundcolor "green"

Test-mailflow -Identity $Server | FT TestMailflowResult,MessageLatencyTime

}

if ($ServerRole -like '*EDGE*'){write-host 'Edge Server Detected...' -foregroundcolor "magenta"
write-host ""
write-host 'Service Status (Displays Services that are configured for Automatic startup but are in a stopped state):' -foregroundcolor "green"
Get-WmiObject Win32_Service -computername $Server |
Where-Object { $_.StartMode -eq 'Auto' -and $_.State -ne 'Running' } |
# process them; in this example we just show them:
Format-Table -AutoSize @(
    'DisplayName'
    @{ Expression = 'State'; Width = 9 }
    @{ Expression = 'StartMode'; Width = 9 }
    'StartName'
)
write-host "All 'Error' & 'Warning' Application log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName application | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host "All 'Error' & 'Warning' System log entries in the most recent 100 entries:" -foregroundcolor "green"

Get-EventLog -Newest 100 -ComputerName $Server -LogName System | where {($_.Entrytype -like 'error') -or ($_.EntryType -like 'warning')} | ft TimeGenerated,EntryType,Source,Message

write-host 'Transport Queue Status:' -foregroundcolor "green"

get-queue -server $Server | FT

}

stop-transcript

#$Body = Get-Content -Path C:\ESHC.txt | Out-String

    write-host ""
write-host "Would you like to send this report to the $SMTPRecipient address?" -foregroundcolor "yellow"
    write-host ""
    write-host "Y - Yes" -foregroundcolor "yellow"
    write-host "N - No" -foregroundcolor "red"
    write-host "" 
    write-host -nonewline "Type your choice and press Enter:" -foregroundcolor "yellow"
    $SendMail = read-host
    write-host ""
    $ok = @("Y","N","X") -contains $SendMail
    if ( -not $ok) { write-host "Invalid selection" }

if($SendMAil -eq 'Y'){send-mailmessage -From:$SMTPSender -To:$SMTPRecipient -SMTPServer:$SMTPServer -Subject:$Server' - Post-change Report' -attach $TranscriptPath -Body:"Server report"
write-host 'Report complete, returning to the command prompt...' -foregroundcolor "yellow"}
if($SendMail -eq 'N'){write-host 'Report complete, returning to the command prompt...' -foregroundcolor "yellow"}
    write-host ""

If for some reason the text above doesn’t work, you can download the file here (rename .txt to .ps1)

Leave a Reply

Your email address will not be published. Required fields are marked *