Generate Exchange Environment Reports

Generate Exchange Environment Reports Using PowerShell

As an Exchange administrator, there’s times when it’s useful to have a visual, straightforward and concise document that gives you a good overview of your environment. Although with tools like Visio and Word you can make such a document, it’s hard to keep these documents up to date or use previous versions to track and check changes.



This script, inspired by the output of an Exchange TAP tool, aims to automatically generate a report that gives you an overview of your environment, Exchange 2007 and 2010 servers and database availability groups - in particular:
  • Total Servers per Exchange version & service pack
  • Total Mailboxes per Exchange version & service pack
  • Totals for Exchange roles across the environment
  • A site-by-site breakdown for the following:
    • Mailboxes per site
    • Exchange servers, version, update rollup (new), service level, highlighted installed roles, OS version and service pack
  • A breakdown of each Database Availability Group including:
    • DAG name, member count and member list
    • Database information such as
      • Name
      • Mailboxes per database and Average Size
      • Archive mailboxes per database and Average Size (new) - only shown if a DAG DB includes Archive mailboxes
      • Database and whitespace size
      • Last full backup date/time (new) - only shown if at least one DAG DB has had a full backup
      • Circular Logging state (new) - only shown if at least one DAG DB has circular logging enabled
      • Server hosting the active copy
      • List of servers hosting copies and copy count
  • A breakdown of Non-DAG databases including Exchange 2007 DBs, including the database information above, along with Storage Group name (where applicable). (new)
The script doesn’t support detailed information about Exchange 2007 CCR/SCC clusters, but these are shown as ClusMBX in the output. At the moment, the script doesn’t show Public Folder information but if there is interest I can add extra features; and of course the source is provided should you wish to alter it to your own needs.

To be able to execute the script, you need to use the Exchange Management Shell (the latest version for your environment, with Powershell 2.0) and be able to get information about AD Sites, Exchange Servers, Mailboxes, Database Availability Groups and Databases. It uses WMI to retrieve OS information and detect Exchange 2007 clusters and calculate Exchange 2007 database size and Remote Registy calls to get Update Rollup information. A normal Exchange administrator should be able to perform these tasks.
Executing the script is straightforward – the only setting you need is to specify where to write the HTML file:

.\Get-ExchangeEnvironmentReport  -HTMLReport c:\report.html

You can if you like set this up on an automatic schedule and get it emailed daily. If you want to see how, I’ve uploaded a quick video here.

As usual, the script is provided below as-is without any warranties. You can download the script ready to use at the bottom of this post. Comments and suggestions are always welcome.
<#
    .SYNOPSIS
    Creates a HTML Report describing the Exchange 2010 and Exchange 2007 environment

    Steve Goodman
 
    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.
 
    .DESCRIPTION
    This script creates a HTML report showing the following information about an Exchange 2010
    and to a lesser extent, 2007, environment. The following is shown:
 
    * Report Generation Time
    * Total Servers per Exchange Version (2007 SP2 > 2010 SP2)
    * Total Mailboxes per Exchange Version and Organisation
    * Total Roles in the environment
     
    Then, per site:
    * Total Mailboxes per site
    * Exchange Servers with:
        o Exchange Server Version
        o Service Pack
        o Update Rollup
        o Roles installed on server and mailbox counts
        o OS Version and Service Pack
     
    Then, per Database availability group (Exchange 2010):
    * 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 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)
    * 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 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 CCR/SCC clusters
    (although it attempts to detect Clustered Exchange 2007 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 cluster and DB size information.
 
    .PARAMETER HTMLReport
    Filename to write HTML Report to

    .EXAMPLE
    Generate the HTML report
    .\Get-ExchangeEnvironmentReport.ps1 -HTMLReport .\report.html
 
    #>
param(
    [parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false,HelpMessage="Filename to write HTML report to")][string]$HTMLReport    )

# Check Powershell Versionif ((Get-Host).Version.Major -eq 1)
{
    throw "Powershell Version 1 not supported";
}

# Check Exchange Management Shell is runningif (!(Get-Command Get-ExchangeServer -ErrorAction SilentlyContinue))
{
    throw "Please launch the Exchange Management Shell"}

# Check to find out if we're running from the Exchange 2007 management console$ExchangeVersion = $null;
if ((Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.Admin -ErrorAction SilentlyContinue))
{
    $ExchangeVersion = "2007";
    # Don't allow this to run from an Exchange 2007 shell when Exchange 2010 servers exist.    if (Get-ExchangeServer | Where {$_.AdminDisplayVersion.Major -eq 14})
    {
        throw "Exchange 2010 detected. Please run this script from an Exchange 2010 management shell"    }
}else{
    $ExchangeVersion = "2010";
}

# View entire forestif ($ExchangeVersion -eq "2010")
{
    Set-ADServerSettings -ViewEntireForest:$true;
} else {
    $global:AdminSessionADSettings.ViewEntireForest = $true;
}

# Hashtable to update with environment data$ExchangeEnvironment = @{};

# Overall Total$ExchangeEnvironment.Add("TotalMailboxes",0);

# Totals for versions$ExchangeEnvironment.Add("TotalMailboxesByVersion",@{});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("8.0",@{"ServerCount"=0;"MailboxCount"=0});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("8.1",@{"ServerCount"=0;"MailboxCount"=0});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("8.2",@{"ServerCount"=0;"MailboxCount"=0});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("8.3",@{"ServerCount"=0;"MailboxCount"=0});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("14.0",@{"ServerCount"=0;"MailboxCount"=0});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("14.1",@{"ServerCount"=0;"MailboxCount"=0});
$ExchangeEnvironment["TotalMailboxesByVersion"].Add("14.2",@{"ServerCount"=0;"MailboxCount"=0});
# Totals by role$ExchangeEnvironment.Add("TotalsServersByRole",@{});
$ExchangeEnvironment["TotalsServersByRole"].Add("ClusteredMailbox",0);
$ExchangeEnvironment["TotalsServersByRole"].Add("Mailbox",0);
$ExchangeEnvironment["TotalsServersByRole"].Add("ClientAccess",0);
$ExchangeEnvironment["TotalsServersByRole"].Add("HubTransport",0);
$ExchangeEnvironment["TotalsServersByRole"].Add("Edge",0);
$ExchangeEnvironment["TotalsServersByRole"].Add("UnifiedMessaging",0);
# Sites Hashtable$ExchangeEnvironment.Add("Sites",@());
# DAGs Hashtable$ExchangeEnvironment.Add("DAGs",@());
# Non-DAG Database Hashtable$ExchangeEnvironment.Add("NonDAGDatabases",@{});
# Public Folder Database Hashtable
#$ExchangeEnvironment.Add("PFDatabases",@());

# Internal number to human readable string mapping
$StringMapping = @{};
$StringMapping.Add("8.0","E12RTM");
$StringMapping.Add("8.1","E12SP1");
$StringMapping.Add("8.2","E12SP2");
$StringMapping.Add("8.3","E12SP3");
$StringMapping.Add("14.0","E14RTM");
$StringMapping.Add("14.1","E14SP1");
$StringMapping.Add("14.2","E14SP2");
$StringMapping.Add("8.0-long","Exchange 2007 (E12) RTM");
$StringMapping.Add("8.1-long","Exchange 2007 (E12) SP1");
$StringMapping.Add("8.2-long","Exchange 2007 (E12) SP2");
$StringMapping.Add("8.3-long","Exchange 2007 (E12) SP3");
$StringMapping.Add("14.0-long","Exchange 2010 (E14) RTM");
$StringMapping.Add("14.1-long","Exchange 2010 (E14) SP1");
$StringMapping.Add("14.2-long","Exchange 2010 (E14) SP2");
$StringMapping.Add("ClusteredMailbox","ClusMBX");
$StringMapping.Add("Mailbox","MBX");
$StringMapping.Add("ClientAccess","CAS");
$StringMapping.Add("HubTransport","HUB");
$StringMapping.Add("UnifiedMessaging","UM");
$StringMapping.Add("Edge","EDGE");

# Generic DB Info Function used by DAG loop and standalone mailbox loopfunction _GetDBInfo
{
    param($Database,$ExchangeVersion,$DAGDB=$False,$AllArchiveMailboxes);
    # Database is the actual DB object    # ExchangeVersion is either 2007 or 2010 and used to determine what we can try    # DAGDB is true/false and if true we'll try and get DAG Database info    # ArchiveMailboxes should be the result of Get-Mailbox -Archive -ResultSize Unlimited    # Add new hashtable for this database    $tDBInfo=@{};
    # Active Owner / Server Name    $tDBInfo.Add("ActiveOwner",$Database.Server.Name);
    # Circular Logging State    if ($Database.CircularLoggingEnabled)
    {
        $tDBInfo.Add("CircularLoggingEnabled","Yes");
    } else {
        $tDBInfo.Add("CircularLoggingEnabled","No");
    }
    # Last Full/Incremental    if ($Database.LastFullBackup)
    {
        $tDBInfo.Add("LastFullBackup",$Database.LastFullBackup.ToString());
    } else {
        $tDBInfo.Add("LastFullBackup","Not Available");
    }
    # Get mailbox count    $tDBInfo.Add("MailboxCount",[int]([array](Get-Mailbox -Database $Database -ResultSize Unlimited)).Count);
    # Mean Average Sizes and totals for Mailbox sizes for working out Exchange 2007 DB sizes - Primary Mailboxes    $MeanAverageSize = 0;
    $MailboxStatistics = [array](Get-MailboxStatistics -Database $Database)
    if ($MailboxStatistics)
    {
        $TotalItemSizeKB=0;
        $TotalDeletedItemSize=0;
        # Mean Average        $MailboxStatistics | %{ $TotalItemSizeKB+=$_.TotalItemSize.Value.ToKB() }
        $MailboxStatistics | %{ $TotalDeletedItemSize+=$_.TotalDeletedItemSize.Value.ToKB() }
        [int]$MeanAverageSize = (($TotalItemSizeKB / $MailboxStatistics.Count))/1024;
    }
    $tDBInfo.Add("AverageMailboxSize",$MeanAverageSize);
 
    if ($ExchangeVersion -eq "2010")
    {
        # Exchange 2010 Only Functions        # Get archive mailbox info        $ArchiveMailboxCount = 0;
        if ($AllArchiveMailboxes)
        {
            $ArchiveMailboxes = [array]($AllArchiveMailboxes | Where {$_.ArchiveDatabase -eq $Database.Name})
            if ($ArchiveMailboxes)
            {
                $ArchiveMailboxCount = [int]$ArchiveMailboxes.Count
            }
        }
        $tDBInfo.Add("ArchiveMailboxCount",$ArchiveMailboxCount);
        # Copies (2010 DAG DBs only, doesn't error for others, so no point checking        $tDBInfo.Add("CopyCount",[int]$Database.Servers.Count);
        # Create an array with the names of copies, if there are any (none on 2007)        $tDAGCopies = @();
        if ([int]$Database.Servers.Count -gt 0)
        {
            $Database.Servers | % { $tDAGCopies+=$_.Name };
        }
        $tDBInfo.Add("Copies",$tDAGCopies);
        # Mean Average Sizes - Archive Mailboxes        $MeanAverageSize = 0;
        if ($ArchiveMailboxes -and $ExchangeVersion -eq "2010")
        {
            $MailboxStatistics = [array]($ArchiveMailboxes | Get-MailboxStatistics -Archive )
            if ($MailboxStatistics)
            {
                # Mean Average                $TotalItemSizeKB=0;
                $MailboxStatistics | %{ $TotalItemSizeKB+=$_.TotalItemSize.Value.ToKB() }
                [int]$MeanAverageSize = (($TotalItemSizeKB / $MailboxStatistics.Count))/1024;
            }
         
        }
        $tDBInfo.Add("AverageArchiveMailboxSize",$MeanAverageSize);
        # Only supplied automatically for 2010 DBs only        if ($Database.ExchangeVersion.ExchangeBuild.Major -eq 14)
        {
            # 2010, get the info            # Database Size            $tDBInfo.Add("Size",[int]$Database.DatabaseSize.ToGB());
            # Whitespace            $tDBInfo.Add("Whitespace",[int]$Database.AvailableNewMailboxSpace.ToGB());
            # Set StorageGroup to False            $tDBInfo.Add("StorageGroup",$false);
        } else {
            # 2007, Use WMI (Based on code by Gary Siepser, http://bit.ly/kWWMb3)            # Database Size            $tWMI_EDBFileSizeMB = ([math]::Round(([int64](get-wmiobject cim_datafile -computername $Database.Server.Name -filter ('name=''' + $Database.edbfilepath.pathname.replace("\","\\") + '''')).filesize / 1MB),1))
            $tWMI_EDBFileSizeGB = ([math]::Round(($tWMI_EDBFileSizeMB/1024),1));
            $tDBInfo.Add("Size",$tWMI_EDBFileSizeGB);
            # Whitespace            $tWhiteSpaceGB = ([math]::Round((($tWMI_EDBFileSizeMB - ($TotalItemSizeKB/1024) - ($TotalDeletedItemSize/1024))/1024),1));
            if ($tWhiteSpaceGB -lt 0) # Because I don't think it's 100% accurate, I think it might show minus in certain situations.            {
                $tWhiteSpaceGB=0;
            }
            $tDBInfo.Add("Whitespace",$tWhiteSpaceGB);
            $tDBInfo.Add("StorageGroup",$Database.DistinguishedName.Split(",")[1].Replace("CN=",""));
        }
    } else {
        # Exchange 2007        # No Archive Mailboxes as not supported        $tDBInfo.Add("ArchiveMailboxCount",0);
        # Database Copies. We can't reliably get this for 2007 so we will leave it out (but populate it so it's not null)        $tDBInfo.Add("CopyCount",0);
        $tDBInfo.Add("Copies",@());
        # Use WMI (based on code from Gary Siepser, http://bit.ly/kWWMb3) to get Size        # Database Size        $tWMI_EDBFileSizeMB = ([math]::Round(([int64](get-wmiobject cim_datafile -computername $Database.Server.Name -filter ('name=''' + $Database.edbfilepath.pathname.replace("\","\\") + '''')).filesize / 1MB),1))
        $tWMI_EDBFileSizeGB = ([math]::Round(($tWMI_EDBFileSizeMB/1024),1));
        $tDBInfo.Add("Size",$tWMI_EDBFileSizeGB);
        # Whitespace        $tWhiteSpaceGB = ([math]::Round((($tWMI_EDBFileSizeMB - ($TotalItemSizeKB/1024) - ($TotalDeletedItemSize/1024))/1024),1));
        if ($tWhiteSpaceGB -lt 0) # Because I don't think it's 100% accurate, I think it might show minus in certain situations.        {
            $tWhiteSpaceGB=0;
        }
        $tDBInfo.Add("Whitespace",$tWhiteSpaceGB);
        # No Archives so no mean average size        $tDBInfo.Add("AverageArchiveMailboxSize",0)
        $tDBInfo.Add("StorageGroup",$Database.StorageGroup.Name);
    }
    # Return DB Info    $tDBInfo;
}

# Get All Archive Mailboxes for later use when getting DB infoif ($ExchangeVersion -eq "2010")
{
    Write-Progress -id 1 -activity "Get-ExchangeEnvironmentReport" -status "Collating Archive Mailbox Information" -percentComplete 1;
    $AllArchiveMailboxes = [array](Get-Mailbox -Archive -ResultSize Unlimited)
} else {
    $AllArchiveMailboxes = $null;
}

# Get sites$Sites=[array](Get-ADSite);
$i=0;
foreach ($Site in $Sites)
{
    Write-Progress -id 1 -activity "Get-ExchangeEnvironmentReport" -status "Determing information for site: $($Site.Name.ToUpper())" -percentComplete (($i/$Sites.Count*100)*0.3)
    $i++;
    # Temporary Site Hashtable    $tSite = @{};
    $tSite.Add("Name",$Site.Name.ToUpper());
    $tSite.Add("Servers",@());
    $tSiteMailboxCount=0;
    # Loop through Exchange Servers    $p=0;
    $ExchangeServers = [array](Get-ExchangeServer | Where {$_.Site -eq $Site.Identity -and $_.AdminDisplayVersion.Major -ge 8} | Sort Name);
    # Only look at this site if we've got Exchange Servers in it..    if ($ExchangeServers)
    {
        foreach ($ExchangeServer in $ExchangeServers)
        {
            Write-Progress -id 2 -activity "Exchange Server Information" -status "Determing Exchange information for: $($ExchangeServer.Name.ToUpper())" -percentComplete ($p/$ExchangeServers.Count*100);
            $p++;
            # Temporary Servers Hashtable            $tServer = @{};
            $tServer.Add("Name",$ExchangeServer.Name.ToUpper());
            $tServer.Add("RealName",$ExchangeServer.Name.ToUpper());
            $tServer.Add("Edition",$ExchangeServer.Edition);
            $tServer.Add("Mailboxes",0);
            $tServer.Add("Version","$($ExchangeServer.AdminDisplayVersion.Major).$($ExchangeServer.AdminDisplayVersion.Minor)");
            $tCount = 0;
            # Get WMI OS Information            $tWMI = Get-WmiObject Win32_OperatingSystem -ComputerName $ExchangeServer.Name
            $tServer.Add("OSVersion",$tWMI.Caption.Replace("(R)","").Replace("Microsoft ","").Replace("Enterprise","Ent").Replace("Standard","Std").Replace(" Edition",""));
            $tServer.Add("OSServicePack",$tWMI.CSDVersion)
         
            # Get Rollup Info (Thanks to Bhargav Shukla http://bit.ly/msxGIJ)            $RollupLevel=0;
            if ($ExchangeServer.AdminDisplayVersion.Major -eq 14)
            {
                $InstallerKey = "AE1D439464EB1B8488741FFA028E291C";
            } else {
                $InstallerKey = "461C2B4266EDEF444B864AD6D9E5B613";
            }
            $RegKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\$($InstallerKey)\\Patches";
            $RemoteRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ExchangeServer.Name);
            $RUKeys= $RemoteRegistry.OpenSubKey($RegKey).GetSubKeyNames() | ForEach {"$RegKey\\$_"}
            if ($RUKeys)
            {
                $DisplayNames = [array] ($RUKeys | %{$RemoteRegistry.OpenSubKey($_).getvalue("DisplayName")})
                foreach ($DisplayName in $DisplayNames)
                {
                    if ($DisplayName -like "Update Rollup *")
                    {
                        $Rollup=$DisplayName.Split(" ")[2];
                        if ($Rollup -like "*-*")
                        {
                            $Rollup = $Rollup.Split("-")[0]
                        }
                        if ($Rollup -gt $RollupLevel)
                        {
                            $RollupLevel=$Rollup;
                        }
                    }
                }
            }
            $tServer.Add("RollupLevel",$RollupLevel)
            # End RollUp Info         
            # Get an array with the roles this Exchange Server has            [array]$Roles = $ExchangeServer.ServerRole.ToString().Replace(" ","").Split(",");
            # Check if this is a Mailbox server            if ($Roles -contains "Mailbox")
            {
                $tCount = (Get-Mailbox -Server $ExchangeServer -ResultSize Unlimited).Count
                $tServer["Mailboxes"]=$tCount                # Check if it's Exchange 2007                if ($tServer["Version"] -like "8.*")
                {
                    # Assume it's a clustered mailbox server if the exchange server name doesn't match the actual server name                    if ($ExchangeServer.Name -ne $tWMI.CSName)
                    {
                        $Roles = [array]($Roles | Where {$_ -ne "Mailbox"})
                        $Roles += "ClusteredMailbox"                        $tServer["RealName"]=$tWMI.CSName.ToUpper();
                    }
                 
                }
            }
            foreach ($Role in $Roles)
            {
                $ExchangeEnvironment["TotalsServersByRole"][$Role]++;
            }
         
            $tServer.Add("Roles",$Roles);
         
            # Update Totals            $ExchangeEnvironment["TotalMailboxes"]+=$tCount;
            $ExchangeEnvironment["TotalMailboxesByVersion"][$tServer["Version"]]["MailboxCount"]+=$tCount;
            $ExchangeEnvironment["TotalMailboxesByVersion"][$tServer["Version"]]["ServerCount"]++;
            $tSiteMailboxCount+=$tCount;
         
            # Add Server to Site         
            $tSite["Servers"]+=$tServer;
        }
        $tSite.Add("TotalMailboxes",$tSiteMailboxCount);
     
        $ExchangeEnvironment["Sites"]+=$tSite;
    }
}

# Database Availability Group Informationif ($ExchangeVersion -eq "2010")
{
    # Get Database Availability Groups    $DAGS = [array](Get-DatabaseAvailabilityGroup)
    $i=0;
    if ($DAGS)
    {
        foreach ($DAG in $DAGS)
        {
            Write-Progress -id 1 -activity "Get-ExchangeEnvironmentReport" -status "Determing information for DAG: $($DAG.Name.ToUpper())" -percentComplete ((($i/$DAGS.Count*100)*0.3)+30)
            $i++;
            # Current DAG Temp Hashtable            $tDAG=@{}
            # Get name and members            $tDAG.Add("Name",$DAG.Name.ToUpper());
            $tDAG.Add("MemberCount",$DAG.Servers.Count);
            $tDatabases=@{};
            $tDAGMembers=@();
            $DAG.Servers | % { $tDAGMembers+=$_.Name }
            $tDAG.Add("Members",$tDAGMembers);
            $p=0;
            # Look at each member's databases            foreach ($Member in $tDAGMembers)
            {
                Write-Progress -id 2 -activity "DAG Server Information" -status "Determing database information for: $($Member)" -percentComplete ($p/$tDAGMembers.Count*100);
                $p++;
                foreach ($Database in (Get-MailboxDatabase -Server $Member -Status))
                {
                    # Check if first time we've seen this database                    if (!$tDatabases.Contains($Database.Name))
                    {
                        # Yes, it's the first time                        $tDBInfo = _GetDBInfo -Database $Database -ExchangeVersion $ExchangeVersion -DAGDB:$True -AllArchiveMailboxes $AllArchiveMailboxes                        # Add the hashtable to the database list:                        $tDatabases.Add($Database.Name,$tDBInfo);
                    }
                }
            }
            # Add the database list to the DAG            $tDAG.Add("Databases",$tDatabases);
            $ExchangeEnvironment["DAGs"]+=$tDAG;
        }
    }
}

# Non-DAG / Exchange 2007 Mailbox Database informationif ($ExchangeVersion -eq "2010")
{
    $NonDagDBs = [array](Get-MailboxDatabase -IncludePreExchange2010 -Status | Where {$_.MasterType -ne "DatabaseAvailabilityGroup" -and $_.ExchangeVersion.ExchangeBuild.Major -ge 8} | Sort Name)
} else {
    $NonDagDBs = [array](Get-MailboxDatabase -Status | Where {$_.ExchangeVersion.ExchangeBuild.Major -ge 8} | Sort Name)
}
if ($NonDagDBs)
{
    $i=0;
    foreach ($Database in $NonDagDBs)
    {
        Write-Progress -id 1 -activity "Get-ExchangeEnvironmentReport" -status "Determing information for Non-DAG Database: $($Database.Name)" -percentComplete ((($i/$NonDagDBs.Count*100)*0.3)+70)
        $i++;
        $tDBInfo = _GetDBInfo -Database $Database -ExchangeVersion $ExchangeVersion -DAGDB:$False -AllArchiveMailboxes $AllArchiveMailboxes        $ExchangeEnvironment["NonDAGDatabases"].Add($Database.Name,$tDBInfo);
    }
}

# HTML Output

# Setup Column  Span for header based on how many different versions of Exchange we have
$ColSpan=0;
$ExchangeEnvironment["TotalMailboxesByVersion"].GetEnumerator()|Sort Name| %{if ($_.Value["ServerCount"] -gt 0) { $ColSpan++}};

# Header$Output="<html>
<body>
<font size="
"1"" face=""Arial,sans-serif"
">
<h3 align="
"center"
">Exchange Environment Report</h3>
<h5 align="
"center"
">Generated $((Get-Date).ToString())</h5>
</font>
<table border="
"0"" cellpadding=""3"" style=""font-size:8pt;font-family:Arial,sans-serif"
">
<tr bgcolor="
"#009900"
">
<th colspan="
"$($ColSpan)""><font color=""#ffffff"
">Total Servers:</font></th>
<th colspan="
"$($ColSpan+1)""><font color=""#ffffff"
">Total Mailboxes:</font></th>
<th colspan="
"$($ExchangeEnvironment["
TotalsServersByRole"].Count)""><font color=""#ffffff"">Total Roles:</font></th></tr>
<tr bgcolor="
"#00CC00"">"
;
# Show Column Headings based on the Exchange versions we have$ExchangeEnvironment["TotalMailboxesByVersion"].GetEnumerator()|Sort Name| %{if ($_.Value["ServerCount"] -gt 0) { $Output+="<th>$($StringMapping[$_.Key])</th>" ; }};
$ExchangeEnvironment["TotalMailboxesByVersion"].GetEnumerator()|Sort Name| %{if ($_.Value["ServerCount"] -gt 0) { $Output+="<th>$($StringMapping[$_.Key])</th>" ; }};
$Output+="<th>Org</th>";
$ExchangeEnvironment["TotalsServersByRole"].GetEnumerator()|Sort Name| %{$Output+="<th>$($StringMapping[$_.Key])</th>"};
$Output+="<tr>";
$Output+="<tr align=""center"" bgcolor=""#dddddd"">";
# Show columns based on the Exchange versions we have$ExchangeEnvironment["TotalMailboxesByVersion"].GetEnumerator()|Sort Name| %{if ($_.Value["ServerCount"] -gt 0) { $Output+="<td>$($_.Value["ServerCount"])</td>"}};
$ExchangeEnvironment["TotalMailboxesByVersion"].GetEnumerator()|Sort Name| %{if ($_.Value["ServerCount"] -gt 0) { $Output+="<td>$($_.Value["MailboxCount"])</td>"}};
$Output+="<td>$($ExchangeEnvironment["TotalMailboxes"])</td>";
$ExchangeEnvironment["TotalsServersByRole"].GetEnumerator()|Sort Name| %{$Output+="<td>$($_.Value)</td>"};
$Output+="</tr><tr><tr></table><br>";

# Sites and Serversforeach ($Site in $ExchangeEnvironment["Sites"].GetEnumerator())
{
    $Output+="<table border=""0"" cellpadding=""3"" width=""100%"" style=""font-size:8pt;font-family:Arial,sans-serif"">
    <col width="
"20%""><col width=""20%"
">
    <colgroup width="
"25%""><col width=""4%""><col width=""4%""><col width=""3%""><col width=""3%"
">
    <col width="
"3%""><col width=""3%""></colgroup><col width=""20%""><col  width=""20%"
">
    <tr bgcolor="
"#000099""><th><font color=""#ffffff"">Site: $($Site["
Name"])</font></th>
    <th><font color="
"#ffffff"
">Exchange Version</font></th>
    <th colspan="
"6""><font color=""#ffffff"
">Roles Installed</font></th>
    <th><font color="
"#ffffff"
">OS Version</font></th>
    <th><font color="
"#ffffff"
">OS Service Pack</font></th></tr>
    <tr bgcolor="
"#0000FF""><th><font color=""#ffffff"">Mailboxes: $($Site["
TotalMailboxes"])</font></th><th></th>";
    $ExchangeEnvironment["TotalsServersByRole"].GetEnumerator()|Sort Name| %{$Output+="<th><font color=""#ffffff"">$($StringMapping[$_.Key])</font></th>";}
    $Output+="<th></th><th></th></tr>";
    $AlternateRow=0;
    foreach ($Server in $Site["Servers"])
    {
        $Output+="<tr ";
        if ($AlternateRow)
        {
            $Output+=" style=""background-color:#dddddd""";
            $AlternateRow=0;
        } else        {
            $AlternateRow=1;
        }
        $Output+="><td>$($Server["Name"])";
        if ($Server["RealName"] -ne $Server["Name"])
        {
            $Output+=" ($($Server["RealName"]))";
        }
        $Output+="</td>
        <td>$($StringMapping["
$($Server["Version"])-long"])";
        if ($Server["RollupLevel"] -gt 0)
        {
            $Output+=" UR$($Server["RollupLevel"])";
        }
        $Output+="</td>";
        $ExchangeEnvironment["TotalsServersByRole"].GetEnumerator()|Sort Name| %{
            $Output+="<td";
            if ($Server["Roles"] -contains $_.Key)
            {
                $Output+=" align=""center"" style=""background-color:#00FF00"""            };
            $Output+=">";
            if ($_.Key -eq "Mailbox" -and $Server["Roles"] -contains $_.Key)
            {
                $Output+=$Server["Mailboxes"];
            }
            if ($_.Key -eq "ClusteredMailbox" -and $Server["Roles"] -contains $_.Key)
            {
                $Output+=$Server["Mailboxes"];
            }
        }
             
        $Output+="<td>$($Server["OSVersion"])</td><td>$($Server["OSServicePack"])</td></tr>";    
    }
    $Output+="<tr></tr>
    </table><br />"
;
}
foreach ($DAG in $ExchangeEnvironment["DAGs"])
{
    # 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;
    foreach ($Database in ($DAG["Databases"].GetEnumerator() | Sort Name))
    {
        if ($Database.Value["ArchiveMailboxCount"] -gt 0)
        {
            $ShowArchiveDBs=$True;
        }
        if ($Database.Value["LastFullBackup"] -ne "Not Available")
        {
            $ShowLastFullBackup=$True;
        }
        if ($Database.Value["CircularLoggingEnabled"] -eq "Yes")
        {
            $ShowCircularLogging=$True;
        }    
    }
    # Database Availability Group Header    $Output+="<table border=""0"" cellpadding=""3"" width=""100%"" style=""font-size:8pt;font-family:Arial,sans-serif"">
    <col width="
"20%""><col width=""10%""><col width=""70%"
">
    <tr align="
"center"" bgcolor=""#FF8000 "
"><th>Database Availability Group Name</th><th>Member Count</th>
    <th>Database Availability Group Members</th></tr>
    <tr><td>$($DAG["
Name"])</td><td align=""center"">
    $($DAG["
MemberCount"])</td><td>";
    $DAG["Members"] | % { $Output+="$($_) " }
    $Output+="</td></tr></table>";
    # DAG Databases    $Output+="<table border=""0"" cellpadding=""3"" width=""100%"" style=""font-size:8pt;font-family:Arial,sans-serif"">
    <col width="
"15%"">    <col width=""5%""><col width=""10%"">    <col width=""5%"">  <col width=""10%"">    <col width=""15%"">        <col width=""15%"">      <col width=""5%"">       <col width=""10%"">  <col width=""10%"
">
    <tr align="
"center"" bgcolor=""#FFD700"
">
    <th>Database Name</th><th>Mailboxes</th><th>Av. Mailbox Size</th>"
;
    if ($ShowArchiveDBs)
    {
        $Output+="<th>Archive MBs</th><th>Av. Archive Size</th>";
    }
    $Output+="<th>DB Size/Whitespace</th>";
    if ($ShowLastFullBackup)
    {
        $Output+="<th>Last Full Backup</th>";
    }
    if ($ShowCircularLogging)
    {
        $Output+="<th>Circular Logging</th>";
    }
    $Output+="<th>Active Owner</th><th>Copies (n)</th></tr>";
    $AlternateRow=0;
    foreach ($Database in ($DAG["Databases"].GetEnumerator() | Sort Name))
    {
        $Output+="<tr";
        if ($AlternateRow)
        {
            $Output+=" style=""background-color:#dddddd""";
            $AlternateRow=0;
        } else        {
            $AlternateRow=1;
        }
        $Output+=">
        <td>$($Database.Key)</td>
        <td align="
"center"">$($Database.Value["
MailboxCount"])</td>
        <td align="
"center"">$($Database.Value["
AverageMailboxSize"]) MB</td>";
        if ($ShowArchiveDBs)
        {
            $Output+="<td align=""center"">$($Database.Value["ArchiveMailboxCount"])</td>
            <td align="
"center"">$($Database.Value["
AverageArchiveMailboxSize"]) MB</td>";
        }
        $Output+="<td align=""center"">$($Database.Value["Size"])/$($Database.Value["Whitespace"]) (GB)</td>";
        if ($ShowLastFullBackup)
        {
            $Output+="<td align=""center"">$($Database.Value["LastFullBackup"])</td>";
        }
        if ($ShowCircularLogging)
        {
            $Output+="<td align=""center"">$($Database.Value["CircularLoggingEnabled"])</td>";
        }
        $Output+="<td>$($Database.Value["ActiveOwner"])</td>
        <td>$($Database.Value["
Copies"]|%{$_}) ($($Database.Value["CopyCount"]))</td>
        </tr>"
;
    }
    $Output+="</table><br />";
 
}
if ($ExchangeEnvironment["NonDAGDatabases"].Count)
{
    # 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;
    foreach ($Database in ($ExchangeEnvironment["NonDAGDatabases"].GetEnumerator()| Sort Name))
    {
        if ($Database.Value["ArchiveMailboxCount"] -gt 0)
        {
            $ShowArchiveDBs=$True;
        }
        if ($Database.Value["LastFullBackup"] -ne "Not Available")
        {
            $ShowLastFullBackup=$True;
        }
        if ($Database.Value["CircularLoggingEnabled"] -eq "Yes")
        {
            $ShowCircularLogging=$True;
        }    
    }
    # Non-DAG Databases Output    $Output+="<table border=""0"" cellpadding=""3"" width=""100%"" style=""font-size:8pt;font-family:Arial,sans-serif"">
        <tr bgcolor="
"#FF8000""><th colspan = ""9"">Mailbox Databases"
;
    # If we're on 2010, signify that these databases are not DAG DBs    if ($ExchangeVersion -eq "2010")
    {
        $Output+=" (Non-DAG)";
    }
    $Output+="</th></tr>";
    $Output+="<col width=""15%""><col width=""10%""><col width=""10%""><col width=""10%""><col width=""10%""><col width=""10%""><col width=""15%""><col width=""15%""><col width=""15%"">
        <tr align="
"center"" bgcolor=""#FFD700"
">
        <th>Database Name</th><th>Server</th><th>Mailboxes</th><th>Av. Mailbox Size</th>"
;
    if ($ShowArchiveDBs)
    {
        $Output+="<th>Archives</th><th>Av. Archive Size</th>";
    }
    $Output+="<th>DB Size/Whitespace</th>";
    if ($ShowLastFullBackup)
    {
        $Output+="<th>Last Full Backup</th>";
    }
    if ($ShowCircularLogging)
    {
        $Output+="<th>Circular Logging</th></tr>";
    }
    $AlternateRow=0;
    foreach ($Database in ($ExchangeEnvironment["NonDAGDatabases"].GetEnumerator()| Sort Name))
    {
     
        $Output+="<tr";
        if ($AlternateRow)
        {
            $Output+=" style=""background-color:#dddddd""";
            $AlternateRow=0;
        } else        {
            $AlternateRow=1;
        }
        $Output+="><td>";
        if ($Database.Value["StorageGroup"])
        {
            $Output+="$($Database.Value["StorageGroup"])\";
        }
        $Output+="$($Database.Key)</td>
        <td>$($Database.Value["
ActiveOwner"])</td>
        <td align="
"center"">$($Database.Value["
MailboxCount"])</td>
        <td align="
"center"">$($Database.Value["
AverageMailboxSize"]) MB</td>";
        if ($ShowArchiveDBs)
        {
            $Output+="<td align=""center"">$($Database.Value["ArchiveMailboxCount"])</td>
            <td align="
"center"">$($Database.Value["
AverageArchiveMailboxSize"]) MB</td>";
        }
        $Output+="<td align=""center"">$($Database.Value["Size"]) / $($Database.Value["Whitespace"]) (GB)</td>";
        if ($ShowLastFullBackup)
        {
            $Output+="<td align=""center"">$($Database.Value["LastFullBackup"])</td>";
        }
        if ($ShowCircularLogging)
        {
            $Output+="<td align=""center"">$($Database.Value["CircularLoggingEnabled"])</td>";
        }
        $Output+="</tr>";
    }
    $Output+="</table>";
}

# End$Output+="</body></html>";
$Output | Out-File $HTMLReport

Download Get-ExchangeEnvironmentReport.ps1