mirror of https://github.com/lework/script
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1454 lines
54 KiB
1454 lines
54 KiB
4 years ago
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates an HTML report describing the On-Premises Exchange environment.
|
||
|
|
||
|
Thomas Stensitzki
|
||
|
|
||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
|
||
|
RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||
|
|
||
|
Version 2.2 January 2020
|
||
|
|
||
|
Based on the original 1.6.2 version by Steve Goodman
|
||
|
|
||
|
Ideas, comments and suggestions to support@granikos.eu
|
||
|
|
||
|
.DESCRIPTION
|
||
|
|
||
|
This script creates an HTML report showing the following information about an Exchange
|
||
|
2019, 2016, 2013, 2010, and, to a lesser extent, 2007 and 2003 environment.
|
||
|
|
||
|
The reports shows the following:
|
||
|
|
||
|
* Report Generation Time
|
||
|
* Total Servers per Exchange Version (2003 > 2010 or 2007 > 2019)
|
||
|
* Total Mailboxes per Exchange Version, Office 365, and Organisation
|
||
|
* Total Roles in the environment
|
||
|
|
||
|
Then, per site:
|
||
|
* Total Mailboxes per site
|
||
|
* Internal, External and CAS Array Hostnames
|
||
|
* Exchange Servers with:
|
||
|
o Exchange Server Version
|
||
|
o Service Pack
|
||
|
o Number of preferred and maximum active databases
|
||
|
o Update Rollup and rollup version
|
||
|
o Roles installed on server and mailbox counts
|
||
|
o OS Version and Service Pack
|
||
|
|
||
|
Then, per Database availability group (Exchange 2010/2013/2016/2019):
|
||
|
* Total members per DAG
|
||
|
* Member list
|
||
|
* Databases, detailing:
|
||
|
o Mailbox Count and Average Size
|
||
|
o Archive Mailbox Count and Average Size (Only shown if DAG includes Archive Mailboxes)
|
||
|
o Database Size and whitespace
|
||
|
o Database and log disk free
|
||
|
o Last Full Backup (Only shown if one or more DAG database has been backed up)
|
||
|
o Circular Logging Enabled (Only shown if one or more DAG database has Circular Logging enabled)
|
||
|
o Mailbox server hosting active copy
|
||
|
o List of mailbox servers hosting copies and number of copies
|
||
|
|
||
|
Finally, per Database (Non DAG DBs/Exchange 2007/Exchange 2003)
|
||
|
* Databases, detailing:
|
||
|
o Storage Group (if applicable) and DB name
|
||
|
o Server hosting database
|
||
|
o Mailbox Count and Average Size
|
||
|
o Archive Mailbox Count and Average Size (Only shown if DAG includes Archive Mailboxes)
|
||
|
o Database Size and whitespace
|
||
|
o Database and log disk free
|
||
|
o Last Full Backup (Only shown if one or more DAG database has been backed up)
|
||
|
o Circular Logging Enabled (Only shown if one or more DAG database has Circular Logging enabled)
|
||
|
|
||
|
This does not detail public folder infrastructure, or examine Exchange 2007/2003 CCR/SCC clusters
|
||
|
(although it attempts to detect Clustered Exchange 2007/2003 servers, signified by ClusMBX).
|
||
|
|
||
|
IMPORTANT NOTE: The script requires WMI and Remote Registry access to Exchange servers from the server
|
||
|
it is run from to determine OS version, Update Rollup, Exchange 2007/2003 cluster and DB size information.
|
||
|
|
||
|
.LINK
|
||
|
http://scripts.granikos.eu
|
||
|
|
||
|
.PARAMETER HTMLReport
|
||
|
Filename to write HTML Report to
|
||
|
|
||
|
.PARAMETER SendMail
|
||
|
Send Mail after completion. Set to $True to enable. If enabled, -MailFrom, -MailTo, -MailServer are mandatory
|
||
|
|
||
|
.PARAMETER MailFrom
|
||
|
Email address to send from. Passed directly to Send-MailMessage as -From
|
||
|
|
||
|
.PARAMETER MailTo
|
||
|
Email address to send to. Passed directly to Send-MailMessage as -To
|
||
|
|
||
|
.PARAMETER MailServer
|
||
|
SMTP Mail server to attempt to send through. Passed directly to Send-MailMessage as -SmtpServer
|
||
|
|
||
|
.PARAMETER ViewEntireForest
|
||
|
By default, true. Set the option in Exchange 2007 or 2010 to view all Exchange servers and recipients in the forest.
|
||
|
|
||
|
.PARAMETER ServerFilter
|
||
|
Use a text based string to filter Exchange Servers by, e.g., NL-*
|
||
|
Note the use of the wildcard (*) character to allow for multiple matches.
|
||
|
|
||
|
.PARAMETER ShowDriveNames
|
||
|
Include drive names of EDB file path and LOG file folder in database report table
|
||
|
|
||
|
.EXAMPLE
|
||
|
Generate the HTML report
|
||
|
.\Get-ExchangeEnvironmentReport.ps1 -HTMLReport .\report.html
|
||
|
|
||
|
.EXAMPLE
|
||
|
Generate am HTML report and send the result as HTML email with attachment to the specified recipient using a dedicated smart host
|
||
|
.\Get-ExchangeEnvironmentReport.ps1 -HTMReport ExchangeEnvironment.html -SendMail -ViewEntireForet $true -MailFrom roaster@mcsmemail.de -MailTo grillmaster@mcsmemail.de -MailServer relay.mcsmemail.de
|
||
|
|
||
|
.EXAMPLE
|
||
|
Generate the HTML report including EDB and LOG drive names
|
||
|
.\Get-ExchangeEnvironmentReport.ps1 -ShowDriveNames -HTMLReport .\report.html
|
||
|
#>
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
[parameter(Position=0,Mandatory,HelpMessage='Filename to write HTML report to')][string]$HTMLReport,
|
||
|
[switch]$SendMail,
|
||
|
[string]$MailFrom = '',
|
||
|
[string]$MailTo = '',
|
||
|
[string]$MailServer = '',
|
||
|
[bool]$ViewEntireForest=$true,
|
||
|
[string]$ServerFilter='*',
|
||
|
[switch]$ShowDriveNames
|
||
|
)
|
||
|
|
||
|
# Warning Limits, adjust as needed
|
||
|
$MinFreeDiskspace = 10 # Mark free space less than this value (%) in red
|
||
|
$MaxDatabaseSize = 250 # Mark database larger than this value (GB) in red
|
||
|
|
||
|
# Default variables
|
||
|
$NotAvailable = 'N/A'
|
||
|
|
||
|
# Sub-Function to Get Database Information. Shorter than expected..
|
||
|
function Get-DatabaseAvailabilityGroupInformation {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$DAG
|
||
|
)
|
||
|
|
||
|
@{Name = $DAG.Name.ToUpper()
|
||
|
MemberCount = $DAG.Servers.Count
|
||
|
Members = [array]($DAG.Servers | ForEach-Object { $_.Name })
|
||
|
Databases = @()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Sub-Function to Get Database Information
|
||
|
function Get-DatabaseInformation {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$Database,
|
||
|
$ExchangeEnvironment,
|
||
|
$Mailboxes,
|
||
|
$ArchiveMailboxes,
|
||
|
$E2010
|
||
|
)
|
||
|
|
||
|
# Circular Logging, Last Full Backup
|
||
|
if ($Database.CircularLoggingEnabled) { $CircularLoggingEnabled='Yes' } else { $CircularLoggingEnabled = 'No' }
|
||
|
if ($Database.LastFullBackup) { $LastFullBackup=$Database.LastFullBackup.ToString() } else { $LastFullBackup = 'Not Available' }
|
||
|
|
||
|
# Drive Letter, GitHub issue #4
|
||
|
$DriveNameEdb = ''
|
||
|
try {
|
||
|
$DriveNameEdb = $Database.EdbFilePath.DriveName
|
||
|
}
|
||
|
catch {
|
||
|
$DriveNameEdb = $NotAvailable
|
||
|
}
|
||
|
|
||
|
$DriveNameLog = ''
|
||
|
try {
|
||
|
$DriveNameLog = $Database.LogFolderPath.DriveName
|
||
|
}
|
||
|
catch {
|
||
|
$DriveNameLog = $NotAvailable
|
||
|
}
|
||
|
|
||
|
# Mailbox Average Sizes
|
||
|
$MailboxStatistics = [array]($ExchangeEnvironment.Servers[$Database.Server.Name].MailboxStatistics | Where-Object {$_.Database -eq $Database.Identity})
|
||
|
|
||
|
if ($MailboxStatistics) {
|
||
|
[long]$MailboxItemSizeB = 0
|
||
|
$MailboxStatistics | ForEach-Object{ $MailboxItemSizeB+=$_.TotalItemSizeB }
|
||
|
[long]$MailboxAverageSize = $MailboxItemSizeB / $MailboxStatistics.Count
|
||
|
}
|
||
|
else {
|
||
|
$MailboxAverageSize = 0
|
||
|
}
|
||
|
|
||
|
# Free Disk Space Percentage
|
||
|
if ($ExchangeEnvironment.Servers[$Database.Server.Name].Disks) {
|
||
|
|
||
|
foreach ($Disk in $ExchangeEnvironment.Servers[$Database.Server.Name].Disks) {
|
||
|
if ($Database.EdbFilePath.PathName -like ('{0}*' -f $Disk.Name)) {
|
||
|
$FreeDatabaseDiskSpace = $Disk.FreeSpace / $Disk.Capacity * 100
|
||
|
}
|
||
|
if ($Database.ExchangeVersion.ExchangeBuild.Major -ge 14) {
|
||
|
|
||
|
if ($Database.LogFolderPath.PathName -like ('{0}*' -f $Disk.Name)) {
|
||
|
$FreeLogDiskSpace = ($Disk.FreeSpace / $Disk.Capacity) * 100
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$StorageGroupDN = $Database.DistinguishedName.Replace(('CN={0},' -f $Database.Name),'')
|
||
|
$Adsi=[adsi]"LDAP://$($Database.OriginatingServer)/$($StorageGroupDN)"
|
||
|
if ($Adsi.msExchESEParamLogFilePath -like ('{0}*' -f $Disk.Name)) {
|
||
|
$FreeLogDiskSpace = $Disk.FreeSpace / $Disk.Capacity * 100
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$FreeLogDiskSpace=$null
|
||
|
$FreeDatabaseDiskSpace=$null
|
||
|
}
|
||
|
|
||
|
if ($Database.ExchangeVersion.ExchangeBuild.Major -ge 14 -and $E2010) {
|
||
|
# Exchange 2010 Database Only
|
||
|
$CopyCount = [int]$Database.Servers.Count
|
||
|
|
||
|
if ($Database.MasterServerOrAvailabilityGroup.Name -ne $Database.Server.Name) {
|
||
|
$Copies = [array]($Database.Servers | ForEach-Object { $_.Name })
|
||
|
}
|
||
|
else {
|
||
|
$Copies = @()
|
||
|
}
|
||
|
# Archive Info
|
||
|
$ArchiveMailboxCount = [int]([array]($ArchiveMailboxes | Where-Object {$_.ArchiveDatabase -eq $Database.Name})).Count
|
||
|
|
||
|
$ArchiveStatistics = [array]($ArchiveMailboxes | Where-Object {$_.ArchiveDatabase -eq $Database.Name} | Get-MailboxStatistics -Archive )
|
||
|
|
||
|
if ($ArchiveStatistics) {
|
||
|
[long]$ArchiveItemSizeB = 0
|
||
|
$ArchiveStatistics | ForEach-Object{ $ArchiveItemSizeB+=$_.TotalItemSize.Value.ToBytes() }
|
||
|
[long]$ArchiveAverageSize = $ArchiveItemSizeB / $ArchiveStatistics.Count
|
||
|
}
|
||
|
else {
|
||
|
$ArchiveAverageSize = 0
|
||
|
}
|
||
|
|
||
|
# DB Size / Whitespace Info
|
||
|
[long]$Size = $Database.DatabaseSize.ToBytes()
|
||
|
[long]$Whitespace = $Database.AvailableNewMailboxSpace.ToBytes()
|
||
|
$StorageGroup = $null
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
$ArchiveMailboxCount = 0
|
||
|
$CopyCount = 0
|
||
|
$Copies = @()
|
||
|
# 2003 & 2007, Use WMI (Based on code by Gary Siepser, http://bit.ly/kWWMb3)
|
||
|
$Size = [long](get-wmiobject -Class cim_datafile -ComputerName $Database.Server.Name -Filter ('name=''' + $Database.edbfilepath.pathname.replace('\','\\') + '''')).filesize
|
||
|
|
||
|
if (!$Size) {
|
||
|
Write-Warning -Message ('Cannot detect database size via WMI for {0}' -f $Database.Server.Name)
|
||
|
[long]$Size = 0
|
||
|
[long]$Whitespace = 0
|
||
|
} else {
|
||
|
[long]$MailboxDeletedItemSizeB = 0
|
||
|
if ($MailboxStatistics) {
|
||
|
$MailboxStatistics | ForEach-Object{ $MailboxDeletedItemSizeB+=$_.TotalDeletedItemSizeB }
|
||
|
}
|
||
|
|
||
|
# Calculate database whitespace
|
||
|
$Whitespace = $Size - $MailboxItemSizeB - $MailboxDeletedItemSizeB
|
||
|
if ($Whitespace -lt 0) { $Whitespace = 0 }
|
||
|
}
|
||
|
|
||
|
$StorageGroup =$Database.DistinguishedName.Split(',')[1].Replace('CN=','')
|
||
|
}
|
||
|
|
||
|
@{Name = $Database.Name
|
||
|
StorageGroup= $StorageGroup
|
||
|
ActiveOwner = $Database.Server.Name.ToUpper()
|
||
|
MailboxCount = [long]([array]($Mailboxes | Where-Object {$_.Database -eq $Database.Identity})).Count
|
||
|
MailboxAverageSize = $MailboxAverageSize
|
||
|
ArchiveMailboxCount = $ArchiveMailboxCount
|
||
|
ArchiveAverageSize = $ArchiveAverageSize
|
||
|
CircularLoggingEnabled = $CircularLoggingEnabled
|
||
|
LastFullBackup = $LastFullBackup
|
||
|
Size = $Size
|
||
|
Whitespace = $Whitespace
|
||
|
Copies = $Copies
|
||
|
CopyCount = $CopyCount
|
||
|
FreeLogDiskSpace = $FreeLogDiskSpace
|
||
|
FreeDatabaseDiskSpace = $FreeDatabaseDiskSpace
|
||
|
DriveNameEdb = $DriveNameEdb
|
||
|
DriveNameLog = $DriveNameLog
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Sub-Function to get mailbox count per server.
|
||
|
# New in 1.5.2
|
||
|
function Get-ExchangeServerMailboxCount {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$Mailboxes,
|
||
|
$ExchangeServer,
|
||
|
$Databases
|
||
|
)
|
||
|
# The following *should* work, but it doesn't. Apparently, ServerName is not always returned correctly which may be the cause of
|
||
|
# reports of counts being incorrect
|
||
|
#([array]($Mailboxes | Where {$_.ServerName -eq $ExchangeServer.Name})).Count
|
||
|
|
||
|
# ..So as a workaround, I'm going to check what databases are assigned to each server and then get the mailbox counts on a per-
|
||
|
# database basis and return the resulting total. As we already have this information resident in memory it should be cheap, just
|
||
|
# not as quick.
|
||
|
$MailboxCount = 0
|
||
|
|
||
|
foreach ($Database in [array]($Databases | Where-Object {$_.Server -eq $ExchangeServer.Name})) {
|
||
|
$MailboxCount+=([array]($Mailboxes | Where-Object {$_.Database -eq $Database.Identity})).Count
|
||
|
}
|
||
|
|
||
|
$MailboxCount
|
||
|
|
||
|
}
|
||
|
|
||
|
# Sub-Function to Get Exchange Server information
|
||
|
function Get-ExchangeServerInformation {
|
||
|
[CmdletBinding()]
|
||
|
param($E2010,$ExchangeServer,$Mailboxes,$Databases,$Hybrids)
|
||
|
|
||
|
# Set Basic Variables
|
||
|
$MailboxCount = 0
|
||
|
$RollupLevel = 0
|
||
|
$RollupVersion = ''
|
||
|
$ExtNames = @()
|
||
|
$IntNames = @()
|
||
|
$CASArrayName = ''
|
||
|
|
||
|
# 2019-05-20 TST Added to handle max preferred/active databases per server
|
||
|
$MaxPrefDatabases = 0
|
||
|
$MaxActiveDatabases = 0
|
||
|
$NotSet = '--'
|
||
|
|
||
|
# Get WMI Information: Operatin System
|
||
|
$tWMI = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ExchangeServer.Name -ErrorAction SilentlyContinue
|
||
|
|
||
|
if ($tWMI) {
|
||
|
$OSVersion = $tWMI.Caption.Replace('(R)','').Replace('Microsoft ','').Replace('Enterprise','Ent').Replace('Standard','Std').Replace(' Edition','')
|
||
|
$OSServicePack = $tWMI.CSDVersion
|
||
|
$RealName = $tWMI.CSName.ToUpper()
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot detect OS information via WMI for {0}' -f $ExchangeServer.Name)
|
||
|
$OSVersion = $NotAvailable
|
||
|
$OSServicePack = $NotAvailable
|
||
|
$RealName = $ExchangeServer.Name.ToUpper()
|
||
|
}
|
||
|
|
||
|
# Get WMI Information: Disk Space
|
||
|
$tWMI=Get-WmiObject -Query 'Select * from Win32_Volume' -ComputerName $ExchangeServer.Name -ErrorAction SilentlyContinue
|
||
|
|
||
|
if ($tWMI) {
|
||
|
$Disks=$tWMI | Select-Object -Property Name,Capacity,FreeSpace | Sort-Object -Property Name
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot detect OS information via WMI for {0}' -f $ExchangeServer.Name)
|
||
|
$Disks=$null
|
||
|
}
|
||
|
|
||
|
# Get Exchange Version
|
||
|
if ($ExchangeServer.AdminDisplayVersion.Major -eq 6) {
|
||
|
$ExchangeMajorVersion = [double]('{0}.{1}' -f $ExchangeServer.AdminDisplayVersion.Major, $ExchangeServer.AdminDisplayVersion.Minor)
|
||
|
$ExchangeSPLevel = $ExchangeServer.AdminDisplayVersion.FilePatchLevelDescription.Replace('Service Pack ','')
|
||
|
}
|
||
|
elseif ($ExchangeServer.AdminDisplayVersion.Major -eq 15 -and $ExchangeServer.AdminDisplayVersion.Minor -ge 1) {
|
||
|
$ExchangeMajorVersion = [double]('{0}.{1}' -f $ExchangeServer.AdminDisplayVersion.Major, $ExchangeServer.AdminDisplayVersion.Minor)
|
||
|
$ExchangeSPLevel = 0
|
||
|
}
|
||
|
else {
|
||
|
$ExchangeMajorVersion = $ExchangeServer.AdminDisplayVersion.Major
|
||
|
$ExchangeSPLevel = $ExchangeServer.AdminDisplayVersion.Minor
|
||
|
}
|
||
|
|
||
|
# Exchange 2007+
|
||
|
if ($ExchangeMajorVersion -ge 8)
|
||
|
{
|
||
|
# Get Roles
|
||
|
$MailboxStatistics=$null
|
||
|
[array]$Roles = $ExchangeServer.ServerRole.ToString().Replace(' ','').Split(',')
|
||
|
|
||
|
# Add Hybrid "Role" for report
|
||
|
if ($Hybrids -contains $ExchangeServer.Name) {
|
||
|
$Roles+='Hybrid'
|
||
|
}
|
||
|
|
||
|
if ($Roles -contains 'Mailbox') {
|
||
|
|
||
|
$MailboxCount = Get-ExchangeServerMailboxCount -Mailboxes $Mailboxes -ExchangeServer $ExchangeServer -Databases $Databases
|
||
|
if ($ExchangeServer.Name.ToUpper() -ne $RealName) {
|
||
|
$Roles = [array]($Roles | Where-Object {$_ -ne 'Mailbox'})
|
||
|
$Roles += 'ClusteredMailbox'
|
||
|
}
|
||
|
|
||
|
# Get Mailbox Statistics the normal way, return in a consitent format
|
||
|
# 2019-05-20 TST, try/catch added
|
||
|
try {
|
||
|
$MailboxStatistics = Get-MailboxStatistics -Server $ExchangeServer -ErrorAction SilentlyContinue | Select-Object -Property DisplayName,@{Name='TotalItemSizeB';Expression={$_.TotalItemSize.Value.ToBytes()}},@{Name='TotalDeletedItemSizeB';Expression={$_.TotalDeletedItemSize.Value.ToBytes()}},Database
|
||
|
}
|
||
|
catch {
|
||
|
$MailboxStatistics = $null
|
||
|
Write-Warning -Message ('Cannot get mailbox statistics for server {0}' -f $ExchangeServer)
|
||
|
}
|
||
|
|
||
|
if($ExchangeMajorVersion -ge 14) {
|
||
|
$mailboxServer = Get-MailboxServer -Identity $($ExchangeServer.Name)
|
||
|
|
||
|
# 2019-05-20 TST Gather max active/max preferred database config
|
||
|
if($ExchangeMajorVersion -lt 15) {
|
||
|
# Exchange 2010
|
||
|
$MaxActiveDatabases = $mailboxServer.MaximumActiveDatabases
|
||
|
}
|
||
|
else {
|
||
|
# Exchange 2013+
|
||
|
if($null -ne $mailboxServer.MaximumPreferredActiveDatabases) {
|
||
|
$MaxPrefDatabases = $mailboxServer.MaximumPreferredActiveDatabases
|
||
|
}
|
||
|
else {
|
||
|
$MaxPrefDatabases = $NotSet
|
||
|
}
|
||
|
|
||
|
if($null -ne $mailboxServer.MaximumActiveDatabases) {
|
||
|
$MaxActiveDatabases = $mailboxServer.MaximumActiveDatabases
|
||
|
}
|
||
|
else {
|
||
|
$MaxActiveDatabases = $NotSet
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Get HTTPS Names (Exchange 2010 only due to time taken to retrieve data)
|
||
|
# 2019-05-16 TST | Update to support 'Mailbox' role for gathering namespace information
|
||
|
if (($Roles -contains 'ClientAccess' -and $E2010) -or ($Roles -contains 'Mailbox' -and $E2010))
|
||
|
{
|
||
|
Get-OWAVirtualDirectory -Server $ExchangeServer -ADPropertiesOnly | ForEach-Object{ $ExtNames+=$_.ExternalURL.Host; $IntNames+=$_.InternalURL.Host }
|
||
|
|
||
|
Get-WebServicesVirtualDirectory -Server $ExchangeServer -ADPropertiesOnly | ForEach-Object{ $ExtNames+=$_.ExternalURL.Host; $IntNames+=$_.InternalURL.Host }
|
||
|
|
||
|
Get-OABVirtualDirectory -Server $ExchangeServer -ADPropertiesOnly | ForEach-Object{ $ExtNames+=$_.ExternalURL.Host; $IntNames+=$_.InternalURL.Host }
|
||
|
|
||
|
Get-ActiveSyncVirtualDirectory -Server $ExchangeServer -ADPropertiesOnly | ForEach-Object{ $ExtNames+=$_.ExternalURL.Host; $IntNames+=$_.InternalURL.Host }
|
||
|
|
||
|
if (Get-Command -Name Get-MAPIVirtualDirectory -ErrorAction SilentlyContinue) {
|
||
|
Get-MAPIVirtualDirectory -Server $ExchangeServer -ADPropertiesOnly | ForEach-Object{ $ExtNames+=$_.ExternalURL.Host; $IntNames+=$_.InternalURL.Host }
|
||
|
}
|
||
|
|
||
|
if (Get-Command -Name Get-ClientAccessService -ErrorAction SilentlyContinue) {
|
||
|
$IntNames+=(Get-ClientAccessService -Identity $ExchangeServer.Name).AutoDiscoverServiceInternalURI.Host
|
||
|
}
|
||
|
else {
|
||
|
# Fallback to use Get-ClientAccessServer cmdlet
|
||
|
$IntNames+=(Get-ClientAccessServer -Identity $ExchangeServer.Name).AutoDiscoverServiceInternalURI.Host
|
||
|
}
|
||
|
|
||
|
if ($ExchangeMajorVersion -ge 14) {
|
||
|
Get-ECPVirtualDirectory -Server $ExchangeServer -ADPropertiesOnly | ForEach-Object{ $ExtNames+=$_.ExternalURL.Host; $IntNames+=$_.InternalURL.Host; }
|
||
|
}
|
||
|
|
||
|
$IntNames = $IntNames | Sort-Object -Unique
|
||
|
$ExtNames = $ExtNames | Sort-Object -Unique
|
||
|
$CASArray = Get-ClientAccessArray -Site $ExchangeServer.Site.Name
|
||
|
|
||
|
if ($CASArray) {
|
||
|
$CASArrayName = $CASArray.Fqdn
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Rollup Level / Versions (Thanks to Bhargav Shukla https://bhargavs.com/index.php/2009/12/14/how-do-i-check-update-rollup-version-on-exchange-20xx-server/)
|
||
|
switch([string]$ExchangeMajorVersion) {
|
||
|
# Exchange Server 2016 / 2019
|
||
|
'15.2' {$RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\442189DC8B9EA5040962A6BED9EC1F1F\\Patches"}
|
||
|
'15.1' {$RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\442189DC8B9EA5040962A6BED9EC1F1F\\Patches"}
|
||
|
# Exchange Server 2010 / 2013
|
||
|
'15' {$RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\AE1D439464EB1B8488741FFA028E291C\\Patches"}
|
||
|
'14' {$RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\AE1D439464EB1B8488741FFA028E291C\\Patches"}
|
||
|
# Exchange 2007
|
||
|
default {$RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\461C2B4266EDEF444B864AD6D9E5B613\\Patches"}
|
||
|
}
|
||
|
|
||
|
# 2019-05-17 Thomas Stensitzki, try/catch added
|
||
|
try {
|
||
|
$RemoteRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ExchangeServer.Name)
|
||
|
}
|
||
|
catch {
|
||
|
$RemoteRegistry = $null
|
||
|
}
|
||
|
|
||
|
if ($null -ne $RemoteRegistry) {
|
||
|
|
||
|
$RUKeys = $RemoteRegistry.OpenSubKey($RegKey).GetSubKeyNames() | ForEach-Object {"$RegKey\\$_"}
|
||
|
|
||
|
if ($RUKeys) {
|
||
|
[array]($RUKeys | ForEach-Object{$RemoteRegistry.OpenSubKey($_).GetValue('DisplayName')}) | `
|
||
|
ForEach-Object{
|
||
|
if ($_ -like 'Update Rollup *') {
|
||
|
$tRU = $_.Split(' ')[2]
|
||
|
if ($tRU -like '*-*') { $tRUV=$tRU.Split('-')[1]; $tRU=$tRU.Split('-')[0] } else { $tRUV='' }
|
||
|
if ([int]$tRU -ge [int]$RollupLevel) { $RollupLevel=$tRU; $RollupVersion=$tRUV }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot detect Rollup Version via Remote Registry for {0}' -f $ExchangeServer.Name)
|
||
|
}
|
||
|
|
||
|
# Exchange 2013+ CU or SP Level
|
||
|
if ($ExchangeMajorVersion -ge 15) {
|
||
|
$RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft Exchange v15"
|
||
|
# 2019-05-17 Thomas Stensitzki, try/catch added
|
||
|
try {
|
||
|
$RemoteRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ExchangeServer.Name)
|
||
|
}
|
||
|
catch {
|
||
|
$RemoteRegistry = $null
|
||
|
}
|
||
|
|
||
|
if ($RemoteRegistry) {
|
||
|
$ExchangeSPLevel = $RemoteRegistry.OpenSubKey($RegKey).GetValue('DisplayName')
|
||
|
|
||
|
if ($ExchangeSPLevel -like '*Service Pack*' -or $ExchangeSPLevel -like '*Cumulative Update*') {
|
||
|
$ExchangeSPLevel = $ExchangeSPLevel.Replace('Microsoft Exchange Server 2013 ','')
|
||
|
$ExchangeSPLevel = $ExchangeSPLevel.Replace('Microsoft Exchange Server 2016 ','')
|
||
|
$ExchangeSPLevel = $ExchangeSPLevel.Replace('Microsoft Exchange Server 2019 ','')
|
||
|
$ExchangeSPLevel = $ExchangeSPLevel.Replace('Service Pack ','SP')
|
||
|
$ExchangeSPLevel = $ExchangeSPLevel.Replace('Cumulative Update ','CU')
|
||
|
}
|
||
|
else {
|
||
|
$ExchangeSPLevel = 0
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot detect CU/SP via Remote Registry for {0}' -f $ExchangeServer.Name)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Exchange 2003
|
||
|
if ($ExchangeMajorVersion -eq 6.5) {
|
||
|
|
||
|
# Mailbox Count
|
||
|
$MailboxCount = Get-ExchangeServerMailboxCount -Mailboxes $Mailboxes -ExchangeServer $ExchangeServer -Databases $Databases
|
||
|
|
||
|
# Get Role via WMI
|
||
|
$tWMI = Get-WMIObject -Class Exchange_Server -Namespace 'root\microsoftexchangev2' -ComputerName $ExchangeServer.Name -Filter "Name='$($ExchangeServer.Name)'"
|
||
|
|
||
|
if ($tWMI) {
|
||
|
if ($tWMI.IsFrontEndServer) { $Roles=@('FE') } else { $Roles=@('BE') }
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot detect Front End/Back End Server information via WMI for {0}' -f $ExchangeServer.Name)
|
||
|
$Roles+='Unknown'
|
||
|
}
|
||
|
|
||
|
# Get Mailbox Statistics using WMI, return in a consistent format
|
||
|
$tWMI = Get-WMIObject -class Exchange_Mailbox -Namespace ROOT\MicrosoftExchangev2 -ComputerName $ExchangeServer.Name -Filter ("ServerName='$($ExchangeServer.Name)'")
|
||
|
if ($tWMI)
|
||
|
{
|
||
|
$MailboxStatistics = $tWMI | Select-Object -Property @{Name='DisplayName';Expression={$_.MailboxDisplayName}},@{Name='TotalItemSizeB';Expression={$_.Size}},@{Name='TotalDeletedItemSizeB';Expression={$_.DeletedMessageSizeExtended }},@{Name='Database';Expression={((Get-MailboxDatabase -Identity "$($_.ServerName)\$($_.StorageGroupName)\$($_.StoreName)").Identity)}}
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot retrieve Mailbox Statistics via WMI for {0}' -f $ExchangeServer.Name)
|
||
|
$MailboxStatistics = $null
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Exchange 2000
|
||
|
if ($ExchangeMajorVersion -eq '6.0')
|
||
|
{
|
||
|
# Mailbox Count
|
||
|
$MailboxCount = Get-ExchangeServerMailboxCount -Mailboxes $Mailboxes -ExchangeServer $ExchangeServer -Databases $Databases
|
||
|
|
||
|
# Get Role via ADSI
|
||
|
$tADSI=[ADSI]"LDAP://$($ExchangeServer.OriginatingServer)/$($ExchangeServer.DistinguishedName)"
|
||
|
|
||
|
if ($tADSI) {
|
||
|
if ($tADSI.ServerRole -eq 1) { $Roles=@('FE') } else { $Roles=@('BE') }
|
||
|
}
|
||
|
else {
|
||
|
Write-Warning -Message ('Cannot detect Front End/Back End Server information via ADSI for {0}' -f $ExchangeServer.Name)
|
||
|
$Roles+='Unknown'
|
||
|
}
|
||
|
$MailboxStatistics = $null
|
||
|
}
|
||
|
|
||
|
# Return Hashtable
|
||
|
@{Name = $ExchangeServer.Name.ToUpper()
|
||
|
RealName = $RealName
|
||
|
ExchangeMajorVersion = $ExchangeMajorVersion
|
||
|
ExchangeSPLevel = $ExchangeSPLevel
|
||
|
Edition = $ExchangeServer.Edition
|
||
|
Mailboxes = $MailboxCount
|
||
|
OSVersion = $OSVersion;
|
||
|
OSServicePack = $OSServicePack
|
||
|
Roles = $Roles
|
||
|
RollupLevel = $RollupLevel
|
||
|
RollupVersion = $RollupVersion
|
||
|
Site = $ExchangeServer.Site.Name
|
||
|
MailboxStatistics = $MailboxStatistics
|
||
|
Disks = $Disks
|
||
|
IntNames = $IntNames
|
||
|
ExtNames = $ExtNames
|
||
|
CASArrayName = $CASArrayName
|
||
|
MaximumPreferredDatabases = $MaxPrefDatabases
|
||
|
MaximumActiveDatabases = $MaxActiveDatabases
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Sub Function to Get Totals by Version
|
||
|
function Get-TotalsByVersion {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$ExchangeEnvironment
|
||
|
)
|
||
|
|
||
|
# Create empty hash table
|
||
|
$TotalMailboxesByVersion=@{}
|
||
|
|
||
|
if ($ExchangeEnvironment.Sites) {
|
||
|
foreach ($Site in $ExchangeEnvironment.Sites.GetEnumerator()) {
|
||
|
foreach ($Server in $Site.Value) {
|
||
|
if (!$TotalMailboxesByVersion["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"]) {
|
||
|
$TotalMailboxesByVersion.Add("$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)",@{ServerCount=1;MailboxCount=$Server.Mailboxes})
|
||
|
}
|
||
|
else {
|
||
|
$TotalMailboxesByVersion["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"].ServerCount++
|
||
|
$TotalMailboxesByVersion["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"].MailboxCount+=$Server.Mailboxes
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($ExchangeEnvironment.Pre2007) {
|
||
|
foreach ($FakeSite in $ExchangeEnvironment.Pre2007.GetEnumerator()) {
|
||
|
foreach ($Server in $FakeSite.Value) {
|
||
|
if (!$TotalMailboxesByVersion["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"]) {
|
||
|
$TotalMailboxesByVersion.Add("$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)",@{ServerCount=1;MailboxCount=$Server.Mailboxes})
|
||
|
}
|
||
|
else {
|
||
|
$TotalMailboxesByVersion["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"].ServerCount++
|
||
|
$TotalMailboxesByVersion["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"].MailboxCount+=$Server.Mailboxes
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$TotalMailboxesByVersion
|
||
|
}
|
||
|
|
||
|
# Sub Function to Get Totals by Role
|
||
|
function Get-TotalsByRole {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$ExchangeEnvironment
|
||
|
)
|
||
|
|
||
|
# Add Roles We Always Show
|
||
|
$TotalServersByRole=@{
|
||
|
'ClientAccess' = 0
|
||
|
'HubTransport' = 0
|
||
|
'UnifiedMessaging' = 0
|
||
|
'Mailbox' = 0
|
||
|
'Edge' = 0
|
||
|
}
|
||
|
|
||
|
if ($ExchangeEnvironment.Sites) {
|
||
|
|
||
|
foreach ($Site in $ExchangeEnvironment.Sites.GetEnumerator()) {
|
||
|
|
||
|
foreach ($Server in $Site.Value) {
|
||
|
|
||
|
foreach ($Role in $Server.Roles) {
|
||
|
if ($null -eq $TotalServersByRole[$Role]) {
|
||
|
$TotalServersByRole.Add($Role,1)
|
||
|
}
|
||
|
else {
|
||
|
$TotalServersByRole[$Role]++
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($ExchangeEnvironment.Pre2007['Pre 2007 Servers']) {
|
||
|
|
||
|
foreach ($Server in $ExchangeEnvironment.Pre2007['Pre 2007 Servers']) {
|
||
|
|
||
|
foreach ($Role in $Server.Roles) {
|
||
|
if ($null -eq $TotalServersByRole[$Role]) {
|
||
|
$TotalServersByRole.Add($Role,1)
|
||
|
}
|
||
|
else {
|
||
|
$TotalServersByRole[$Role]++
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$TotalServersByRole
|
||
|
}
|
||
|
|
||
|
# Sub Function to return HTML Table for Sites/Pre 2007
|
||
|
function Get-HtmlOverview {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$Servers,
|
||
|
$ExchangeEnvironment,
|
||
|
$ExRoleStrings,
|
||
|
$Pre2007=$False
|
||
|
)
|
||
|
|
||
|
|
||
|
if ($Pre2007) {
|
||
|
$BGColHeader='#880099'
|
||
|
$BGColSubHeader='#8800CC'
|
||
|
$Prefix=''
|
||
|
$IntNamesText=''
|
||
|
$ExtNamesText=''
|
||
|
$CASArrayText=''
|
||
|
$ColClass = 'pre2007overviewcolheader'
|
||
|
$ColSubClass = 'pre2007overviewcolsubheader'
|
||
|
}
|
||
|
else {
|
||
|
$BGColHeader='#000099'
|
||
|
$BGColSubHeader='#0000FF'
|
||
|
$Prefix='Site:'
|
||
|
$IntNamesText=''
|
||
|
$ExtNamesText=''
|
||
|
$CASArrayText=''
|
||
|
$IntNames=@()
|
||
|
$ExtNames=@()
|
||
|
$CASArrayName=''
|
||
|
$ColClass = 'overviewcolheader'
|
||
|
$ColSubClass = 'overviewcolsubheader'
|
||
|
|
||
|
foreach ($Server in $Servers.Value) {
|
||
|
$IntNames+=$Server.IntNames
|
||
|
$ExtNames+=$Server.ExtNames
|
||
|
$CASArrayName=$Server.CASArrayName
|
||
|
}
|
||
|
|
||
|
$IntNames = $IntNames | Sort-Object -Unique
|
||
|
$ExtNames = $ExtNames | Sort-Object -Unique
|
||
|
|
||
|
$IntNames = [string]::Join(',',$IntNames)
|
||
|
$ExtNames = [string]::Join(',',$ExtNames)
|
||
|
|
||
|
if ($IntNames) {
|
||
|
$IntNamesText=('Internal Names: {0}' -f $IntNames)
|
||
|
$ExtNamesText=('External Names: {0}<br />' -f $ExtNames)
|
||
|
}
|
||
|
|
||
|
if ($CASArrayName) {
|
||
|
$CASArrayText="CAS Array: $($CASArrayName)"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Output="<table class='overview'>
|
||
|
<col width='20%'>
|
||
|
<col width='20%'>
|
||
|
<colgroup width='25%'>"
|
||
|
|
||
|
$ExchangeEnvironment.TotalServersByRole.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{$Output+="<col width='3%'>"}
|
||
|
|
||
|
$Output+="</colgroup><col width='20%'><col width='20%'>
|
||
|
<tr class=""$($ColClass)"">
|
||
|
<th class='overview'>$($Prefix) $($Servers.Key)</th>
|
||
|
<th class='overview' colspan=""$(($ExchangeEnvironment.TotalServersByRole.Count)+2)"" align=""left"">$($ExtNamesText)$($IntNamesText)</th>
|
||
|
<th class='overview' align=""center"">$($CASArrayText)</th>
|
||
|
<th colspan='2'> </th>
|
||
|
</tr>"
|
||
|
$TotalMailboxes=0
|
||
|
$Servers.Value | ForEach-Object{$TotalMailboxes += $_.Mailboxes}
|
||
|
$Output+="<tr class=""$($ColSubClass)""><th class='overview'>Mailboxes: $($TotalMailboxes)</th>"
|
||
|
$Output+="<th class='overview'>Exchange Version</th>"
|
||
|
$ExchangeEnvironment.TotalServersByRole.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{$Output+="<th class='overview'>$($ExRoleStrings[$_.Key].Short)</th>"}
|
||
|
|
||
|
# 2016-04-19 Thomas Stensitzki Pref/Max Databases added
|
||
|
$Output+="<th class='overview'>Databases Pref/Max</th><th class='overview'>OS Version</th><th class='overview'>OS Service Pack</th></tr>"
|
||
|
|
||
|
$AlternateRow=0
|
||
|
|
||
|
foreach ($Server in $Servers.Value) {
|
||
|
$Output+='<tr'
|
||
|
|
||
|
if ($AlternateRow) {
|
||
|
$Output+=" class='alternaterow'"
|
||
|
$AlternateRow=0
|
||
|
}
|
||
|
else {
|
||
|
$AlternateRow=1
|
||
|
}
|
||
|
|
||
|
$Output+=('><td>{0}' -f $Server.Name)
|
||
|
|
||
|
if ($Server.RealName -ne $Server.Name) {
|
||
|
$Output+=(' ({0})' -f $Server.RealName)
|
||
|
}
|
||
|
|
||
|
$Output+="</td><td>$($ExVersionStrings["$($Server.ExchangeMajorVersion).$($Server.ExchangeSPLevel)"].Long)"
|
||
|
|
||
|
if ($Server.RollupLevel -gt 0) {
|
||
|
$Output+=(' UR{0}' -f $Server.RollupLevel)
|
||
|
if ($Server.RollupVersion) {
|
||
|
$Output+=(' {0}' -f $Server.RollupVersion)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Output+='</td>'
|
||
|
|
||
|
$ExchangeEnvironment.TotalServersByRole.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{
|
||
|
$Output+='<td'
|
||
|
if ($Server.Roles -contains $_.Key) {
|
||
|
$Output+=" class='roledata'>"
|
||
|
}
|
||
|
else {
|
||
|
$Output+=' />'
|
||
|
}
|
||
|
|
||
|
if (($_.Key -eq 'ClusteredMailbox' -or $_.Key -eq 'Mailbox' -or $_.Key -eq 'BE') -and $Server.Roles -contains $_.Key) {
|
||
|
$Output+=('{0}</td>' -f $Server.Mailboxes)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# 2016-04-19 Thomas Stensitzki Max Databases added
|
||
|
if($Server.Roles -contains 'Mailbox') {
|
||
|
$Output+="<td align=""center"">$($Server.MaximumPreferredDatabases) / $($Server.MaximumActiveDatabases)</td><td>$($Server.OSVersion)</td><td>$($Server.OSServicePack)</td></tr>"
|
||
|
}
|
||
|
else {
|
||
|
$Output+=('<td align=""center"">Not Applicable</td><td>{0}</td><td>{1}</td></tr>' -f $Server.OSVersion, $Server.OSServicePack)
|
||
|
}
|
||
|
|
||
|
# $Output+="<td>$($Server.OSVersion)</td><td>$($Server.OSServicePack)</td></tr>";
|
||
|
}
|
||
|
|
||
|
$Output+='<tr />
|
||
|
</table><br />'
|
||
|
|
||
|
$Output
|
||
|
}
|
||
|
|
||
|
# Sub Function to return HTML Table for Databases
|
||
|
function Get-HtmlDatabaseInformationTable {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$Databases
|
||
|
)
|
||
|
|
||
|
# Only Show Archive Mailbox Columns, Backup Columns and Circ Logging if at least one DB has an Archive mailbox, backed up or Cir Log enabled.
|
||
|
$ShowArchiveDBs = $False
|
||
|
$ShowLastFullBackup = $False
|
||
|
$ShowCircularLogging = $False
|
||
|
$ShowStorageGroups = $False
|
||
|
$ShowCopies = $False
|
||
|
$ShowFreeDatabaseSpace = $False
|
||
|
$ShowFreeLogDiskSpace = $False
|
||
|
|
||
|
foreach ($Database in $Databases) {
|
||
|
if ($Database.ArchiveMailboxCount -gt 0) {
|
||
|
$ShowArchiveDBs=$True
|
||
|
}
|
||
|
if ($Database.LastFullBackup -ne 'Not Available') {
|
||
|
$ShowLastFullBackup=$True
|
||
|
}
|
||
|
if ($Database.CircularLoggingEnabled -eq 'Yes') {
|
||
|
$ShowCircularLogging=$True
|
||
|
}
|
||
|
if ($Database.StorageGroup) {
|
||
|
$ShowStorageGroups=$True
|
||
|
}
|
||
|
if ($Database.CopyCount -gt 0) {
|
||
|
$ShowCopies=$True
|
||
|
}
|
||
|
if ($Database.FreeDatabaseDiskSpace -ne $null) {
|
||
|
$ShowFreeDatabaseSpace=$true
|
||
|
}
|
||
|
if ($Database.FreeLogDiskSpace -ne $null) {
|
||
|
$ShowFreeLogDiskSpace=$true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Output="<table class='databases'>"
|
||
|
|
||
|
#region database table header
|
||
|
$Output +="<tr class='databases'>
|
||
|
<th>Server</th>"
|
||
|
|
||
|
if ($ShowStorageGroups) {
|
||
|
$Output+='<th>Storage Group</th>'
|
||
|
}
|
||
|
|
||
|
$Output+='<th>Database Name</th>
|
||
|
<th>Standard Mailboxes</th>
|
||
|
<th>Av. Mailbox Size</th>'
|
||
|
|
||
|
if ($ShowArchiveDBs) {
|
||
|
$Output+='<th>Archive Mailboxes</th><th>Av. Archive Size</th>'
|
||
|
}
|
||
|
|
||
|
$Output+='<th>DB Size</th><th>DB Whitespace</th>'
|
||
|
|
||
|
if ($ShowFreeDatabaseSpace) {
|
||
|
$Output+='<th>Database Disk Free</th>'
|
||
|
}
|
||
|
if ($ShowFreeLogDiskSpace) {
|
||
|
$Output+='<th>Log Disk Free</th>'
|
||
|
}
|
||
|
if ($ShowLastFullBackup) {
|
||
|
$Output+='<th>Last Full Backup</th>'
|
||
|
}
|
||
|
if ($ShowCircularLogging) {
|
||
|
$Output+='<th>Circular Logging</th>'
|
||
|
}
|
||
|
if ($ShowCopies) {
|
||
|
$Output+='<th>DB Copies (n)</th>'
|
||
|
}
|
||
|
|
||
|
# Drive names, issue #4
|
||
|
if($ShowDriveNames) {
|
||
|
$Output+='<th>EDB / LOG</th>'
|
||
|
}
|
||
|
|
||
|
$Output+='</tr>'
|
||
|
#endregion
|
||
|
|
||
|
$AlternateRow=0
|
||
|
|
||
|
foreach ($Database in $Databases) {
|
||
|
$Output+='<tr'
|
||
|
|
||
|
if ($AlternateRow) {
|
||
|
$Output+=" class='alternaterow'"
|
||
|
$AlternateRow=0
|
||
|
}
|
||
|
else {
|
||
|
$AlternateRow=1
|
||
|
}
|
||
|
|
||
|
# Close open <tr tag
|
||
|
$Output+=('><td>{0}</td>' -f $Database.ActiveOwner)
|
||
|
|
||
|
if ($ShowStorageGroups) {
|
||
|
$Output+=('><td>{0}</td>' -f $Database.StorageGroup)
|
||
|
}
|
||
|
|
||
|
$Output+="<td>$($Database.Name)</td>
|
||
|
<td class='center'>$($Database.MailboxCount)</td>
|
||
|
<td class='center'>$("{0:N2}" -f ($Database.MailboxAverageSize/1MB)) MB</td>"
|
||
|
|
||
|
if ($ShowArchiveDBs) {
|
||
|
$Output+="<td class=""center"">$($Database.ArchiveMailboxCount)</td>
|
||
|
<td class='center'>$("{0:N2}" -f ($Database.ArchiveAverageSize/1MB)) MB</td>"
|
||
|
}
|
||
|
|
||
|
if([double]($Database.Size/1GB) -le $MaxDatabaseSize) {
|
||
|
$Output+="<td class=""center"">$("{0:N2}" -f ($Database.Size/1GB)) GB </td>"
|
||
|
}
|
||
|
else {
|
||
|
$Output+="<td class=""center alert"">$("{0:N2}" -f ($Database.Size/1GB)) GB </td>"
|
||
|
}
|
||
|
|
||
|
$Output+="<td class='center'>$("{0:N2} GB" -f ($Database.Whitespace/1GB))</td>"
|
||
|
|
||
|
# $Output+="<td align=""center"">$("{0:N2}" -f ($Database.Size/1GB)) GB </td><td class='center'>$("{0:N2}" -f ($Database.Whitespace/1GB)) GB</td>"
|
||
|
|
||
|
if ($ShowFreeDatabaseSpace) {
|
||
|
if([double]($Database.FreeDatabaseDiskSpace) -gt $MinFreeDiskspace) {
|
||
|
$Output+="<td class='center'>$("{0:N1}" -f $Database.FreeDatabaseDiskSpace)%</td>"
|
||
|
}
|
||
|
else {
|
||
|
$Output+="<td class='center alert'>$("{0:N1}" -f $Database.FreeDatabaseDiskSpace)%</td>"
|
||
|
}
|
||
|
}
|
||
|
if ($ShowFreeLogDiskSpace) {
|
||
|
if([double]($Database.FreeLogDiskSpace) -gt $MinFreeDiskspace) {
|
||
|
$Output+="<td class='center'>$("{0:N1}" -f $Database.FreeLogDiskSpace)%</td>"
|
||
|
}
|
||
|
else {
|
||
|
$Output+="<td class='center alert'>$("{0:N1}" -f $Database.FreeLogDiskSpace)%</td>"
|
||
|
}
|
||
|
}
|
||
|
if ($ShowLastFullBackup) {
|
||
|
$Output+="<td class='center'>$($Database.LastFullBackup)</td>"
|
||
|
}
|
||
|
if ($ShowCircularLogging) {
|
||
|
$Output+="<td class='center'>$($Database.CircularLoggingEnabled)</td>"
|
||
|
}
|
||
|
if ($ShowCopies) {
|
||
|
$Output+="<td>$($Database.Copies | ForEach-Object{$_}) ($($Database.CopyCount))</td>"
|
||
|
}
|
||
|
|
||
|
# Drive names, issue #4
|
||
|
if ($ShowDriveNames) {
|
||
|
$Output+="<td class='center'>$("{0} / {1}" -f $Database.DriveNameEdb, $Database.DriveNameLog)</td>"
|
||
|
}
|
||
|
$Output+='</tr>'
|
||
|
}
|
||
|
|
||
|
$Output+='</table><br />'
|
||
|
|
||
|
$Output += '<p class="dagtablefooter">Explanation</p>'
|
||
|
$Output += ("<p class='dagtablefooter'>Maximum mailbox database size: {0} GB<br/>Minimum free disk space: {1}%</p>" -f $MaxDatabaseSize, $MinFreeDiskspace)
|
||
|
|
||
|
$Output
|
||
|
}
|
||
|
|
||
|
function Get-HtmlReportHeader {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
$ExchangeEnvironment,
|
||
|
$Path
|
||
|
)
|
||
|
|
||
|
# Labels and stuff
|
||
|
$LabelTotalServers = 'Total Servers'
|
||
|
$LabelTotalMailboxes = 'Total Mailboxes'
|
||
|
$LabelTotalRoles = 'Total Roles'
|
||
|
$DateLabelFormat = 'yyyy-MM-dd HH:mm'
|
||
|
$CssFileName = 'EnvironmentReport.css'
|
||
|
$UseCss = $true
|
||
|
|
||
|
# Header
|
||
|
$Output='
|
||
|
<html>
|
||
|
<body>
|
||
|
<title>Exchange Environment Report</title>'
|
||
|
|
||
|
if($UseCss -and (Test-Path -Path (Join-Path -Path (Split-Path -Parent $Path) -ChildPath $CssFileName))) {
|
||
|
$Output += "<style type=""text/css"">$(Get-Content -Path (Join-Path -Path (Split-Path -Parent $Path) -ChildPath $CssFileName))</style>"
|
||
|
} else {
|
||
|
$Output += "<style type=""text/css"">body{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:10pt;}h1{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:16px;font-weight}h2{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:14px;}h3{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:12px;}h4{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:10px;}table.header{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;border:0px;padding:3px;font-size:11px;}table tr.header{background:#009900;}table th.header{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;color:#ffffff;}table.overview{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:11px;font-weicht:bold;padding:3px;border:0px;width:100%}table th.overview{color:#ffffff;}table tr.overviewcolheader{background:#000099;}table tr.overviewcolsubheader{background:#0000FF;}table tr.pre2007overviewcolheader{background:#880099;}table tr.pre2007overviewcolsubheader{background:#8800CC;}table tr.alternaterow{background:#dddddd}table td.roledata{text-align:center;background:#00FF00;}table tr.subheader{background:#009900;}table th.subheader{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif,monospace,Lucida Console;color:#ffffff;font-size:11px;text-align:center;}table tr.headerdata{background:#dddddd;}table td.headerdata{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;color:#000000;font-size:11px;text-align:center;}table.dagsummary{border:0px;padding:3px;width:100%;font-size:8pt;font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;align:center;}table tr.dagsummary{text-align:center;background:#FF8000;}table tr.dagsummarynondag{text-align:center;background:#FF8000;}table.databases{border:0px;padding:3px;width:100%;font-size:9pt;font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;}table tr.databases{background:#FFD700;text-align:center;}.dagtablefooter{font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;font-size:8pt;}.center{text-align:center;}.alert{color:red;}</style>"
|
||
|
}
|
||
|
|
||
|
$Output += ("<h2 align=""center"">Exchange Environment Report</h2><h3 align=""center"">Organization: {3}</h3>
|
||
|
<h4 align=""center"">Generated {0}</h4>
|
||
|
<table class='header'>
|
||
|
<tr class='header'>
|
||
|
<th colspan=""{1}"" class='header'>{2}</th>" -f (Get-Date -Format $DateLabelFormat), $ExchangeEnvironment.TotalMailboxesByVersion.Count, $LabelTotalServers, $ExchangeEnvironment.OrganizationName)
|
||
|
|
||
|
if ($ExchangeEnvironment.RemoteMailboxes) {
|
||
|
$Output+=("<th colspan=""{0}"" class='header'>{1}</th>" -f ($ExchangeEnvironment.TotalMailboxesByVersion.Count+2), $LabelTotalMailboxes)
|
||
|
}
|
||
|
else {
|
||
|
$Output+=("<th colspan=""{0}"" class='header'>{1}</th>" -f ($ExchangeEnvironment.TotalMailboxesByVersion.Count+1), $LabelTotalMailboxes)
|
||
|
}
|
||
|
|
||
|
$Output+=("<th colspan=""{0}"" class='header'>{1}</th></tr>
|
||
|
<tr class='subheader'>" -f $ExchangeEnvironment.TotalServersByRole.Count, $LabelTotalRoles)
|
||
|
|
||
|
# Show Column Headings based on the Exchange versions we have
|
||
|
$ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{$Output+="<th class='subheader'>$($ExVersionStrings[$_.Key].Short)</th>"}
|
||
|
$ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{$Output+="<th class='subheader'>$($ExVersionStrings[$_.Key].Short)</th>"}
|
||
|
|
||
|
if ($ExchangeEnvironment.RemoteMailboxes) {
|
||
|
$Output+="<th class='subheader'>Office 365</th>"
|
||
|
}
|
||
|
|
||
|
$Output+="<th class='subheader'>Org</th>"
|
||
|
|
||
|
# Exchange Server Roles
|
||
|
$ExchangeEnvironment.TotalServersByRole.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+="<th class='subheader'>$($ExRoleStrings[$_.Key].Short)</th>"}
|
||
|
|
||
|
$Output += '</tr>'
|
||
|
|
||
|
$Output += "<tr class='headerdata'>"
|
||
|
|
||
|
$ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+="<td class='headerdata'>$($_.Value.ServerCount)</td>" }
|
||
|
$ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+="<td class='headerdata'>$($_.Value.MailboxCount)</td>" }
|
||
|
|
||
|
if ($RemoteMailboxes) {
|
||
|
$Output+="<td class='headerdata'>$($ExchangeEnvironment.RemoteMailboxes)</td>"
|
||
|
}
|
||
|
|
||
|
$Output += "<td class='headerdata'>$($ExchangeEnvironment.TotalMailboxes)</td>"
|
||
|
|
||
|
$ExchangeEnvironment.TotalServersByRole.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+="<td class='headerdata'>$($_.Value)</td>"}
|
||
|
|
||
|
#$Output+="</tr><tr><tr></table><br>"
|
||
|
$Output += '</tr></table><!-- End --><br />'
|
||
|
|
||
|
$Output
|
||
|
}
|
||
|
|
||
|
function Get-HtmlDagHeader {
|
||
|
[CmdletBinding()]
|
||
|
param (
|
||
|
$DAG
|
||
|
)
|
||
|
|
||
|
# Database Availability Group Header
|
||
|
$Output+="<table class=""dagsummary"">
|
||
|
<col width='20%'><col width='10%'><col width='10%'><col width='70%''>
|
||
|
<tr class=""dagsummary""><th>Database Availability Group Name</th><th>Member Count</th><th># Databases</th>
|
||
|
<th>Database Availability Group Members</th></tr>
|
||
|
<tr><td>$($DAG.Name)</td><td>$($DAG.MemberCount)</td><td>$(($DAG.Databases | Measure-Object).Count)</td><td>"
|
||
|
|
||
|
$DAG.Members | ForEach-Object { $Output+=('{0} ' -f $_) }
|
||
|
|
||
|
$Output += '</td></tr></table><br />'
|
||
|
|
||
|
$Output
|
||
|
}
|
||
|
|
||
|
# Sub Function to neatly update progress
|
||
|
function Show-ProgressBar {
|
||
|
[CmdletBinding()]
|
||
|
param(
|
||
|
[int]$PercentComplete,
|
||
|
[string]$Status,
|
||
|
[int]$Stage
|
||
|
)
|
||
|
|
||
|
$TotalStages=5
|
||
|
Write-Progress -Id 1 -Activity 'Get-ExchangeEnvironmentReport' -Status $Status -PercentComplete (($PercentComplete/$TotalStages)+(1/$TotalStages*$Stage*100))
|
||
|
}
|
||
|
|
||
|
# 1. Initial Startup
|
||
|
|
||
|
# 1.0 Check Powershell Version
|
||
|
if ((Get-Host).Version.Major -eq 1) {
|
||
|
throw 'Powershell Version 1 not supported'
|
||
|
}
|
||
|
|
||
|
# 1.1 Check Exchange Management Shell, attempt to load
|
||
|
if (!(Get-Command -Name Get-ExchangeServer -ErrorAction SilentlyContinue))
|
||
|
{
|
||
|
# 2019-05-17 Thomas Stensitzki, Support for Exchange Scripts located in non-default locations
|
||
|
# Use $env:ExchangeInstallPath for Exchange 2010/2013+ installations
|
||
|
$ExchangeInstallPath = $env:ExchangeInstallPath
|
||
|
|
||
|
if (($ExchangeInstallPath -eq '') -or ($ExchangeInstallPath -eq $null)) {
|
||
|
# $env:ExchangeInstallPath not available on Exchange Server 2007 Setups
|
||
|
try {
|
||
|
$ExchangeInstallPath = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Exchange\Setup).MsiInstallPath
|
||
|
}
|
||
|
catch {}
|
||
|
}
|
||
|
|
||
|
Write-Verbose -Message ('Exchange Install Path: {0}' -f $ExchangeInstallPath)
|
||
|
|
||
|
$RemoteExchangePath = Join-Path -Path $ExchangeInstallPath -ChildPath 'bin\RemoteExchange.ps1'
|
||
|
$LocalExchangePath = Join-Path -Path $ExchangeInstallPath -ChildPath 'bin\Exchange.ps1'
|
||
|
|
||
|
if (Test-Path -Path $RemoteExchangePath) {
|
||
|
. $RemoteExchangePath
|
||
|
Connect-ExchangeServer -auto
|
||
|
}
|
||
|
elseif (Test-Path -Path $LocalExchangePath) {
|
||
|
Add-PSSnapIn -Name Microsoft.Exchange.Management.PowerShell.Admin
|
||
|
. $LocalExchangePath
|
||
|
}
|
||
|
else {
|
||
|
throw 'Exchange Management Shell cannot be loaded'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# 1.2 Check if -SendMail parameter set and if so check -MailFrom, -MailTo and -MailServer are set
|
||
|
if ($SendMail)
|
||
|
{
|
||
|
if (!$MailFrom -or !$MailTo -or !$MailServer)
|
||
|
{
|
||
|
throw 'If -SendMail specified, you must also specify -MailFrom, -MailTo and -MailServer'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# 1.3 Check Exchange Management Shell Version
|
||
|
if ((Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.Admin -ErrorAction SilentlyContinue))
|
||
|
{
|
||
|
$E2010 = $false;
|
||
|
if (Get-ExchangeServer | Where-Object {$_.AdminDisplayVersion.Major -gt 14}) {
|
||
|
Write-Warning -Message "Exchange 2010 or higher detected. You'll get better results if you run this script from the latest management shell"
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
|
||
|
$E2010 = $true
|
||
|
|
||
|
# 2019-05-17 Thomas Stensitzki, Support for Exchange 2013+ servers with installed management tools
|
||
|
$localversion = $localserver = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiProductMajor
|
||
|
|
||
|
if ($localversion -eq 15) { $E2013 = $true }
|
||
|
}
|
||
|
|
||
|
# 1.4 Check view entire forest if set (by default, true)
|
||
|
if ($E2010) {
|
||
|
Set-ADServerSettings -ViewEntireForest:$ViewEntireForest
|
||
|
}
|
||
|
else {
|
||
|
$global:AdminSessionADSettings.ViewEntireForest = $ViewEntireForest
|
||
|
}
|
||
|
|
||
|
# 1.5 Initial Variables
|
||
|
|
||
|
# 1.5.1 Hashtable to update with environment data
|
||
|
$ExchangeEnvironment = @{
|
||
|
Sites = @{}
|
||
|
Pre2007 = @{}
|
||
|
Servers = @{}
|
||
|
DAGs = @()
|
||
|
NonDAGDatabases = @()
|
||
|
OrganizationName = ''
|
||
|
}
|
||
|
|
||
|
# 1.5.7 Exchange Major Version String Mapping
|
||
|
$ExMajorVersionStrings = @{
|
||
|
'6.0' = @{Long='Exchange 2000';Short='E2000'}
|
||
|
'6.5' = @{Long='Exchange 2003';Short='E2003'}
|
||
|
'8' = @{Long='Exchange 2007';Short='E2007'}
|
||
|
'14' = @{Long='Exchange 2010';Short='E2010'}
|
||
|
'15' = @{Long='Exchange 2013';Short='E2013'}
|
||
|
'15.1' = @{Long='Exchange 2016';Short='E2016'}
|
||
|
'15.2' = @{Long='Exchange 2019';Short='E2019'} #2019-05-17 TST Exchange Server 2019 added
|
||
|
}
|
||
|
|
||
|
# 1.5.8 Exchange Service Pack String Mapping
|
||
|
$ExSPLevelStrings = @{
|
||
|
'0' = 'RTM'
|
||
|
'1' = 'SP1'
|
||
|
'2' = 'SP2'
|
||
|
'3' = 'SP3'
|
||
|
'4' = 'SP4'
|
||
|
'SP1' = 'SP1'
|
||
|
'SP2' = 'SP2'}
|
||
|
|
||
|
# Add many CUs
|
||
|
for ($i = 1; $i -le 40; $i++) {
|
||
|
$ExSPLevelStrings.Add("CU$($i)","CU$($i)");
|
||
|
}
|
||
|
|
||
|
# 1.5.9 Populate Full Mapping using above info
|
||
|
$ExVersionStrings = @{}
|
||
|
|
||
|
foreach ($Major in $ExMajorVersionStrings.GetEnumerator()) {
|
||
|
foreach ($Minor in $ExSPLevelStrings.GetEnumerator()) {
|
||
|
$ExVersionStrings.Add("$($Major.Key).$($Minor.Key)",@{Long="$($Major.Value.Long) $($Minor.Value)";Short="$($Major.Value.Short)$($Minor.Value)"})
|
||
|
}
|
||
|
}
|
||
|
# 1.5.10 Exchange Role String Mapping
|
||
|
$ExRoleStrings = @{'ClusteredMailbox' = @{Short='ClusMBX';Long='CCR/SCC Clustered Mailbox'}
|
||
|
'Mailbox' = @{Short='MBX';Long='Mailbox'}
|
||
|
'ClientAccess' = @{Short='CAS';Long='Client Access'}
|
||
|
'HubTransport' = @{Short='HUB';Long='Hub Transport'}
|
||
|
'UnifiedMessaging' = @{Short='UM';Long='Unified Messaging'}
|
||
|
'Edge' = @{Short='EDGE';Long='Edge Transport'}
|
||
|
'FE' = @{Short='FE';Long='Front End'}
|
||
|
'BE' = @{Short='BE';Long='Back End'}
|
||
|
'Hybrid' = @{Short='HYB'; Long='Hybrid'}
|
||
|
'Coexistence' = @{Short='COEX'; Long='Coexistence'} #2019-05-17 TST Coexistence added
|
||
|
'Unknown' = @{Short='Unknown';Long='Unknown'}}
|
||
|
|
||
|
# 2 Get Relevant Exchange Information Up-Front
|
||
|
|
||
|
# 2.1 Get Server, Exchange and Mailbox Information
|
||
|
Show-ProgressBar -PercentComplete 1 -Status 'Getting Exchange Server List' -Stage 1
|
||
|
|
||
|
$ExchangeServers = [array](Get-ExchangeServer $ServerFilter | Sort-Object Name)
|
||
|
if (!$ExchangeServers) {
|
||
|
throw ('No Exchange Servers matched by -ServerFilter {0}' -f $ServerFilter)
|
||
|
}
|
||
|
|
||
|
$HybridServers=@()
|
||
|
if (Get-Command -Name Get-HybridConfiguration -ErrorAction SilentlyContinue) {
|
||
|
$HybridConfig = Get-HybridConfiguration
|
||
|
$HybridConfig.ReceivingTransportServers | ForEach-Object{ $HybridServers+=$_.Name }
|
||
|
$HybridConfig.SendingTransportServers | ForEach-Object{ $HybridServers+=$_.Name }
|
||
|
$HybridServers = $HybridServers | Sort-Object -Unique
|
||
|
}
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 10 -Status 'Getting Mailboxes' -Stage 1
|
||
|
|
||
|
$Mailboxes = [array](Get-Mailbox -ResultSize Unlimited) | Where-Object {$_.ServerName -like $ServerFilter}
|
||
|
|
||
|
if ($E2010) {
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 60 -Status 'Getting Archive Mailboxes' -Stage 1
|
||
|
|
||
|
$ArchiveMailboxes = [array](Get-Mailbox -Archive -ResultSize Unlimited) | Where-Object {$_.ServerName -like $ServerFilter}
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 70 -Status 'Getting Remote Mailboxes' -Stage 1
|
||
|
|
||
|
$RemoteMailboxes = [array](Get-RemoteMailbox -ResultSize Unlimited)
|
||
|
$ExchangeEnvironment.Add('RemoteMailboxes',$RemoteMailboxes.Count)
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 90 -Status 'Getting Databases' -Stage 1
|
||
|
|
||
|
if ($E2013) {
|
||
|
# 2019-05-17 TST Sorting added
|
||
|
$Databases = [array](Get-MailboxDatabase -IncludePreExchange2013 -Status) | Sort-Object -Property Name | Where-Object {$_.Server -like $ServerFilter}
|
||
|
}
|
||
|
elseif ($E2010) {
|
||
|
# 2019-05-17 TST Sorting added
|
||
|
$Databases = [array](Get-MailboxDatabase -IncludePreExchange2010 -Status) | Sort-Object -Property Name | Where-Object {$_.Server -like $ServerFilter}
|
||
|
}
|
||
|
|
||
|
$DAGs = [array](Get-DatabaseAvailabilityGroup) | Where-Object {$_.Servers -like $ServerFilter}
|
||
|
}
|
||
|
else {
|
||
|
$ArchiveMailboxes = $null
|
||
|
$ArchiveMailboxStats = $null
|
||
|
$DAGs = $null
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 90 -Status 'Getting Databases' -Stage 1
|
||
|
$Databases = [array](Get-MailboxDatabase -IncludePreExchange2007 -Status) | Where-Object {$_.Server -like $ServerFilter}
|
||
|
$ExchangeEnvironment.Add('RemoteMailboxes',0)
|
||
|
}
|
||
|
|
||
|
# 2.3 Populate Information we know
|
||
|
$ExchangeEnvironment.Add('TotalMailboxes',$Mailboxes.Count + $ExchangeEnvironment.RemoteMailboxes)
|
||
|
|
||
|
# 2.4 Organizational Info
|
||
|
|
||
|
$ExchangeEnvironment.OrganizationName = (Get-OrganizationConfig).Name
|
||
|
|
||
|
# 3 Process High-Level Exchange Information
|
||
|
|
||
|
# 3.1 Collect Exchange Server Information
|
||
|
for ($i=0; $i -lt $ExchangeServers.Count; $i++) {
|
||
|
Show-ProgressBar -PercentComplete ($i/$ExchangeServers.Count*100) -Status 'Getting Exchange Server Information' -Stage 2
|
||
|
|
||
|
# Get Exchange Info
|
||
|
$ExSvr = Get-ExchangeServerInformation -E2010 $E2010 -ExchangeServer $ExchangeServers[$i] -Mailboxes $Mailboxes -Databases $Databases -Hybrids $HybridServers
|
||
|
|
||
|
# Add to site or pre-Exchange 2007 list
|
||
|
if ($ExSvr.Site) {
|
||
|
# Exchange 2007 or higher
|
||
|
if (!$ExchangeEnvironment.Sites[$ExSvr.Site]) {
|
||
|
$ExchangeEnvironment.Sites.Add($ExSvr.Site,@($ExSvr))
|
||
|
}
|
||
|
else {
|
||
|
$ExchangeEnvironment.Sites[$ExSvr.Site]+=$ExSvr
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
# Exchange 2003 or lower
|
||
|
if (!$ExchangeEnvironment.Pre2007['Pre 2007 Servers']) {
|
||
|
$ExchangeEnvironment.Pre2007.Add('Pre 2007 Servers',@($ExSvr))
|
||
|
}
|
||
|
else {
|
||
|
$ExchangeEnvironment.Pre2007['Pre 2007 Servers']+=$ExSvr
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Add to Servers List
|
||
|
$ExchangeEnvironment.Servers.Add($ExSvr.Name,$ExSvr)
|
||
|
}
|
||
|
|
||
|
# 3.2 Calculate Environment Totals for Version/Role using collected data
|
||
|
Show-ProgressBar -PercentComplete 1 -Status 'Getting Totals' -Stage 3
|
||
|
|
||
|
$ExchangeEnvironment.Add('TotalMailboxesByVersion',(Get-TotalsByVersion -ExchangeEnvironment $ExchangeEnvironment))
|
||
|
$ExchangeEnvironment.Add('TotalServersByRole',(Get-TotalsByRole -ExchangeEnvironment $ExchangeEnvironment))
|
||
|
|
||
|
# 3.4 Populate Environment DAGs
|
||
|
Show-ProgressBar -PercentComplete 5 -Status 'Getting DAG Info' -Stage 3
|
||
|
|
||
|
if ($DAGs) {
|
||
|
foreach($DAG in $DAGs) {
|
||
|
$ExchangeEnvironment.DAGs+=(Get-DatabaseAvailabilityGroupInformation -DAG $DAG)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# 3.5 Get Database information
|
||
|
Show-ProgressBar -PercentComplete 60 -Status 'Getting Database Info' -Stage 3
|
||
|
|
||
|
for ($i=0; $i -lt $Databases.Count; $i++)
|
||
|
{
|
||
|
$Database = Get-DatabaseInformation -Database $Databases[$i] -ExchangeEnvironment $ExchangeEnvironment -Mailboxes $Mailboxes -ArchiveMailboxes $ArchiveMailboxes -E2010 $E2010
|
||
|
$DAGDB = $false
|
||
|
for ($j=0; $j -lt $ExchangeEnvironment.DAGs.Count; $j++) {
|
||
|
if ($ExchangeEnvironment.DAGs[$j].Members -contains $Database.ActiveOwner) {
|
||
|
$DAGDB=$true
|
||
|
$ExchangeEnvironment.DAGs[$j].Databases += $Database
|
||
|
}
|
||
|
}
|
||
|
if (!$DAGDB) {
|
||
|
$ExchangeEnvironment.NonDAGDatabases += $Database
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# 4 Write Information
|
||
|
Show-ProgressBar -PercentComplete 5 -Status 'Writing HTML Report Header' -Stage 4
|
||
|
|
||
|
$Output = Get-HtmlReportHeader -ExchangeEnvironment $ExchangeEnvironment -Path $MyInvocation.MyCommand.Path
|
||
|
|
||
|
# Sites and Servers
|
||
|
Show-ProgressBar -PercentComplete 20 -Status 'Writing HTML Site Information' -Stage 4
|
||
|
|
||
|
foreach ($Site in $ExchangeEnvironment.Sites.GetEnumerator()) {
|
||
|
$Output+=Get-HtmlOverview -Servers $Site -ExchangeEnvironment $ExchangeEnvironment -ExRoleStrings $ExRoleStrings
|
||
|
}
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 40 -Status 'Writing HTML Pre-2007 Information' -Stage 4
|
||
|
|
||
|
foreach ($FakeSite in $ExchangeEnvironment.Pre2007.GetEnumerator()) {
|
||
|
$Output+=Get-HtmlOverview -Servers $FakeSite -ExchangeEnvironment $ExchangeEnvironment -ExRoleStrings $ExRoleStrings -Pre2007:$true
|
||
|
}
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 60 -Status 'Writing HTML DAG Information' -Stage 4
|
||
|
|
||
|
foreach ($DAG in $ExchangeEnvironment.DAGs) {
|
||
|
|
||
|
if ($DAG.MemberCount -gt 0) {
|
||
|
|
||
|
# Get DAG Header
|
||
|
$Output += Get-HtmlDagHeader -DAG $DAG
|
||
|
|
||
|
# Get Table HTML for DAG databases
|
||
|
$Output += Get-HtmlDatabaseInformationTable -Databases $DAG.Databases
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($ExchangeEnvironment.NonDAGDatabases.Count) {
|
||
|
|
||
|
Show-ProgressBar -PercentComplete 80 -Status 'Writing HTML Non-DAG Database Information' -Stage 4
|
||
|
|
||
|
$Output+='<table class="dagsummary">
|
||
|
<tr class="dagsummarynondag"><th>Mailbox Databases (Non-DAG)</th></table>'
|
||
|
|
||
|
# Get Table HTML for non-DAG databases
|
||
|
$Output+=Get-HtmlDatabaseInformationTable -Databases $ExchangeEnvironment.NonDAGDatabases
|
||
|
}
|
||
|
|
||
|
|
||
|
# End
|
||
|
Show-ProgressBar -PercentComplete 90 -Status 'Finishing off..' -Stage 4
|
||
|
|
||
|
$Output+='</body></html>'
|
||
|
|
||
|
# 2019-05-20 TST Updated to ensure script path as storage location
|
||
|
$HtmlReportFullPath = Join-Path -Path (Split-Path -Path $script:MyInvocation.MyCommand.Path) -ChildPath $HTMLReport
|
||
|
|
||
|
$Output | Out-File -FilePath $HtmlReportFullPath -Force -Encoding utf8
|
||
|
|
||
|
|
||
|
if ($SendMail)
|
||
|
{
|
||
|
Show-ProgressBar -PercentComplete 95 -Status 'Sending mail message..' -Stage 4
|
||
|
|
||
|
# 2019-05-17 TST, Changed to .NET send method to work as scheduled job
|
||
|
|
||
|
$smtpMail = New-Object Net.Mail.SmtpClient($MailServer)
|
||
|
|
||
|
$smtpMessage = New-Object System.Net.Mail.MailMessage $MailFrom, $MailTo
|
||
|
|
||
|
if(Test-Path -Path $HtmlReportFullPath) {
|
||
|
$smtpAttachment = New-Object Net.Mail.Attachment($HtmlReportFullPath, 'text/plain')
|
||
|
$smtpMessage.Attachments.Add($smtpAttachment)
|
||
|
}
|
||
|
|
||
|
$smtpMessage.Subject = 'Exchange Environment Report'
|
||
|
$smtpMessage.Body = $Output
|
||
|
$smtpMessage.IsBodyHtml = $true
|
||
|
|
||
|
$smtpMail.Send($smtpMessage)
|
||
|
|
||
|
Return 0
|
||
|
}
|