Hello Internet!!!

So it’s time for part two of the Exchange health script. This post will cover the script itself, and include a little module that I added for looking at network configuration.

Things this script is really good at!

– Checking to see if your mailflow functions properly
– Checking to see if you can access your various web services
– Creating a fancy report for your boss!
– Exporting all the relevant data so you can put it somewhere fancy!

This this script won’t help you with!

– That nasty cough you’ve had for a week… Seriously dude, work from home we don’t want that crap!
– Curing your Exchange servers syphilis. Tell your damn Exchange server to wrap it’s junk… I don’t care if the Notes server said it feels better without.
– Giving you super powers, unless that the super power you are looking for is the power to make on the fly color coded html tables of the status of your Exchange environment. (Can you tell that I’m really excited about the HTML coding… AND I’M not even releasing it today!)

So, now that we have that out of the way, let’s discuss the script! It is pretty basic, but has a few caveats that are worth mentioning. First, you should open a new fresh window of Exchange Management Shell. The modules don’t get cleared out properly or something, and it just doesn’t seem to work that well after it’s been run once. Second, this is only functional for Exchange 2013 right now, I am working on 2010 and 2007, but I have to build labs for them to get it all working properly, and I am kind of lazy/busy. Third! There are three things that it does automatically, if you don’t want these things happening, you will have to modify the script yourself. Those things are, turning on the ExchangeHM service and then turning it off on remote machines if it isn’t running for whatever reason, Sends an email to the address nic.holmes@mailinator.com, it is unmonitored, but if you want it sent somewhere else go ahead and modify it. And lastly, it will create the Extest_ account if it doesn’t exist… A lot of the tests don’t work properly without that account. If you don’t want that account, I would recommend not using this script as the script will largely not work without it.

Okay! So we’ve hit the point where the code goes!

Function Test-E15MailboxServer {
    Param(
        [string]$date,
        $mailServer
    )
    New-Item .\Results$date\Mailbox\$($mailServer.Name) -ItemType Directory
    #Basic Network Info
    if ((hostname) -notlike $mailServer.Name) {
        Get-NetworkInfo -serverName $mailServer.Name -serverType "Mailbox" -date $date
    }
    else {
        Get-NetworkInfoLocal -serverName $mailServer.Name -serverType "Mailbox" -date $date
    }

    #Health Checks
        
    Write-Host "Getting Basic Information about $mailServer Health."
    $actSyncHealth = Test-ActiveSyncConnectivity -MailboxServer $mailServer.Name
    $mRSHealth = Test-MRSHealth -Identity $mailServer.Name
    $assistHealth = Test-AssistantHealth -ServerName $mailServer.Name
    $searchHealth = Test-ExchangeSearch -Server $mailServer.Name
    $mAPIHealth = Test-MapiConnectivity -Server $mailServer.Name
    $replHealth = Test-ReplicationHealth -Identity $mailServer.Name
    #Note: The below email is one that exists and receives email, but isn't monitored... If you want to change it feel free!
    $mailFlowHealth = Test-Mailflow -TargetEmailAddress "nic.holmes@mailinator.com"
    $sMTPHealth = Test-SmtpConnectivity -Identity $mailServer.Name
    $replStatus = Get-MailboxDatabaseCopyStatus -Server $mailServer.Name
    $calConnect = Test-CalendarConnectivity -MailboxServer $mailServer.name
    $ecpConn = Test-EcpConnectivity -MailboxServer $mailServer.Name
    $imapConn = Test-ImapConnectivity -MailboxServer $mailServer.Name
    $popHealth = Test-PopConnectivity -MailboxServer $mailServer.Name
    $serviceHealth = Test-ServiceHealth -Server $mailServer.Name
    if ((Get-Service -ComputerName $mailServer.Name -Name MSExchangeHM).Status -eq "Running") {
        $outlookAnyHealth = Test-OutlookConnectivity -ProbeIdentity OutlookRPCDeepTestProbe -Hostname $mailServer.Name
        $mapiHttpHealth = Test-OutlookConnectivity -ProbeIdentity OutlookMapiHttpDeepTestProbe -Hostname $mailServer.Name
    }
    else {
        Invoke-Command -ComputerName $mailServer.Name -ScriptBlock { Start-Service -Name MSExchangeHM }
        $outlookAnyHealth = Test-OutlookConnectivity -ProbeIdentity OutlookRPCDeepTestProbe -Hostname $mailServer.Name
        $mapiHttpHealth = Test-OutlookConnectivity -ProbeIdentity OutlookMapiHttpDeepTestProbe -Hostname $mailServer.Name
        Invoke-Command -ComputerName $mailServer.Name -ScriptBlock { Stop-Service -Name MSExchangeHM }
    }
        
    #Gather Configuration Data

    Write-Host "Getting Configuration Info on $mailServer"
    $databases = Get-MailboxDatabase -Server $mailServer.Name
    $storeUsageStat = Get-StoreUsageStatistics -Server $mailServer.Name
    $mailboxes = Get-Mailbox -Server $mailServer.Name
    $mailboxStats = $mailboxes | Get-MailboxStatistics | Sort-Object -Property TotalItemSize -Descending
    $largeMailboxes = @($mailboxStats[0], $mailboxStats[1], $mailboxStats[2], $mailboxStats[3], $mailboxStats[4])

    #Outputting Data!
    Write-Host "Outputting Data about $mailServer" 

    "Mailbox Replication Health", $mRSHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt
    "Mailbox Assistant Health", $assistHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Search Health", $SearchHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "MAPI Health", $mAPIHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Replication Health", $replHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Mailflow Health Check", $mailFlowHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "SMTP Send receive health", $sMTPHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Replication Status", $replStatus | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Calendar Health", $calConnect | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Exchange Control Panel Health", $ecpConn | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "IMAP Health", $imapConn | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "OWA Health", $owaHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "POP3 Health", $popHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Service Health", $serviceHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "Outlook Anywhere Health", $outlookAnyHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append
    "MAPI over HTTP Health", $mapiHttpHealth | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\HealthCheck.txt -Append

    #Mailbox data

    $databases | Export-Csv -Path .\Results$date\Mailbox\$($mailServer.Name)\databases.csv
    $storeUsageStat | Out-File -FilePath .\Results$date\Mailbox\$($mailServer.Name)\usage.txt
    $mailboxes | Export-Csv -Path .\Results$date\Mailbox\$($mailServer.Name)\mailboxes.csv
    $largeMailboxes | Export-Csv -Path .\Results$date\Mailbox\$($mailServer.Name)\largemailboxes.csv

    #Writing Event Logs

    $eventLogs = Get-EventLog -LogName Application -ComputerName $($mailServer.Name) -Source MSExchange*
    $eventLogs | Export-Csv -Path .\results$date\Mailbox\$($mailServer.name)\eventlog.csv

    #Creating HTML report

    New-HTMLReport -mRSHealth $mRSHealth -assistHealth $assistHealth -mAPIHealth $mAPIHealth -replHealth $replHealth -mailFlowHealth $mailFlowHealth -sMTPHealth $sMTPHealth -replStatus $replStatus -calConnect $calConnect -ecpConn $ecpConn -imapConn $imapConn -popHealth $popHealth -serviceHealth $serviceHealth -outlookAnyHealth $outlookAnyHealth -mAPIHttpHealth $mapiHttpHealth -actSyncHealth $actSyncHealth -date $date -serverName $mailServer.Name -serverType "Mailbox" 

}

Function Test-E15ClientAccessServer {
    Param(
        [string]$date,
        $cAServer
    )
    New-Item .\results$date\CAS\$($caServer.Name) -ItemType Directory

    #Basic Network Info
    if ((hostname) -notlike $caServer.Name) {
        Get-NetworkInfo -serverName $caServer.Name -serverType "CAS" -date $date
    }
    else {
        Get-NetworkInfoLocal -serverName $caServer.Name -serverType "CAS" -date $date
    }

    #Checking for the EXTest_Account, if it doesn't exist it creates it and generates a random password
    Write-Host "Looking for EXtest Account."
    $ErrorActionPreference = 'silentlycontinue'
    if ((Get-Mailbox "*extest*") -eq $null) {
        Write-Host -ForegroundColor Red "Unable to find EXTest account! Creating it now."
        $ascii = $NULL
        For ($a = 33; $a –le 126; $a++) {
            $ascii += , [char][byte]$a 
        }
        For ($loop = 1; $loop –le 20; $loop++) {
            $TempPassword += ($ascii | Get-Random)
        }
        $ExPass = ConvertTo-SecureString $TempPassword -AsPlainText -Force
        $currentLoc = Get-Location
        Set-Location $env:ExchangeInstallPath\scripts
        .\new-TestCasConnectivityUser.ps1 -Password $ExPass
        Set-Location $currentLoc
    }
    $ErrorActionPreference = 'continue'

    #Health Checks

    Write-Host "Getting Basic information about $caServer Health"
    $actSyncHealth = Test-ActiveSyncConnectivity -ClientAccessServer $cAServer.Name
    $calConnect = Test-CalendarConnectivity -ClientAccessServer $cAServer.name
    $ecpConn = Test-EcpConnectivity -ClientAccessServer $cAServer.Name
    $imapConn = Test-ImapConnectivity -ClientAccessServer $cAServer.Name
    $popHealth = Test-PopConnectivity -ClientAccessServer $cAServer.Name
    if ((Get-Service -ComputerName $cAServer.Name -Name MSExchangeHM).Status -eq "Running") {
        $outlookAnyHealth = Test-OutlookConnectivity -ProbeIdentity OutlookRpcCtpProbe -Hostname $cAServer.Name
        $mapiHttpHealth = Test-OutlookConnectivity -ProbeIdentity OutlookMapiHttpCtpProbe -Hostname $cAServer.Name
    }
    else {
        Invoke-Command -ComputerName $cAServer.Name -ScriptBlock { Start-Service -Name MSExchangeHM }
        $outlookAnyHealth = Test-OutlookConnectivity -ProbeIdentity OutlookRpcCtpProbe -Hostname $cAServer.Name
        $mapiHttpHealth = Test-OutlookConnectivity -ProbeIdentity OutlookMapiHttpCtpProbe -Hostname $cAServer.Name
        Invoke-Command -ComputerName $cAServer.Name -ScriptBlock { Stop-Service -Name MSExchangeHM }
    }
    $serviceHealth = Test-ServiceHealth -Server $cAServer.Name

    #Get information

    $actSyncVirDir = Get-ActiveSyncVirtualDirectory -Server $cAServer.Name
    $eCPVirDir = Get-EcpVirtualDirectory -Server $cAServer.Name
    $mapiVirDir = Get-MapiVirtualDirectory -Server $cAServer.Name
    $oABVirDir = Get-OabVirtualDirectory -Server $cAServer.Name
    $oWAVirDir = Get-OwaVirtualDirectory -Server $cAServer.Name
    $eWSVirDir = Get-WebServicesVirtualDirectory -Server $cAServer.Name
    $outlookAnywhereVirDir = Get-OutlookAnywhere -Server $cAServer.Name

    #Output data
    Write-Host "Outputting Data for $cAServer"
        
    "ActiveSync Health", $actSyncHealth | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt
    "Calendar Connectivity Health", $calConnect | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt -Append
    "Exchange Control Panel Health", $ecpConn | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt -Append
    "IMAP Connectivity", $imapConn | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt -Append
    "POP3 Connectivity check", $popHealth | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt -Append
    "Outlook Anywhere Health Check", $outlookAnyHealth | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt -Append
    "MAPI over HTTP Health Check", $mapiHttpHealth | Out-File .\results$date\CAS\$($cAServer.name)\Health.txt -Append

    #Outputting Virutal Directories

    "ActiveSync Virtual Directory", $actSyncVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt 
    "Exchange Control Panel Directory", $eCPVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt -Append
    "MAPI over HTTP Directory", $mapiVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt -Append
    "Offline Address Book Virtual Directory", $oabVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt -Append
    "Outlook Web Acess Virtual Directory", $oWAVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt -Append
    "Exchange Web Services Virtual Directory", $eWSVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt -Append
    "Outlook Anywhere Virtual Directory", $outlookAnywhereVirDir | Out-File .\results$date\CAS\$($cAServer.name)\VirDir.txt -Append

    #Writing Event Logs

    $eventLogs = Get-EventLog -LogName Application -ComputerName $cAServer.Name -Source MSExchange*
    $eventLogs | Export-Csv -Path .\results$date\CAS\$($cAServer.name)\eventlog.csv

    #Creating HTML report

    New-HTMLReport -calConnect $calConnect -ecpConn $ecpConn -imapConn $imapConn -popHealth $popHealth -outlookAnyHealth $outlookAnyHealth -mAPIHttpHealth $mapiHttpHealth -actSyncHealth $actSyncHealth -date $date -serverName $caServer.Name -serverType "CAS" 

}

Function Get-E15OrganizationInfo {
    Param(
        [string]$date
    )

    #Creating Org Directory
    New-Item .\results$date\OrgConfig -ItemType Directory

    #Collecting Org Data
    $owaMailPol = Get-OwaMailboxPolicy
    $activeSyncMailPol = Get-MobileDeviceMailboxPolicy
    $addressPol = Get-AddressBookPolicy
    $emailPol = Get-EmailAddressPolicy
    $malFiltPol = Get-MalwareFilterPolicy
    $tipPol = Get-PolicyTipConfig
    $retenPol = Get-RetentionPolicy
    $retenPolTag = Get-RetentionPolicyTag
    $sharePol = Get-SharingPolicy
    $uMMailPol = Get-UMMailboxPolicy
    $mailboxes = Get-Mailbox -ResultSize Unlimited
    $mailboxSize += ($mailboxes | Get-MailboxStatistics).TotalItemSize.Value
    $mailboxSize | % { $totalSize = $totalSize + $_ }
    $actSyncDev = $mailbox | Get-MobileDevice
    $dagConfig = Get-DatabaseAvailabilityGroup 
    $dagInfo = Get-DatabaseAvailabilityGroupConfiguration
    $pubFoldDB = Get-PublicFolderDatabase
    $pubFolder = Get-PublicFolder -GetChildren

    #Writing Org info

    "OWA Mailbox Policy", $owaMailPol | Out-File .\results$date\OrgConfig\Policy.txt
    "ActiveSync Device Policy", $activeSyncMailPol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Address Book Policy", $addressPol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Email Address Polict", $emailPol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Malware Filter Policy", $mailFiltPol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Policy Tip Config", $tipPol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Retention Policy", $retenPol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Retention Policy Tags", $retenPolTag | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "Sharing Policy", $sharePol | Out-File .\results$date\OrgConfig\Policy.txt -Append
    "UM Mailbox Policy", $uMMailPol | Out-File .\results$date\OrgConfig\Policy.txt -Append

    #All Org appliances

    $mailboxes | Export-Csv -Path .\results$date\OrgConfig\AllMailboxes.csv
    "Total size of all Mailboxes", $totalSize | Out-File .\results$date\OrgConfig\totalMailboxSize.txt
    "Database Availability Group Info", $dagConfig | Out-File .\results$date\OrgConfig\DAG.txt
    "Database Availability Group Config", $dagInfo | Out-File .\results$date\OrgConfig\DAG.txt -Append
    "Public Folder Database", $pubFoldDB | Out-File .\results$date\OrgConfig\PublicFolder.txt
    "Public Folders", $pubFolder | Out-File .\results$date\OrgConfig\Publicfolder.txt -Append

}

Also, the module for get-networkinfo.psm1 is as follows:

Function Get-NetworkInfo {
    Param(
        [string]$serverName,
        [string]$serverType,
        [string]$date
    )
    Write-Host "Gathering Network Information for $serverName"
    $netConfig = Invoke-Command -ComputerName $serverName -ScriptBlock { ipconfig /all }
    $ping = $null
    $ping = Invoke-Command -ComputerName $serverName -ScriptBlock { ping google.com }
    $tracert = $null
    if ($ping -like "*Reply from*") {
        $tracert = $ping = Invoke-Command -ComputerName $serverName -ScriptBlock { tracert google.com }
    }
    else {
        $pingError = "No available connection to the internet for $serverName !"
        Write-Host -ForegroundColor Red "No available connection to the internet!"
    }
    Write-Host "Outputting Information on $serverName to file"
    "IP Config", $netConfig | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt
    "Ping Results", $ping | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt -Append
    if ($tracert) {
        "Trace Route", $tracert | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt -Append
    }
    else {
        $pingError | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt -Append
    }
}

Function Get-NetworkInfoLocal {
    Param(
        [string]$serverName,
        [string]$serverType,
        [string]$date
    )
    Write-Host "Gathering Network Information for $serverName"
    $netConfig = ipconfig /all
    $ping = $null
    $ping = ping google.com
    $tracert = $null
    if ($ping -like "*Reply from*") {
        $tracert = tracert google.com
    }
    else {
        $pingError = "No available connection to the internet for $serverName !"
        Write-Host -ForegroundColor Red "No available connection to the internet!"
    }
    Write-Host "Outputting Information on $serverName to file"
    "IP Config", $netConfig | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt
    "Ping Results", $ping | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt -Append
    if ($tracert) {
        "Trace Route", $tracert | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt -Append
    }
    else {
        $pingError | Out-File .\Results$date\$serverType\$serverName\NetConfig.txt -Append
    }
}
Expand

So as you can see from the above code, it’s largely just collecting the data and putting it into variables, then outputting that data to a relative file path.

Check in tomorrow for the HTML report portion! And then check on Monday for the final piece which will include a download link to allow all the relative paths to work properly!

As always, if you have any questions, please feel free to comment below!

Leave a Reply

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