4 min read
How to Leverage IT Automation for Maximum Efficiency
Are you ready for a transformation of your IT operations? Our brand-new white paper, Maximizing IT Automation: The...
ScriptRunner Blog
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.
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.
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.
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.
First, let’s explore the configuration of Exchange Server itself.
$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
Useful for verifying server versions as well as what roles are installed on the server.
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 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
Another crucial part of an Exchange server is the configuration of the underlying Windows Server that it runs on.
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
$RamInGb = (Get-wmiobject -ComputerName $Server -Classname win32_physicalmemory -ErrorAction Stop | measure-object -property capacity -sum).sum/1GB
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
}
$Line = EventLog -list | where {($_.log -eq 'Application') -or ($_.log -eq 'system') -or ($_.log -eq 'Security')} |ft Log,MAx*,Over* -auto | Out-File $EventLogConfiguration
$OSName = ($os = Get-WmiObject Win32_OperatingSystem -Namespace "root\CIMV2").Name.split('|')[0]
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.
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
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
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
$MailContactsDestination = ‘MailContacts.txt’
$Line = Get-MailContact -ResultSize Unlimited | Ft DisplayName, Alias, EmailAddress, HiddenFromAddressListsEnabled -Auto | Out-File $MailContactsDestination
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:
$Line = Get-EmailAddressPolicy -ErrorAction STOP | Ft Name, RecipientFilterType, Priority ,Enabled*, RecipientFilter, LDAP*, IsValid -Auto | Out-File $Destination -Append
$Line = Get-AddressBookPolicy | Fl | Out-File $Destination -Append
$Line = Get-AuthenticationPolicy -ErrorAction STOP | Fl Name,Identity,allow* | Out-file $Destination -Append
$Line = Get-ReceiveConnector | Ft Identity, Bindings, EnabledAuthMechanism, MaxMessageSize, PermissionGroups, RequireTLS, TransportRole -Auto | Out-file $Destination -Append
$Line = Get-SendConnector -ErrorAction STOP | Ft Identity, HomeMtaServerId, Enabled, MaxMessageSize, AddressSpaces, CloudServicesMailEnabled, RequireTLS, SmartHosts -Auto | Out-file $Destination -Append
$Line = Get-DLPPolicy | Ft Name,State,Mode,Identity -Auto | Out-File $Destination -Append
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
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.
Nov 28, 2024 by Heiko Brenn
Are you ready for a transformation of your IT operations? Our brand-new white paper, Maximizing IT Automation: The...
Nov 21, 2024 by Guy Leech
Perhaps it is because I began my professional coding career writing device drivers for Unix systems in C, back in the...
Nov 20, 2024 by Damian Scoles
What is Microsoft Purview for you? Do you already master data governance with it? Learn how PowerShell simplifies...
Damian Scoles is a ten-time Microsoft MVP specializing in Exchange, Office 365 and PowerShell who has 25 years of IT industry experience. He is based in the Chicago area and started out managing Exchange 5.5 and Windows NT. Over the years he has worked with Office 365 since BPOS and his experience has grown to include Azure AD, Security and Compliance Admin Centers, and Exchange Online. His community outreach includes contributing to TechNet forums, creating PowerShell scripts that can be found on his blogs, writing in-depth PowerShell / Office365 / Exchange blog articles, tweeting, and creating PowerShell videos on YouTube. He has written five PowerShell books and is also actively working on the book "Microsoft 365 Security for IT Pros".