Skip to the main content.

Documenting Exchange Server with PowerShell

Table of Contents

Post Featured Image

Documenting a working (or not?) environment is an important task usually undertaken by administrators and consultants to get a better idea of what is configured for that environment and Exchange Server 201x is no different. Proper maintenance, migration and business continuity activities can depend heavily on this documentation and having as much and as accurate a sense of what is configured will assist in those activities/tasks. So how do we document an Exchange Server environment? A combination of Get cmdlets and export files - txt, csv and HTML - will allow us to create proper documentation of an environment.

Caveat: This article will not walk though how to document each and every item in Exchange as there are far too many moving parts. However, the article will concentrate on some of the most important aspects to document.

Building the Framework

Before diving into the documentation cmdlets, we need to build our framework to capture this data, and this will consist of creating or defining output files as well as other variables we will need. First, some output from our cmdlets could be wide, very wide. As such we may need to adjust the buffer size of the window so we don’t miss out on any details.

Use this one-liner to set the PowerShell window Buffer Size to allow for wide output:

$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size (500, 9999)

Next, we need to define a main output destination file and file path:

$Path = (Get-Item -Path ".\" -Verbose).FullName
$File = "ExchangeServerInformation.Txt"
$Destination = $Path+"\"+$File

Exchange Servers contain a wide variety of data and configuration information that can be queried with PowerShell and depending on the size of the environment, you may want to create additional files to help keep this data separated. For example, an environment with 100 mailboxes may be able to export all settings and data to a single file as it would be easy to work with, however, an environment with 10,000 + mailboxes will find this rather restrictive and may wish to define files for each separate component. Some example log files are shown below (predefined prior to running Get cmdlets):

Sample data collection about user's mailboxes:

$ArchiveMailboxDestination = $Path+"\ArchiveMailboxStatistics.txt"
$CASMailboxDestination = $Path+"\CASMailboxProperties.csv"
$MailboxStatDestination = $Path+"\MailboxStatistics.txt"

Sample server configuration files:

$ExchangeDatabaseDestination = $Path+"\ExchangeDatabaseInformation-Detailed.txt"
$NICInfoCSVDestination = $Path+"\ExchangeServerNICConfiguration.csv"

The list above is hardly wholistic, but a good starting place for you to start building your own scripts.

Exploring and Exporting Config Data

Exchange is a complex system, but it can be broken down into components that will allow for a more organized approach for data gathering. Breaking it up into chunks does require some extensive knowledge of Exchange and then knowledge of the backend PowerShell cmdlets that will help uncover data and provide reports for reporting purposes. Let’s break an Exchange environment down into the working parts and create some sample documentation.

Sample Breakdown

Exchange Server Setup: Number of Servers, Version, Patch Level, Databases, URLs
Windows Server Configuration: Hardware or Resources (VM), Pagefile, Event Logs, Version, Disk Size
User Mailboxes: Mailbox Statistics, Archive Mailbox Statistics, Protocols Allowed, and Mail Contacts
Exchange Software settings: Email Address Policies, Address Book Policies, Authentication Policies, Receive Connectors, Send Connectors, DLP Policies, etc.

Note: Know your environment. If there are more than a thousand of anything, make sure to use ‘-ResultSize Unlimited’ like it’s going out of style.

Exchange Server Setup

First, let’s explore the configuration of Exchange Server itself.

Version, Edition, Role and Site

$ExchangeServers = Get-ExchangeServer
$LocalExchangeServer = $Env:ComputerName
$ServerVersionCheck = $True
Foreach ($ExchangeServer in $ExchangeServers) {
$Server = $ExchangeServer.Name
$Site = $ExchangeServer.Site
$ServerRole = $ExchangeServer.ServerRole
$Edition = $ExchangeServer.Edition
If ($Server -ne $LocalExchangeServer) {
$Version = Invoke-Command -ComputerName $Server -ScriptBlock {$Ver = Get-Command Exsetup.exe | ForEach-Object {$_.FileversionInfo};$Version = $Ver.FileVersion;$Version} -ErrorAction Stop
} Else {
$Ver = Get-Command Exsetup.exe | ForEach-Object {$_.FileversionInfo}
$Version = $Ver.FileVersion
$Line = "$Server|"+"$Version|"+"$Edition|"+"$ServerRole|"+"$Site" | Out-file $Destination -Append

Results from the script would look something like this:

Results from the script Results from the script 

Useful for verifying server versions as well as what roles are installed on the server.

Mailbox Databases

Next, we’ll pull some basic information from our Exchange databases:

Get-MailboxDatabase | FT Name, Server, Recovery, ReplicationType, LogFolderPath, EdbFilePath, DeletedItemRetention, MailboxRetention -Auto | Out-file $Destination -Append

Sample output:

sample output
Sample output from our Exchange databases


Important for client connections and outside processes, having the correct URL configuration for Exchange servers is important and can lead to a better experience for the end user. The below code block queries each URL type and exports results to a file.

$Line = Get-OWAVirtualDirectory -ADPropertiesOnly |ft Server,*lurl* -Auto | Out-file $Destination -Append
$Line = Get-WebServicesVirtualDirectory -ADPropertiesOnly |ft Server,*lurl* -Auto | Out-file $Destination -Append
$Line = Get-ActiveSyncVirtualDirectory -ADPropertiesOnly |ft Server,*lurl* -Auto | Out-file $Destination -Append
$Line = Get-AutoDiscoverVirtualDirectory -ADPropertiesOnly |ft Server,*lurl* -Auto | Out-file $Destination -Append
$Line = Get-MAPIVirtualDirectory -ADPropertiesOnly |ft Server,*lurl* -Auto | Out-file $Destination -Append
$Line = Get-OABVirtualDirectory -ADPropertiesOnly |ft Server,*lurl* -Auto | Out-file $Destination -Append
$Line = Get-ClientAccessService | ft Name,*uri* -Auto | Out-file $Destination -Append


Windows Server Configuration

Hardware or Resources (VM), Pagefile, Event Logs, OS Version

Another crucial part of an Exchange server is the configuration of the underlying Windows Server that it runs on.

Processor Cores – Logical and Physical

Next, check the number of logical and physical processors. This can be used for documentation, as well as verifying that Hyperthreading is not in use.

$Processors = Get-WMIObject Win32_Processor -ComputerName $Server
$LogicalCPU = ($Processors | Measure-Object -Property NumberOfLogicalProcessors -sum).Sum
$PhysicalCPU = ($Processors | Measure-Object -Property NumberOfCores -sum).Sum

Server RAM

$RamInGb = (Get-wmiobject -ComputerName $Server -Classname win32_physicalmemory -ErrorAction Stop | measure-object -property capacity -sum).sum/1GB

Pagefile Configuration

Check if there is a Pagefile, if there is, check the minimum and maximum size. If the Pagefile query comes up null, then the Pagefile is managed.

$PageFileCheck = Get-CIMInstance -ComputerName $Server -Class WIN32_PageFile -ErrorAction STOP
$Managed = $False
If ($Null -ne $PageFileCheck) {
$MaximumSize = (Get-CimInstance -ComputerName $Server -Query "Select * from win32_PageFileSetting" | select-object MaximumSize).MaximumSize
$InitialSize = (Get-CimInstance -ComputerName $Server -Query "Select * from win32_PageFileSetting" | select-object InitialSize ).InitialSize
} Else {
$Managed = $True

Event Logs

$Line = EventLog -list | where {($_.log -eq 'Application') -or ($_.log -eq 'system') -or ($_.log -eq 'Security')} |ft Log,MAx*,Over* -auto | Out-File $EventLogConfiguration

Windows OS Information

$OSName = ($os = Get-WmiObject Win32_OperatingSystem -Namespace "root\CIMV2").Name.split('|')[0]


User Mailboxes / Contacts

An important aspect of an Exchange Server environment consists of mail objects like contacts and mailboxes. Querying mailbox numbers, sizes, archive information and more can be quite useful as it can provide a sense of scale for support personnel. It can also assist in developing the right support systems for maintenance, backups, migrations and more.

Mailbox Statistics

First, for user mailbox statistics, we have a defined file to export the information to. Then we can export that separately as the file could get quite large (think 10,000 plus user environments).

$MailboxStatDestination = $Path+"\MailboxStatistics.txt"
Get-Mailbox -ResultSize Unlimited | Get-MailboxStatistics | Select-Object DisplayName, @{expression = { $_.TotalItemSize.Value.ToMB()};label="Mailbox Size(MB)"} | ft -auto | Out-File $MailboxStatDestination

Archive Mailbox Statistics

Next, for user’s archive mailbox, we execute the same code set, but use the -Archive switch for Get-Mailbox so we can query all user’s archive mailboxes:

$ArchiveMailboxDestination = $Path+"\ArchiveMailboxStatistics.txt"
Get-Mailbox -ResultSize Unlimited -Archive | Get-MailboxStatistics | Select-Object DisplayName, @{expression = { $_.TotalItemSize.Value.ToMB()};label="Mailbox Size(MB)"} | ft -auto | Out-File $ArchiveMailboxDestination

Protocols Allowed

Then for protocols that users can use for their mailboxes, we use the Get-CASMailbox cmdlet and export that to our predefined results file:

$CasMailboxDestination = CASMailboxProperties.txt
Get-CasMailbox | ft -auto | Out-File $CasMailboxDestination

Mail Contacts

$MailContactsDestination = ‘MailContacts.txt’
$Line = Get-MailContact -ResultSize Unlimited | Ft DisplayName, Alias, EmailAddress, HiddenFromAddressListsEnabled -Auto | Out-File $MailContactsDestination


Exchange Software Settings

In addition to the base Exchange Server setup, there are also custom settings we can configure in the Exchange Server software. Below are some examples of these configurations that we can query with PowerShell:

Email Address Policies

$Line = Get-EmailAddressPolicy -ErrorAction STOP | Ft Name, RecipientFilterType, Priority ,Enabled*, RecipientFilter, LDAP*, IsValid -Auto | Out-File $Destination -Append

Address Book Policies

$Line = Get-AddressBookPolicy | Fl | Out-File $Destination -Append

Authentication Policies

$Line = Get-AuthenticationPolicy -ErrorAction STOP | Fl Name,Identity,allow* | Out-file $Destination -Append

Receive Connectors

$Line = Get-ReceiveConnector | Ft Identity, Bindings, EnabledAuthMechanism, MaxMessageSize, PermissionGroups, RequireTLS, TransportRole -Auto | Out-file $Destination -Append

Send Connectors

$Line = Get-SendConnector -ErrorAction STOP | Ft Identity, HomeMtaServerId, Enabled, MaxMessageSize, AddressSpaces, CloudServicesMailEnabled, RequireTLS, SmartHosts -Auto | Out-file $Destination -Append

DLP Policies

$Line = Get-DLPPolicy | Ft Name,State,Mode,Identity -Auto | Out-File $Destination -Append


Creating Reports

Some of the data we gather can be better represented or provided to other people as HTML reports as the data can be represented in a more consumable way. Perhaps the reporting will be used by IT Management who need to provide auditing information or high-level architecture information to internal / external sources for a business process. With PowerShell, we can turn the information queried from regular CSV output files to HTML files. There are a lot of options for doing this and this article will not provide an in-depth method for that process, we will however, cover a high-level approach to this that can be used in your report creation process.

In this report we will query NIC information for all Exchange Servers and this is being done to make sure that no server has DHCP configured, or too many gateways or other NIC configuration issues. 

$ExchangeServers = (Get-ExchangeServer).Name
Foreach ($ExchangeServer in $ExchangeServers) {
$Networks = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ExchangeServer -EA Stop | ? {$_.IPEnabled}
Foreach ($Network in $Networks) {
$IPAddress = $Network.IpAddress[0]
$SubnetMask = $Network.IPSubnet[0]
$DefaultGateway = $Network.DefaultIPGateway
$DNSServers = $Network.DNSServerSearchOrder
$WINS1 = $Network.WINSPrimaryServer
$WINS2 = $Network.WINSSecondaryServer
$WINS = @($WINS1,$WINS2)
$IsDHCPEnabled = $false
If($network.DHCPEnabled) {
$IsDHCPEnabled = $true
$MACAddress = $Network.MACAddress
$OutputObj = New-Object -Type PSObject
$OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer.ToUpper()
$OutputObj | Add-Member -MemberType NoteProperty -Name IPAddress -Value $IPAddress
$OutputObj | Add-Member -MemberType NoteProperty -Name SubnetMask -Value $SubnetMask
$OutputObj | Add-Member -MemberType NoteProperty -Name Gateway -Value ($DefaultGateway -join ",")
$OutputObj | Add-Member -MemberType NoteProperty -Name IsDHCPEnabled -Value $IsDHCPEnabled
$OutputObj | Add-Member -MemberType NoteProperty -Name DNSServers -Value ($DNSServers -join ",")
$OutputObj | FT -auto
$Row = "$Computer," + "$IPAddress," +"$SubnetMask," +"$DefaultGateway," +"$ISDHCPEnabled," + "$DNSServers"
Add-Content -Path $NICInfoCSVDestination $Row

Once we have all of that information gathered up into a CSV file defined by the $NICInfoCSVDestination variable, we can then start creating the HTML header for our file here:

# Define Style for HTML file
$OutputFile = "<style>"
$OutputFile = $OutputFile + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$OutputFile = $OutputFile + "TH{border-width: 1px;padding: 2px;border-style: solid;border-color: black;}"
$OutputFile = $OutputFile + "TD{border-width: 1px;padding: 2px;border-style: solid;border-color: black;}"
$OutputFile = $OutputFile + "</style>"

Then convert the $NICInfoCSVDestination file and apply the header we defined previously:

# Convert CSV to HTML
$Output = Import-Csv -Path $NICInfoCSVDestination
$Output | ConvertTo-Html -Head $OutputFile | Set-Content -Path $NICInforHTMLDestination

When this code section is run, we should have an HTML file like so:

Screenshot of an exemplary HTML file 
Screenshot of an exemplary HTML file 

What we see above is, again, a summary list of an Exchange Server’s Network configuration and it enables a high-level review of the configuration and help spot misconfiguration.


Well, you’ve made it through the barrage of PowerShell code snippets, congratulations! Now the hope is that you saw the value as well as the quick and easy construction of the snippets and can start to see how this would be of value for your environment. Remember that this article is not comprehensive, nor would we be able to truly recreate an entire script in a blog post. One script that I have written is over 5,000 lines for documenting Exchange Server. For next steps, read and re-read the post, pick apart the code and then expand it out to other portions of the configuration that you may need to document. Then create an appropriate set of output files. Lastly, make sure to gauge the readers and consumers of the documentation and gauge it to them. Exports can be super technical with little to no additional explanation, or the script can perform a translation where maybe a threshold could be associated and reported on for a manager level report. Things to keep in mind. In the end, what you do with this is up to you, the reader, and that is the freedom of PowerShell.


Related Content

Introduction: HTML Result Messages and customizable HTML Reports




Related posts

About the author: