0

Powershell – Exchange Environmental Check

I have adapted the “Modern Exchange Environment Check” from Satheshwaran Manoharan and for use on a Exchange Server 2016 environment.
Added to the already excelent script is the following

  • Section Headers
  • MailboxDB provisioning Status
  • Whitespace check
  • Exchange Log Dir Size
  • Exchange Server Drive Space
  • Mail Queue Checks
  • House Keeping of created files


I feel that this is now a more complete report of the Exchange Server environment and the servers it runs upon.

I had toyed with adding the “10 largest user Mailboxes” to the account, but this takes a considerable time to run and would be better suited to ad-hoc reporting.

# Requires -version 2 - Runs in Exchange Management Shell

# It displays Complete Exchange Environment Information in a modern HTML. It covers only Exchange 2010 or later.

# Set Working Dir
Push-Location (Split-Path -path $MyInvocation.MyCommand.Definition -Parent)

#Add Exchange Server snapin if not already loaded

if (!(Get-PSSnapin | where {$_.Name -eq "Microsoft.Exchange.Management.PowerShell.E2010"}))
{
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue
}
Write-Progress -Activity "Date" -status "Collecting Dates info"
$Date = Get-date -ErrorAction SilentlyContinue

# ----- Settings ----

#Should the Script Open the HTML File locally on Finishing the script - Say Yes - if you wish to

$Openhtmllocally = "No"

Write-Progress -Activity "Email Settings" -status "Storing Email Settings"
## ----- Email ----Fill in with your details

$EmailTo = "[email protected]"
$EmailFrom = "[email protected]"
$EmailSubject = "Exchange Status Report $Date"
$SmtpServer = "mail.yourdomain.com"
$Date = (Get-Date -format "dd-MM-yyyy")
$Attachment = ".\output\ModernExchangeEnvironmentReport($date).htm"

# ----- Settings ----

#Collecting Environment Information
Write-Progress -Activity "Exchange Servers" -status "Collecting Exchange Servers info"
$ExchangeServers = Get-ExchangeServer -ErrorAction SilentlyContinue

Write-Progress -Activity "Mailboxes" -status "Collecting Collecting Mailboxes info"
$mailboxes = Get-mailbox -ResultSize Unlimited -ErrorAction SilentlyContinue

Write-Progress -Activity "Databases" -status "Collecting Databases info"
$Databases = Get-mailboxdatabase -Status -ErrorAction SilentlyContinue

Write-Progress -Activity "DAG" -status "Collecting DAG info"
$DAGS = Get-DatabaseAvailabilityGroup -ErrorAction SilentlyContinue

Write-Progress -Activity "DistributionGroups" -status "Collecting DistributionGroup info"
$DistributionGroups = Get-DistributionGroup -ResultSize Unlimited -ErrorAction SilentlyContinue

Write-Progress -Activity "DynamicDistributionGroups" -status "Collecting DynamicDistributionGroup info"
$DynamicGroups = Get-DynamicDistributionGroup -ResultSize Unlimited -ErrorAction SilentlyContinue

Write-Progress -Activity "MailContacts" -status "Collecting MailContacts info"
$MailContacts = Get-MailContact -ResultSize Unlimited -ErrorAction SilentlyContinue

Write-Progress -Activity "Public Folder" -status "Collecting Public Folder Mailboxes info"
$PFmailboxes = Get-mailbox -PublicFolder -ResultSize Unlimited -ErrorAction SilentlyContinue

Write-Progress -Activity "SendConnectors" -status "Collecting SendConnectors info"
$SendConnectors = Get-SendConnector -ErrorAction SilentlyContinue

Write-Progress -Activity "Accepted Domains" -status "Collecting Accepted Domains info"
$AcceptedDomains = Get-AcceptedDomain -ErrorAction SilentlyContinue

Write-Progress -Activity "OrgAdmins" -status "Collecting OrgAdmins info"
$OrgAdmins = Get-RoleGroupMember "Organization Management" -ErrorAction SilentlyContinue

#Applying Initial CSS For the HTML
Write-Progress -Activity "ModernExchangeEnvironmentReport" -status "Applying CSS"
$head = @"
<Title>Volume Report</Title>
<Style>
.CSSTableGenerator {
margin:0px;padding:0px;
width:100%;
box-shadow: 10px 10px 5px #888888;
border:1px solid #7aa3c1;

-moz-border-radius-bottomleft:0px;
-webkit-border-bottom-left-radius:0px;
border-bottom-left-radius:0px;

-moz-border-radius-bottomright:0px;
-webkit-border-bottom-right-radius:0px;
border-bottom-right-radius:0px;

-moz-border-radius-topright:0px;
-webkit-border-top-right-radius:0px;
border-top-right-radius:0px;

-moz-border-radius-topleft:0px;
-webkit-border-top-left-radius:0px;
border-top-left-radius:0px;
}.CSSTableGenerator table{
border-collapse: collapse;
border-spacing: 0;
width:100%;
height:100%;
margin:0px;padding:0px;
}.CSSTableGenerator tr:last-child td:last-child {
-moz-border-radius-bottomright:0px;
-webkit-border-bottom-right-radius:0px;
border-bottom-right-radius:0px;
}
.CSSTableGenerator table tr:first-child td:first-child {
-moz-border-radius-topleft:0px;
-webkit-border-top-left-radius:0px;
border-top-left-radius:0px;
}
.CSSTableGenerator table tr:first-child td:last-child {
-moz-border-radius-topright:0px;
-webkit-border-top-right-radius:0px;
border-top-right-radius:0px;
}.CSSTableGenerator tr:last-child td:first-child{
-moz-border-radius-bottomleft:0px;
-webkit-border-bottom-left-radius:0px;
border-bottom-left-radius:0px;
}.CSSTableGenerator tr:hover td{

}
.CSSTableGenerator tr:nth-child(odd){ background-color:#ffffff; }
.CSSTableGenerator tr:nth-child(even){ background-color:#ffffff; }
.CSSTableGenerator td{
vertical-align:middle;
border:1px solid #7aa3c1;
border-width:0px 1px 1px 0px;
text-align:center;
padding:8px;
font-size:10px;
font-family:Arial;
font-weight:normal;
color:#000000;
}.CSSTableGenerator tr:last-child td{
border-width:0px 1px 0px 0px;
}.CSSTableGenerator tr td:last-child{
border-width:0px 0px 1px 0px;
}.CSSTableGenerator tr:last-child td:last-child{
border-width:0px 0px 0px 0px;
}
.CSSTableGenerator tr:first-child td{
background:-o-linear-gradient(bottom, #0072c6 5%, #0072c6 100%);
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #0072c6), color-stop(1, #0072c6) );
background:-moz-linear-gradient( center top, #0072c6 5%, #0072c6 100% );
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#0072c6", endColorstr="#0072c6");
background: -o-linear-gradient(top,#0072c6,0072c6);
background-color:#0072c6;
border:0px solid #7aa3c1;
text-align:center;
border-width:0px 0px 1px 1px;
font-size:14px;
font-family:Trebuchet MS;
font-weight:bold;
color:#ffffff;
}
.CSSTableGenerator tr:first-child:hover td{
background:-o-linear-gradient(bottom, #0072c6 5%, #0072c6 100%);
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #0072c6), color-stop(1, #0072c6) );
background:-moz-linear-gradient( center top, #0072c6 5%, #0072c6 100% );
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#0072c6", endColorstr="#0072c6");
background: -o-linear-gradient(top,#0072c6,0072c6);
background-color:#0072c6;
}
.CSSTableGenerator tr:first-child td:first-child{
border-width:0px 0px 1px 0px;
}
.CSSTableGenerator tr:first-child td:last-child{
border-width:0px 0px 1px 1px;
}
h1 {
font-size:14px;
font-family:Trebuchet MS;
font-weight:bold;
font-color:#7aa3c1
}
</Style>
"@

#Create Tables in split so that users can remove or add tables easily.
$start = @"
<html>

<div class="CSSTableGenerator">
"@
Write-Progress -Activity "ModernExchangeEnvironmentReport" -status "Writing Enviroment Initial Info"
$Table1 =@"
<H1>Exchange Environment Report</h1>
<table>
<tr><td>No.Exchange Servers</td>
<td>No.Databases</td>
<td>No.Mailboxes</td>
<td>No.PublicFolder Mailboxes</td>
<td>No.DistributionGroups</td>
<td>No.DynamicGroups</td>
<td>No.Contacts</td>
<td>No.DAG</td>
<td>No.Domains</td>
<td>No.OrgAdmins</td>
<td>Date</td>
</tr>
<tr><td>$($ExchangeServers.count)</td>
<td>$($Databases.count)</td>
<td>$($mailboxes.count)</td>
<td>$($PFmailboxes.count)</td>
<td>$($DistributionGroups.count)</td>
<td>$($DynamicGroups.count)</td>
<td>$($MailContacts.count)</td>
<td>$($DAG.count)</td>
<td>$($AcceptedDomains.count)</td>
<td>$($OrgAdmins.count)</td>
<td>$($Date)</td>
</tr>
</table>

<table>
<tr><td>Exchange Servers</td>
<td>Roles</td>
<td>Edition</td>
<td>Site</td>
<td>Version</td>
<td>Operating System</td>
<td>SendConnectors Used</td>
</tr>
"@

Write-Progress -Activity "ExchangeServers" -status "Writing ExchangeServers info"
$Table2 =
foreach ($server in ($ExchangeServers))
{"<tr><td>$($server.name)</td>
<td>$($server.serverrole)</td>
<td>$($server.edition)</td>
<td>$($server.site.name)</td>
<td>$($server.AdmindisplayVersion -replace "version",'')</td>
<td>
$($windows2012above = ((Get-WmiObject -ComputerName $server.name -class Win32_OperatingSystem -ErrorAction SilentlyContinue) | Where-Object{($_.Version -like "6.*") -or ($_.version -like "10.*") -and ($_.Version -notlike "6.1.*") -and ($_.version -notlike "6.0.*")}).version.count
if ($windows2012above -eq 1)
{(Get-CimInstance -ComputerName $server.name Win32_OperatingSystem).caption -replace "Microsoft Windows Server",''})</td>
<td>$(($SendConnectors | Where-Object{$_.SourceTransportServers -match "$($server.name)"}).identity.name)</td>
</tr>"}

$table2close = @"
</table>

"@

Write-Progress -Activity "Databases" -status "Writing Database info"
$Table3 =@"
<h1>Mailbox DB Stats</h1>
<table>
<tr><td>Mailbox Databases</td>
<td>Mounted</td>
<td>ContentIndex</td>
<td>DBSize</td>
<td>WhiteSpace</td>
<td>Mailboxes</td>
<td>Master Group</td>
<td>Database copies</td>
<td>MbxRetention.Days</td>
<td>ItemRetention.Days</td>
<td>CircularLogging</td>
<td>Provisioning Suspended</td>
<td>Provisioning Disabled</td>
<td>Last Full Backup</td>
</tr>
"@

$Table3data = foreach ($Database in $Databases)
{
$dbsize = $($Database.databasesize -replace "\(.*",'')

$dbWhitespace = $($database.AvailableNewMailboxSpace -replace "\(.*",'')

$Mountstatus = $($Database.Mounted)

if($Mountstatus -eq "True")
{
$Mcolor = "#99FF66"
}
else
{
$Mcolor = "#FF5050"
}

$ContentIndexStatus = $((Get-MailboxDatabaseCopyStatus "$($database.name)\$($database.servername)").contentindexstate)
if($ContentIndexStatus -eq "Healthy")
{
$color = "#99FF66"
}
else
{
$color = "#FF5050"
}

"<tr><td>$($Database.name)</td>
<td bgcolor = $Mcolor>$($Database.Mountedonserver)</td>
<td bgcolor = $color>$ContentIndexStatus</td>
<td>$dbsize</td>
<td bgcolor = $WSColor>$dbWhitespace</td>
<td>$((get-mailbox -database $Database).count)</td>
<td>$($Database.MasterServerOrAvailabilityGroup)</td>
<td>$($Database.databasecopies.identity.name)</td>
<td>$($Database.MailboxRetention.days)</td>
<td>$($Database.DeletedItemRetention.days)</td>
<td>$($Database.CircularLoggingEnabled -replace "False","No" -replace "True","Yes")</td>
<td>$($database.IsSuspendedFromProvisioning -replace "False","No" -replace "True","Yes")</td>
<td>$($database.IsExcludedFromProvisioning -replace "False","No" -replace "True","Yes")</td>
<td>$($database.lastfullbackup)</td>
</tr>"
}

$table3close = @"
</table>

"@

if($dags.count -ge 1)
{
Write-Progress -Activity "Dag" -status "Writing DaG info"
$Table4 =@"
<h1>DAG statistics</h1>
<table>
<tr><td>DAG Name </td>
<td>Member Servers</td>
<td>DAC Mode</td>
<td>Witness server</td>
<td>Witness Directory</td>
</tr>
"@

$Table4data = foreach ($dag in $dags)
{"<tr><td>$($DAG.name)</td>
<td>$($dag.servers.name)</td>
<td>$($dag.DatacenterActivationMode)</td>
<td>$($dag.Witnessserver)</td>
<td>$($dag.WitnessDirectory)</td>
</tr>"
}

$table4close = @"
</table>

"@

Write-Progress -Activity "Dag" -status "Writing DaG Replication info"
$Table5 =@"
<h1>Server Statistics</h1>
<table>
<tr><td>Server</td>
<td>Check</td>
<td>Result</td>
<td>Error</td>
</tr>
"@

Write-Progress -Activity "Dag" -status "Writing DaG info"

$Table5data = foreach ($dag in $dags)
{
foreach ($member in $((Get-DatabaseAvailabilityGroup $dag).servers.name))
{
$replstatus = Test-ReplicationHealth $member
for($i=0;$i -lt $replstatus.count;$i++)
{
if($replstatus.result[$i].value -eq "Passed")
{
$Rcolor = "#99FF66"
}
else
{
$Rcolor = "#FF5050"
}
"<tr><td>$(($replstatus).Server[$i])</td>
<td>$(($replstatus).Check[$i])</td>
<td bgcolor = $Rcolor>$($replstatus.Result[$i].value)</td>
<td>$(($replstatus).Error[$i])</td>
</tr>"
}
}
}

$table5close = @"
</table>

"@
}
else
{
Write-Progress -Activity "Dag" -status "Skipping DaG - No DAG Found"
}

$MailqueueTb= @"
<h1>Mail Queues</h1>
<table>
<tr><td>Next Hop Domain</td>
<td>Status</td>
<td>Message Count</td>
<td>Freespace (GB)</td>
</tr>

"@

Write-Progress -Activity "Mail Queues" -status "Perusing the Mail Queues"

Foreach ($server in $ExchangeServers){
$MailQueues += Get-Queue -server $server | Select-Object NextHopDomain, Status, @{Label="Message Count";Expression={if ($_.MessageCount -lt 30){"#div_gre"+ $_.MessageCount}else{"#div_red"+ $_.MessageCount}}}, Identity
}
$MailQueues = $MailQueues|ConvertTo-Html -Fragment
$file = ".\MailQueues.txt"
$MailQueues | SC $file
(GC $file|Select-Object -Skip 3)|SC $file
$MailQueues = GC $file
$MailQueues = $MailQueues -replace '<td>#div_red', '<td bgcolor = red>'
$MailQueues = $MailQueues -replace '<td>#div_gre', '<td bgcolor = #99FF66>'
Remove-Item $file

$table6= @"
<h1>Server Drive Space Statistics</h1>
<table>
<tr><td>Server</td>
<td>Volume</td>
<td>Capacity (GB)</td>
<td>Freespace (GB)</td>
<td>Percent Free</td>
</tr>

"@

Write-Progress -Activity "Drive Space" -status "Writing Drive Space info"
$threshold='90'
Write-Verbose -Message "===================================`n= DISK SPACE CHECKS =`n= Checks the size of volumes, including =`n= mount points, and shows those with =`n= less than $threshold% free. =`n============================================"
$thresholdMsg = "<div class='threshold'>Drive(s) listed below have less than $threshold% free space. Drives above this threshold will not be listed.</div><br />"
$onlinelist = @()
$offlinelist = @()
$results = @()
Write-Verbose -Message 'Performing ping test on servers ...'
$exchangeservers|ForEach-Object -Process {
if(Test-Connection -ComputerName $_ -Quiet -Count 2)
{
Write-Verbose -Message "`t$_ is up"
$onlinelist += $_
}
else
{
Write-Warning -Message "`t`t`t`t$_ is down"
$offlinelist += $_
}
}

if($offlinelist -ne $null)
{
$ofs = ','
$offlineMsg = "<div class='offline'>The following servers are offline: $offlinelist</div><br />"
$ofs = ' '
}
ForEach($server in $onlinelist)
{
Try
{
Write-Verbose -Message "Gathering drive info on $server"
$row = Get-WmiObject -ComputerName $server -Class Win32_Volume `
|
Where-Object -FilterScript {
$_.DriveType -eq '3'
} `
|
Where-Object -FilterScript {
$_.Capacity -gt 0
} `
|
Where-Object -FilterScript {
($_.freespace/$_.capacity)*100 -lt $threshold
} `
|
Select-Object -Property SystemName, Name, @{
n = 'Capacity (GB)'
e = {
'{0:n2}' -f ($_.capacity/1gb)
}
}, @{
n = 'FreeSpace (GB)'
e = {
'{0:n2}' -f ($_.freeSpace/1gb)
}
}, @{
n = 'PercentFree'
e = {
if('{0:n2}' -f (($_.freespace/$_.capacity)*100) -lt 10)
{
"#div_red"+'{0:n2}' -f ($_.freespace/$_.capacity*100)
}
elseif('{0:n2}' -f (($_.freespace/$_.capacity)*100) -lt 15)
{
"#div_ora"+'{0:n2}' -f ($_.freespace/$_.capacity*100)
}
elseif('{0:n2}' -f (($_.freespace/$_.capacity)*100) -lt 20)
{
"#div_yel"+'{0:n2}' -f (($_.freespace/$_.capacity)*100)
}
elseif('{0:n2}' -f ($_.freespace/$_.capacity*100) -gt 20)
{
"#div_gre"+'{0:n2}' -f (($_.freespace/$_.capacity)*100)
}
}
}
}
Catch [System.Exception]
{
Write-Warning -Message $_.Exception.Message
}
$results += $row
}
$results = $results|ConvertTo-Html -Fragment
$file = ".\results.txt"
$results | SC $file
(GC $file|Select-Object -Skip 3)|SC $file
$results = GC $file
$results = $results -replace '<td>#div_yel', '<td bgcolor = yellow>'
$results = $results -replace '<td>#div_ora', '<td bgcolor = orange>'
$results = $results -replace '<td>#div_red', '<td bgcolor = red>'
$results = $results -replace '<td>#div_gre', '<td bgcolor = #99FF66>'
Remove-Item $file

$table6close=@"

"@

Write-Progress -Activity "Log Sizes" -status "Writing Log Size info"
$table7=@"
<h1>Exchange Log Sizes</h1>
<table>
<tr><td>Log Path</td>
<td>Size MB</td>
</tr>
"@

$trupath = @()
$databases | ForEach-ObJect{
$logpath = $_.LogFolderPath.ToString()
$logserver = $_.Servername.ToString()
$trupath += $logpath -replace "C:\", "\\$logserver\c$"
}

$table7data=ForEach($pat in $trupath){
$size = "{0:N2} MB" -f ((GCI $pat -recurse |Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1MB)
"<tr><td>$Pat</td>
<td>$size</td>
</tr>"
}

$table7close=@"
</table>
"@

$complete = @"
</div></html>
"@

#Combining All Tables.

$alltables = "$start $Table1 $Table2 $table2close $Table3 $Table3data $table3close $Table4 $Table4data $table4close $Table5 $Table5data $table5close $mailqueuetb $mailqueues $table6 $table6close $results $Table7 $Table7data $table7close "

$Combine = ConvertTo-Html -Head $head -Body $alltables

#Saving HTML File To the local C Drive - You can modify as per your wish.
$html += $Combine
$html > $Attachment

#open the HTML File Locally
If($Openhtmllocally -eq "yes")
{
Invoke-Item "$Attachment"
}
else
{
Write-Progress -Activity "Open HTML" -status "Settings - Do not Open"
}
#Sending Email Message

#Send-mailmessage -to $EmailTo -from $EmailFrom -subject $EmailSubject -SmtpServer $SmtpServer -Body "Please see attached" -BodyAsHtml -Attachments $Attachment -ErrorAction SilentlyContinue

# House Keping
GCI .\output\ | Where-Object -FilterScript {
$_.LastWriteTime -lt (Get-Date).AddDays(-6)}|Remove-Item

Niall

Leave a Reply

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