Generate Exchange Environment Reports using Powershell

image

Update 16th September – V1.6.1 – Exchange 2016 support and various bug fixes (see here for more information)

Update 2nd February – V1.5.8 – Exchange 2013 CU and SP support, HTTPS and CAS array names shown, initial Office 365 Hybrid support (see here for more information)

Update 19th January – V1.5.6 – Exchange 2013 support and bug fixes (see here for more information)

Update 17th August – V1.5.4 – New features and bug fixes (see here for more information)

Update 24th July – V1.5.3 – Bug fixes (see here for more information)

Update 21th July – V1.5 – Re-write with new features including Exchange 2003 support for mixed environments and more detailed information (see here for more information)

Update 21th June – V1.1 – Bug fixes, Exchange 2007-only support in addition to 2010/2010+2007, and new features.

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 2003, 2007, 2010, 2013 and 2016 servers and database availability groups – in particular:

    • Total Servers per Exchange version & service pack
    • Total Mailboxes per Exchange version & service pack, plus Office 365 remote mailboxes
    • Totals for Exchange roles across the environment
    • A site-by-site breakdown for the following:
      • Mailboxes per site
      • HTTPS FQDNs used for Internal, External and SCP URLs
      • CAS array names
      • Exchange servers, version, update rollup and version, 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 only shown if a DB includes Archive mailboxes
          • Database and whitespace size
          • Database and log disk free space percentage
          • 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 and 2003 DBs, including the database information above, along with Storage Group name (where applicable).

The script doesn’t support detailed information about Exchange 2007/2003 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:

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

If you want it to email the results, the follow parameters are available to allow the report to be sent directly from the script:

1
.\Get-ExchangeEnvironmentReport -HTMLReport c:\report.html -SendMail:$true -MailFrom:you@example.com -MailTo:you@example.com -MailServer:smtp.example.com

As usual, the script is provided to download as-is without any warranties, at the bottom of this post. Comments and suggestions are always welcome.

Download Get-ExchangeEnvironmentReport.ps1

 

 

663 thoughts on “Generate Exchange Environment Reports using Powershell

  1. This is an awesome script. up to this point I have had multiple scripts that provided some of this information and not in a neat html file either and for that I thank you. but I have one issue. When I run this the Log disk Free data is not reporting correctly. Has anyone else experienced this issue? I am running this from my Win7 pro x64 workstation as a scheduled job. I have not tried to run this from one of the actual exchange servers though I don’t anticipate that it will make a difference. Any thoughts/recommendations are welcome.

  2. Installed Update Rollup 11 for Exchange Server 2007 Service Pack 3 (KB2873746) 8.3.327.1

    Report shows as UR9

    $RegKey=”SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\461C2B4266EDEF444B864AD6D9E5B613\\Patches” has 4 Patches in this order – UR9, UR11, RU8-v2, RU8

    If I change DisplayName from 9 to 19, it shows RU8 (I assume would show RU8-v2!)
    If I change DisplayName from 8 to 18, it shows RU8-v2
    If I change DisplayName from 8-v2 to 18-v2, it shows RU19 (I would assume to see RU11 by now!)

    So change the DisplayName in Registry to “Update Rollup*” instead of “Update Rollup *” for UR9, RU8-v2, RU8 and now RU11 shows in report…

    Suggestion use ‘461C2B4266EDEF444B864AD6D9E5B613\InstalledProperties\InstallDate’ as this matches ‘Installed’ in Patches to get the latest RU

    PS: Excellent script. We use it for all our customer audits 🙂

    • Thanks Jeff Guillet

      Updating line 330 of version 1.5.6 to support Exchange 2007 SP3 UR11.

      To:
      if ([int]$tRU -ge [int]$RollupLevel) { $RollupLevel=$tRU; $RollupVersion=$tRUV }

      Did the trick 🙂

  3. First of all:
    FANTASTIC script!

    Question: Is it possible to list the mailservers alphabetically?
    I get results in the style of:

    Mailserver03
    Mailserver07
    Mailserver02
    Mailserver04
    Mailserver06
    Mailserver05
    Mailserver01

  4. hi, scheduling a task does not work. I get the following message:
    C:\scripts\Get-ExchangeEnvironmentReport.ps1 : A parameter cannot be found that matches parameter name ‘ScheduleAs’.
    At line:1 char:97
    + .\Get-ExchangeEnvironmentReport.ps1 -HTMLReport c:\Exchange_Environment_report.html -ScheduleAs: <<<< domain\user
    + CategoryInfo : InvalidArgument: (:) [Get-ExchangeEnvironmentReport.ps1], ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Get-ExchangeEnvironmentReport.ps1

    Can anyone help me to get this working. I am trying to set this so I can get a weekly report but task scheduler does not work as I need to manually enter the location for the file to be saved.

    If anyone can help, would be appreciated

    • If you have trouble with scheduling it via the script, try my failproof approach.

      * Create a launcher PS1 script, e.g. C:\Scripts\RunEER.ps1 with contents similar to this:

      1
      C:\Scripts\Get-ExchangeEnvironmentReport.ps1 -HTMLReport C:\Scripts\Report.html

      * Create a new CMD file that calls the launcher script, e.g. C:\Scripts\RunEER.cmd

      1
      powershell -File C:\Scripts\RunEER.ps1

      * Schedule the CMD file as you see fit

      • thank you, was scratching my head looking for a solution.
        Also, do you have those features for the script to check for Public Folders as well?
        thanks

  5. Pingback: Office 365 Tools | MessagingSolutions

  6. Pingback: Special version of the Exchange Environment Report for PowerShell v1.0 available | Steve Goodman's Exchange Blog

  7. Great work, Steve.

    You need to make a change on line 330 of version 1.5.6 to support Exchange 2007 SP3 UR10.

    Change:
    if ($tRU -ge $RollupLevel) { $RollupLevel=$tRU; $RollupVersion=$tRUV }

    To:
    if ([int]$tRU -ge [int]$RollupLevel) { $RollupLevel=$tRU; $RollupVersion=$tRUV }

    The change is required because PowerShell treats the string “10” differently than the number 10. For example, “10” > “9” is FALSE; 10 > 9 is TRUE.

  8. Great script!
    Question: I’d like to add in -JournalRecipient for each database. This would help to insure that no new DB would be improperly configured on a fairly major setting.

    How to do that? I unfortunately cannot follow the code well enough to do that.

    Thanks! BrentG

  9. @M.M (regarding line 897) i solved it by modify the script with the following:
    for ($i=0; $i -lt @($Databases).length; $i++)
    {
    if (@($Databases).length -eq 1) {
    $Database = _GetDB -Database $Databases -ExchangeEnvironment $ExchangeEnvironment -Mailboxes $Mailboxes -ArchiveMailboxes $ArchiveMailboxes -E2010 $E2010
    }
    else {
    $Database = _GetDB -Database $Databases[$i] -ExchangeEnvironment $ExchangeEnvironment -Mailboxes $Mailboxes -ArchiveMailboxes $ArchiveMailboxes -E2010 $E2010
    }

  10. Pingback: Exchange Server 2010 - HTML Dokumentation per Script | chilltimes.de - the home of manuel m.

  11. Ok, i fix this by change this lines:

    897: for ($i=0; $i -lt @($Databases).Count; $i++)

    899: $Database = _GetDB -Database @($Databases)[$i] -ExchangeEnvironment $ExchangeEnvironment -Mailboxes $Mailboxes -ArchiveMailboxes $ArchiveMailboxes -E2010 $E2010

  12. Hi, I found a bug in this script – if is only one database in DAG script dont show database information. In line

    835 $Databases = [array](Get-MailboxDatabase -IncludePreExchange2010 -Status) | Where {$_.Server -like $ServerFilter}

    databases are converted to array but in in loop on line

    897 for ($i=0; $i -lt $Databases.Count; $i++)

    $databases,count return null – not 1. I’m not a good powershell programmer so please write fix for this situation. Thanks.

  13. Just a quick note to say thanks a million for this script! These reports will be helpful in plotting storage growth over time, and planning for our future needs. THANKS!

  14. I have a problem scheduling this. The scheduling job is created successful, but when I run it with my user (same as I run script manualt with) it runs for a couple of seconds, and it then gives a errorcode (0x1).

    have tried running it like this also:
    -version 2.0 -NonInteractive -WindowStyle Hidden -command “. ‘C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1’; Connect-ExchangeServer -auto; ‘C:\Script\Get-ExchangeEnvironmentReport.ps1 -HTMLReport c:\……………'”

  15. after i run the script, the report has Rollup 8-v2 instead of 10 which is the latest installed.

    my environment is exchange 2007 sp3 RollUp 10 on top of windows 2003 x64

    Could anyone please tell me why this is happening?

    • I fixed it. it was a bug in the code -line 330 the compare is:
      if ($tRU -ige $RollupLevel) { $RollupLevel=$tRU; $RollupVersion=$tRUV }
      which is wronge because you are comparing numbers as a string : meaning “10” is less than “8” in string language. my fix is to add [int] before the variable :

      if ([int]$tRU -ige [int]$RollupLevel) { $RollupLevel=$tRU; $RollupVersion=$tRUV }

      I hope this is a correct fix. if anyone could confirm it for me, please do.

  16. Also getting the error :

    WARNING: Cannot detect OS information via WMI for “ServerName”

  17. Hello,

    I’m getting below error while running the script:

    Get-WmiObject : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
    At C:\Get-ExchangeEnvironmentReport.ps1:265 char:23
    + $tWMI = Get-WmiObject <<<< Win32_OperatingSystem -ComputerName $ExchangeServer.Name -ErrorAction SilentlyContinu
    e
    + CategoryInfo : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

    Kindly suggest

  18. Pingback: Creating a HTML report of Exchange 2010 databases | Welcome @ VSPBreda

  19. Hi Steve,

    Thanks for the script, i got the below error while executing the script. Appreciate your advice. Thank you

    You must provide a value for this property.
    + CategoryInfo : NotSpecified: (0:Int32) [Get-MailboxDatabase], DataValidationException
    + FullyQualifiedErrorId : 16AD8B37,Microsoft.Exchange.Management.SystemConfigurationTasks.GetMailboxDatabase

    The report is not showing any mailbox details. Please advice. Thanks

  20. Run the script with a dot before it

    e.g.

    . .\Get-ExchangeEnvironmentReport -htmlreport .\something.html

    Then you’ll be able to explore the $ExchangeEnvironment variable. You’ll see what’s exposed there – everything in the HTML report is contained within.

    Steve

    • Well If I run it like you mention int I get an error:
      The term ‘..\Get-ExchangeEnvironmentReport’ is not recognized…

      If I do: .\Get-ExchangeEnvironmentReport -htmlreport .\something.html

      I get the HTML file as always. Where should I find the contents of the $ExchangeEnvironment variable?

      Thanks for your patience

  21. The Hashtables contain all the data that’s used to build the tables, and it should be neatly organized. It’s layout out as trees of objects rather than a flat format you might use for SQL, though. So I would suggest iterating through the hashtables to “flatten” it for the SQL table

    • Makes sense in a way. Which section is the hashtables or in what function? I’m in a beginners level compared to you guys

  22. Edwin,

    There is a Powershell SQL Server extensions that work with ADO commands, you can use that to insert data directly to a SQL Server Database.

    Regards

    • I have other scripts exporting data to SQL, but havent found the way with this one. Can’t find a situable variable to pick the values from. Just looking for the values from the DB table

  23. awesome script, greate for final project report. my boss will love it, I can actually picture him in a meeting handing off copies of the enviromwnt and getting all the credit for what I DID, LOL

  24. I would also like to be just to run this against certain servers. Is there a way to do that?

  25. Fantastic piece of work. However, I seem to be missing some results in my report. Exch2010, 2 servers in a DAG. The report doesn’t show any of the average mailbox sizes, DB size, etc. There are no errors when I run the report. Any thoughts?

  26. Is there a way to run the script again a set of servers rather than all? We have an org that we share with a parent company and I only want the information for the servers that I manage.

  27. If I have a server with no DAG the script shows no information about the database(s) like DBsize, last Full Backup.

    Why is that and is it possible to have this?

  28. Hi

    if i change following parameter

    [parameter(Position=5,Mandatory=$false,ValueFromPipeline=$false,HelpMessage=’Server Name Filter (eg NL-*)’)][string]$ServerFilter=”*”

    to

    [parameter(Position=5,Mandatory=$false,ValueFromPipeline=$false,HelpMessage=’Server Name Filter (eg NL-*)’)][string]$ServerFilter=”US-*” (so instead of * i wann use US-*)

    then the amount of mailboxes on all databases is “0”. anyone an idea how to solve this?

    thanks a lot

    • Not sure if you already find out what happened.

      Please have a look into the section => UpProg1 10 “Getting Mailboxes” 1
      Replace all the word Where {$_.Server => Where {$_.ServerName

  29. Hi Rich

    thank you so much for the reply, much appreciated. I did manage to adjust the script trust level on our exchange box, but I wanted to confirm something. our exchange boxes are running PowerShell v1.0 right, I want to install PowerShell v2 on my windows 7 box, will the script still run or does our exchange box also have to run PowerShell v2.0 (ive installd EMC on my windows 7 machine also) again RICH thank you for the reply. much appreciated

  30. any help guys. I just wanna know how to use the script. looks very impressive and very easy. please guys

  31. Hi guys, im a beginner with exchange and im struggling to use the script. is there any instructions on how to xactly use it cause im lost??

    • Hi Gavin,

      Some pointers:

      1. Copy the script text to an Exchange 2010 server or workstation with Powershell 2.0 and Exchange EMC installed. Save it in c:\temp\ExchangeEnvironmentReport.ps1 or somewhere.
      2. Open the Exchange Management Shell
      3. You may need to adjust the script trust levels to permit you to run this type of (non-Microsoft) script on your Exchange server. Please google “how to adjust script trust level in powershell” if this is the case
      3. Type “CD C:\temp” to go to the directory.
      4. Type .\Get-ExchangeEnvironmentReport -HTMLReport c:\report.html
      5. Assuming you have the correct permissions and WMI & Remote registry services are running, the script should run and you are able to open the html output report.
      6. Present to management and acknowledge praise.

  32. Hi Steve,

    Really an excelent script, works for me, just have one suggestion It would be great to see the % Free Space available colour coded, example (mountpoints below 10% of free disk space would show up Red, below 20% – yellow, and the rest Green. Hope you can include that in a next version. Make it easier to identify potential diskspace problems with just a glance.

    Thanks again…

    Regards

  33. Steve,

    Thanks loads for all of your work. I am relatively new to PowerShell also. When I run this command in the EMS, I get this:

    Unexpected token ‘and’ in expression or statement at GetExchangeEnvironmentReport.ps1:16 char 19
    + 2013, 2010 and <<<< to a lesser extent, 2007 , 2003, environment.

    It seems PS thinks this comment is part of the code. I am sure this is easy to fix, but I do not know how to trooubleshoot Powershell. could you point me in the right direction?

  34. Hi Steve,
    Nice script, i tried to run this in Win 2003 R2, i am getting a error msg like windows manangement shell not configured, how can i fix this. i am new to Power shell. But i need to run a report for my exchang environment, can you help me to proceed.
    Thanks in advance.

    Regards,
    Senthil

  35. Is there a way to log what is happening in the script? The scirpt outputs and emails but it lists up specific information about the my DAG. I can see mailbox roles and numbers but not DB size and stuff like that. I can get wmi info through powershell from my mailbox servers.

  36. Pingback: Some useful exchange monitoring tools | 503 5.0.0 polite people say HELO

  37. Exchange 2007 Whitespace.

    Hi Steve, would you be able to help me understand how this is calculated? I was expecting to see some kind of call to even 1221.

    I ask as we are looking for some ‘Missing’ edb space, and the white space in your report differs from the 1221 events I see.

    Hope you can help.

    Thanks,
    Max

      • I found this in his script for 2007 Whitespace.
        {
        $ArchiveMailboxCount = 0
        $CopyCount = 0
        $Copies = @()
        # 2003 & 2007, Use WMI (Based on code by Gary Siepser, http://bit.ly/kWWMb3)
        $Size = [long](get-wmiobject cim_datafile -computername $Database.Server.Name -filter (‘name=”’ + $Database.edbfilepath.pathname.replace(“\”,”\\”) + ””)).filesize
        if (!$Size)
        {
        Write-Warning “Cannot detect database size via WMI for $($Database.Server.Name)”
        [long]$Size = 0
        [long]$Whitespace = 0
        } else {
        [long]$MailboxDeletedItemSizeB = 0
        if ($MailboxStatistics)
        {
        $MailboxStatistics | %{ $MailboxDeletedItemSizeB+=$_.TotalDeletedItemSizeB }
        }
        $Whitespace = $Size – $MailboxItemSizeB – $MailboxDeletedItemSizeB
        if ($Whitespace -lt 0) { $Whitespace = 0 }
        }

        Problem with that this does not look to give the correct calculations for Whitespace. And I am failing to change it something that uses the application log 1221 message.
        Like this one.

        function Get-ExchangeWhiteSpace {

        $computernames = Get-MailboxDatabase | select-Object servername -Unique
        foreach ($ComputerName in $computernames) {
        $tc = [System.Management.ManagementDateTimeconverter]
        $Start =$tc::ToDmtfDateTime( (Get-Date).AddDays(-1).Date )
        $End =$tc::ToDmtfDateTime( (Get-Date).Date)

        $DB = @{Name=”DB”;Expression={$_.InsertionStrings[1]}}
        $FreeMB = @{Name=”White Space (MB)”;Expression={$_.InsertionStrings[0]}}

        Get-WMIObject Win32_NTLogEvent -ComputerName $ComputerName.servername -Filter “LogFile=’Application’ AND EventCode=1221 AND TimeWritten>=’$Start’ AND TimeWritten<='$End'" | Select-Object $DB,$FreeMB | Sort-Object "White Space (MB)" –Unique -Descending
        }

        This would make a difference for all of us that is using exchange 2007

          • Wondering If you have made the change yet for 2007? We are planning to switch to 2013 in the future but that is to be seen when the project becomes a reality.

  38. This is a great script to get an overview of the environment!
    If I can make a request I would like to see a version where you also can view the “Copy Status” and the “Copy Queue Length” of the mailbox databases on each server.

    Best regards

    // Jonas

  39. We are using mount points for our Exchange drives, but recently we gave those mount points drive lets as well (so we could get to the database and log files faster than going thru mount points). Since we added the drive letters, now your report shows the same percentage free space for every database and logs drives for every database on the server. Thoughts – I’ve walked thru your script, but I am obviously missing something.

    • We use mount points for Exchange 2010 database and log file locations. F:\ is a small volume to hold mount points for databases (e.g. F:\DB01\, F:\DB02\, etc.) and G:\ is a small volume to host mount points for log files (e.g. G:\TL01\, G:\TL02\, etc.). In this configuration the script would report 98.8% free space for ALL log file paths regardless of how full they were because it was picking up the root volume (G:\) rather than the volume mounted at the mount point (G:\TL01\, G:\TL02\, etc.).

      I was able to show the correct free log disk space in v1.5.6 by modifying line 147 so that it matches properly on the mount point volume name rather than the volume that holds the mount point:

      [147] if (($Database.LogFolderPath.PathName + “\”) -like $Disk.Name)

      This worked for me; however, I’m not certain how this would behave if mount points were NOT being used (and have not tested that scenario)…

Comments are closed.