Customize the Outlook Web App Logon Page

Microsoft has provided some basic details on how to customize the Outlook Web App logon page (OWA) in Exchange 2013.  I’ve taken things a bit further and modified logon.aspx and logon.css to customize the OWA logon page with a PowerShell script.

It is important to note that at this time the script does not back up any of the files that it modifies.  Be warned, you should backup logon.aspx and logon.css.  As of Exchange 2013 SP1 CU6 they are located in the following directories:

Logon.aspx:  C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth

Logon.css:  C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\15.0.995\themes\resources

Read moreCustomize the Outlook Web App Logon Page

PowerShell Registry Update File MRU and Place MRU

Recently, as part of a data migration project and in an effort to create a seamless user experience in Microsoft Office, the recent documents and recent places needed to be updated with a new server name.  This prevented any users from using this Office feature and having problems.

Using PowerShell to make updates to the File MRU and Place MRU accomplishes this goal.

# Create an empty array to contain collected data for parsing.
$MRUArray = @()
# Get-ChildItem recursively searches for File MRU and Place MRU. Get-Item returns
#  Name, Propery and PSPath that are used for updating the registry.  The items
#  are added to the variable.
$MRU = Get-ChildItem HKCU:\Software -recurse -ea SilentlyContinue `
| where { $_.Name -like "*File MRU" -or $_.Name -like "*Place MRU" } | Get-Item

# Foreach item returned above and contained in $MRU.
foreach ($Item in $MRU) {
	# Process the current item that matches "item*" and pipe to a new foreach loop.
	$ -like "item*" |
	foreach {
		# Create a new object and add PSPaath, Item, and MRUPath to the array.
		$RegObject = New-Object system.Object
		$RegObject | Add-Member -type NoteProperty -Name "PSPath" -Value $Item.PSPath
		$RegObject | Add-Member -type NoteProperty -Name "Item" -Value $_
		$RegObject | Add-Member -type NoteProperty -Name "MRUPath" -Value (Get-ItemProperty $Item.PSPath).$_
		$MRUArray += $RegObject

# Foreach object in $MRUArray.
foreach ($object in $MRUArray) {
	# Process the current object and replace server name.
	set-itemproperty -Path $object.PSPath -Name $object.Item `
	-value $object.MRUPath.replace("OldServer", "NewServer")


PowerShell Try Catch Finally Error Handling

I’ve recently been experimenting with the script blocks in PowerShell try catch finally.  These script blocks can help you deal with terminating errors that may occur in your scripts.  A terminating error, otherwise known as an exception, will stop a command from processing the current object or from processing additional objects.  On the other hand, there are many situations that will result in a non-terminating error.  In a non-terminating error situation an error will generally be displayed on the console and continue processing.  An example of a non-terminating error is passing a variable containing a list of mailboxes to the Get-Mailbox cmdlet.  If an error occurs matching a name in the variable to an Exchange mailbox, the process continues but an error is displayed to the console.  A good resource for determining what exactly constitutes a terminating error or non-terminating error can be found in this MSDN article. This is where the script blocks try catch finally can be helpful. The try block is where you will put the code that you want to execute and monitor for errors.  If an error occurs then the code in the catch block is executed.  It is possible to include multiple catch blocks in order to catch certain types of errors.  The finally block runs no matter what happens during the try and catch script blocks.  The finally block can be used to provide informational output gather from a script or free up resources, per Microsoft’s documentation.  You may even choose to omit the finally block as part of your script if you don’t feel that it is necessary.

So if you are using the try catch finally blocks and have non-terminating errors within the try block, the catch block ignores the errors and does not fire.  It is possible to force any error in the try block by using the -ErrorAction switch.  Setting the error action to stop forces the catch block to see all errors, either terminating or non-terminating.

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction 'SilentlyContinue'
$ExchangeAlias = Read-Host "Please enter alias"

Try {
"Attempting to get-mailbox of $ExchangeAlias"
Get-Mailbox $ExchangeAlias -ErrorAction stop

Catch {

Finally {
Remove-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction 'SilentlyContinue'

You’ll notice in the catch block that I’m using the $Error variable. This is where PowerShell stores all the errors for a given session.  Using [0] after the $Error variable denotes accessing the first time in a PowerShell array so we’ll be returning the most recent error in $Error.  Just for the sake of the example, the finally block unloads the Exchange 2010 PowerShell snapin.

Ok so this script is reinventing the wheel a bit but the try catch finally script blocks are extremely useful for handling terminating errors or when you want to trap non-terminating errors in your code.

Monitoring the DAG Replication Network

The Exchange 2010 Database Availability Group (DAG) is an important feature that provides high availability.  Therefore, monitoring the DAG replication network is an important part of keeping the high availability in your environment as healthy as possible.  Replication of Exchange 2010 databases in your messaging environment has specific network requirements outlined in this TechNet document.  The best scenario is to use two network adapters per DAG node member.  One adapter supports the MAPI Network which is used by other servers, for example other Exchange 2010 servers or directory servers.  The other adapter is for the DAG replication network which is dedicated for database replication.  With all this said, Exchange 2010 is smart enough to switch replication from the replication network to the MAPI network in the event of a communications problem on the DAG replication network.

Read moreMonitoring the DAG Replication Network

Get IronPort Delivery Status With PowerShell

The Cisco IronPort E-mail Security Appliance (ESA) makes various statuses available in an XML format and you can use PowerShell to parse the XML data and get some useful information.  With this technique you can create a report about the IronPort delivery status in your environment.  Forget about trying to force PowerShell into using some sort of SSH connection method.  I’ve tried it and it isn’t pretty.  There is a security concern with the following method but it is a proof of concept that may work well in your environment.  Here are the ingredients to get you started:

  • A Cisco IronPort E-mail Security Appliance (ESA) and a need to access the status pages.
  • An account that can authenticate to the ESA, preferably with the guest role.
  • A locked down environment that will reduce attack surfaces within your organization.

The methodology is rather simple, use PowerShell to grab an XML status page and parse it into an object that can be massaged for your benefit.  Think of having a script send you a periodic report about the IronPort delivery status of your appliance, awesome!  To illustrate, I’ll be looking at the tophosts status since I really want to know about my partner organizations that could be having problems.  Why not automate and be proactive at the same time?

Read moreGet IronPort Delivery Status With PowerShell

Count Mailboxes Per Database Faster

Waiting for a script to gather data can be a frustratingly long experience at times.  A common task when using PowerShell cmdlets with Exchange 2010 is to enumerate mailboxes on a database.  If you are counting the number of mailboxes per database using Get-Mailbox, it’s possible to speed things up.  A faster method is to use Get-MailboxStatistics, although the cmdlets return different data, the mailbox count per database is what we want in a speedy manner.  I’ve created two scripts with a timer built into each so that the speed of mailbox enumeration can be effectively illustrated.  We are doing the following:

  • Comparing Get-Mailbox versus Get-MailboxStatistics.
  • Using a timer to illustrate the speed difference of counting mailboxes per database.

The test environment consists of 48 DAG databases with about 400 mailboxes on each, spread across four servers.

Read moreCount Mailboxes Per Database Faster

Who moved my PAM?

If your Exchange 2010 deployment utilizes a DAG in two or more Active Directory sites you may have run into situation where the Primary Active Manager (PAM) is unexpectedly located on a DAG node that resides in another site.  The PAM, aka the “cluster group” will shift around as necessary among the members of your DAG for various reasons.  So you ask, “Who moved my PAM?”:

  1. You may have moved it as part of DAG maintenance.
  2. There was problem with the DAG node hosting the PAM, such as errors in the ESE or a storage problem, and the role moved to a server in an alternate site.
  3. A network communications error occurred, affecting the cluster service heartbeat, causing the PAM to move to what it believes is a surviving member of the DAG.

Unfortunately there is no way to add a server preference to the PAM role.  It would be nice to be able to tell the PAM to try and stay on a server at a preferred active directory site.  The only thing that you can do is to block a DAG member from having database copies activated on it with the following command:

Read moreWho moved my PAM?

Lost and Found DAG Databases

If you have a large amount of DAG databases you may want a quick way of identifying which Exchange mailbox role is hosting the active copy of your databases.  Sure you can take a look at the EMC but I have a quick way for you to determine if each database is mounted on the mailbox server specified with an activation preference of 1.  Credit for this script is to René van Maasakkers on his blog.

Start out with filtering down the databases that are part of a DAG and not recovery databases.  Then run a ForEach loop that will capture the name of the database, server on which the database is mounted, and the activation preference of the database.  Finally use Write-Host to enumerate the names grabbed earlier and use IF ELSE logic to output the status of the mounted database.

Read moreLost and Found DAG Databases

Exchange 2010 Migration Script Part 2

Do you need an automated process to migrate from a legacy Exchange environment to Exchange 2010?  This article is step two of a two step migration process that involves separate PowerShell scripts to accomplish an Exchange 2010 migration.  This is the second script  that resumes the migration of legacy Exchange mailboxes and distributes them evenly across existing Exchange 2010 databases.  The bulk of this script deals with reporting the results of the migration for later review and analysis.  I’ll break down the script in detail so that you can understand what is happening.   (Part 1 can be found here)

The script  executes the following:

  1. Selects the pending move requests based on a batch name.
  2. Verifies that status of move requests are acceptable and then resumes the migration.
  3. Waits until all resumed migrations are completed or completed with warnings before starting to build a report.
  4. Creates a report about all the mailboxes moved.
  5. Writes report to CSV and sends the report via email.

Read moreExchange 2010 Migration Script Part 2

Exchange 2010 Migration Script Part 1

Do you need an automated process to migrate from a legacy Exchange environment to Exchange 2010?  This article is step one of a two step migration process that involves separate PowerShell scripts to accomplish an Exchange 2010 migration.  The two part migration process will allow you to begin migrations during the work day and then cut over the mailboxes to Exchange 2010 at a convenient time.  This is the first script  that migrates legacy Exchange mailboxes evenly across existing Exchange 2010 databases.  I’ll break down the script in detail so that you can understand what is happening.  The script  executes the following:

  1. Selects a set of mailboxes to migrate based upon membership of a distribution group.
  2. Selects the Exchange 2010 databases that you want to target.
  3. Creates a data table to hold the mailboxes to be migrated.
  4. Creates a data table of target mailbox databases that will be used to select the optimal database target based on various criteria.
  5. Queries the target Exchange 2010 databases to see how the databases are populated and considers various criteria.
  6. Queries the source mailboxes and assigns them to target Exchange 2010 databases for a move request.
  7. Submits new move requests.
  8. Loops until all mailboxes are in an auto suspended state or failed state.

Read moreExchange 2010 Migration Script Part 1