# Configuring Anti-Affinity in Failover Clusters

Many customers nowadays are running a virtualized Exchange environment, utilizing Database Availability Groups, load balanced Client Access Servers and the works. However, I also see environments where it is up to the Hypervisor of choice on the hosting of virtual machines after a (planned) fail-over. This goes for Exchange servers, but also for redundant infrastructure components like Domain Controllers or Lync Front-End servers for example.

So, leaving it to “default” is not a good idea when you want to achieve the maximum availability potential. Think about what will happen if redundant roles are located on the same host and that host goes down. What you want to do is prevent hosts from becoming the single point of failure, something which can be accomplished by using a feature called anti-affinity. This will distribute virtual machines over as much hosts as possible. Where affinity means to have an preference for, like in Processor Affinity for processes, Anti-Affinity can be regarded as repulsion in magnetism.

For VMWare, you can utilize DRS Anti-Affinity rules; I’ll describe how you can configure Anti Affinity in Hyper-V clusters using the AntiAffinityClassNames property (which by the way already exists since Windows Server 2003). And yes, property means it’s not accessible from the Failover Cluster Manager, but I’ve create a small PowerShell script which lets you configure the AntiAffinityClassNames property (in pre-Server 2012 you could also use cluster.exe to configure this property).

Note: For readability, when you see virtual machine(s), read cluster group(s); In Microsoft failover clustering, a clustered virtual machine role is a cluster group.

Now, before we’ll get to the script, first something on how AntiAffinityClassNames works. The AntiAffinityClassNames property may contain multiple unique strings which you can make up yourself. I’d recommend creating logical names based on the underlying services, like ExchangeDAG or ExchangeCAS. When a virtual machine is moved the process is as follows:

1. When defined, the cluster tries to locate the next preferred node using the preferred owner list;
2. Does the designated node host a virtual machine with a matching element in their AntiAffinityClassNames property; if not, the designated host is selected; if it is, move to the next available preferred owner and repeat step 2;
3. If the list is exhausted (i.e. only anti-affined hosts), the anti-affinity attribute is ignored and the preferred owner list is checked again, ignoring anti-affinity (“last resort”).

Traces of Anti-Affinity influencing failover behavior can be found in the cluster event log:

00000648.00000d54::2013/07/22-10:40:33.162 INFO  [RCM] group ex2 should fail back from node 2 to node 3 now due anti-affinity

Usage
Now on to the script, Configure-AntiAffinity.ps1. The syntax is as follows:

Configure-AntiAffinity.ps1 [-Cluster] <String> [-Groups] <Array> [-Class] <String> [[-Overwrite]] [[-Clear]] [<CommonParameters>]

A small explanation of the available parameters:

• Cluster is used to specify which cluster you cant to configure (mandatory);
• Groups specifies which Cluster Groups (Virtual Machines) you want to configure Anti-Affinity for (mandatory);
• Class specifies which name you want to use for configuring Anti-Affinity (optional, AntiAffinityClassName);
• When Overwrite is specified, all existing Anti-Affinity class names will be overwritten by Class for the specified Groups, otherwise Class will be added (default);
• When Clear is specified, all existing Anti-Affinity class names will be removed for the specified Groups;
• The Verbose parameter is supported.

So, for example assume you have 3+ Hyper-V cluster named Cluster1 consisting of 3+ nodes running 3 virtualized Exchange servers hosting a 3-node DAG, ex1, ex2 and ex3 and you want to configure anti-affinity for these virtual machines using the label PRODEX, you could use the script as follows :

Configure-AntiAffinity.ps1 -Cluster Cluster1 -Groups ex1,ex2, ex3 –Class PRODEX –Verbose

To clear anti-affinity you could use:

Configure-AntiAffinity.ps1 -Cluster Cluster1 -Groups ex1,ex2,ex3 -Clear

Here’s a screenshot of the script for creating anti-affinity, add additional anti-affinity class names and clearing anti-affinity settings:

Feedback
Feedback is welcomed through the comments. If you got scripting suggestions or questions, do not hesitate using the contact form.

You can download the script from the TechNet Gallery here.

Revision History
-

# Removing Duplicate Items from a Mailbox

For those involved with Exchange migration projects or managing Exchange environments, at some point you probably have experienced the situation where people ended up with duplicate items in their mailbox. Duplicate items can be caused by many things, but most common are:

• Synchronization tools or plug-in. Entries from the mailbox are treated as new entries and as a consequence are added to the mailbox when synchronizing information back to the mailbox, creating duplicates. In the past, I’ve seen this happening with Nokia PC Suite and Google Apps Sync for example;
• Importing existing data. Accidental import from – for example – a PST file to a mailbox  can lead to duplicate entries.

When looking for a solution, you’ll probably encounter MSKB299349, “How to remove duplicate imported items in Outlook”. This article describes a manual procedure to remove duplicates entries from your calendar, contacts, inbox or other folders. Not a very helpful and labor intensive.

When continuing your search, you’ll find lots (I mean lots!) of tools and Outlook add-ins, like Vaita’s DIR or MAPILab’s Duplicate Remover. Not all this software is free (some even require payment per duplicate removal of appointments, contacts or e-mail) and some might not even work (MAPI-based tools may not work against Exchange 2013).

When you finally have selected a tool, in most cases they require installation of a piece of software and someone to perform the removal process using the tool or Outlook with add-in. When you’re an Apple shop you’ll require different tools, unless you’re running a Windows desktop somewhere (I’ll just pretend I didn’t hear you saying ‘Why don’t you install the tool on the Exchange server’).

Wouldn’t it be nice if you’d have a PowerShell script you can conveniently run from any workstation (or server) with PowerShell installed, removing those duplicate items from a user’s mailbox remotely? If the answer is yes, the Remove-DuplicateItems.ps1 script may be something for you.

Requirements
Using the Remove-DuplicateItems.p1 script requires Exchange 2007 or later and Exchange Web Services (EWS) Managed API 1.2 (or later) which you can download here. Alternatively, when you already got the EWS Managed API Microsoft.Exchange.WebServices.DLL somewhere, you can copy it to the same folder where the script is located and it will be picked up. The script has been developed and tested against Exchange 2007, meaning it’s a PowerShell 1.0 script which should be compatible with later versions of PowerShell or Exchange.

Also take notice that since you’ll be processing user mailboxes, you’ll need to have full mailbox access or impersonation permissions; the latter is preferred. For details on how to configure impersonation for Exchange 2010 using RBAC, see this article or check here for details on how to configure impersonation for Exchange 2007.

Usage
The script Remove-DuplicateItems.ps1 uses the following syntax:

Remove-DuplicateItems.ps1 [-Mailbox] <String> [[-Type] <String>] [-Server <String>] [-Impersonation] [-DeleteMode <String>] [-Mode <String>][-WhatIf] [-Confirm] [<CommonParameters>]


A quick walk-through on the parameters and switches:

• Mailbox is the name of the mailbox to process;
• Type determines what folders are checked for duplicates. Valid options are Mail, Calendar, Contacts, Tasks, Notes or All (Default);
• Server is the name of the Client Access Server to access for Exchange Web Services. When omitted, the script will attempt to use Autodiscover;
• When the Impersonation switch is specified, impersonation will be used for mailbox access, otherwise the current user context will be used;
• DeleteMode specifies how to remove messages. Possible values are HardDelete (permanently deleted), SoftDelete (use dumpster, default) or MoveToDeletedItems (move to Deleted Items folder).
• Mode determines how items are matched. Options are Quick, which uses PidTagSearchKey and is the default mode, or Full which uses a predefined set of attributes to match items, depending on the item class:
 ItemClass Criteria Contacts File As, First Name, Last Name, Company Name, Business Phone, Mobile Phone, Home Phone Distribution List FileAs, Number of Members Calendar Subject, Location, Start & End Date Task Subject, Start Date, Due Date, Status Note Contents, Color Mail Subject, Internet Message ID, DateTimeSent, DateTimeReceived, Sender Other Subject, DateTimeReceived

Few notes:

• When MoveToDeletedItems is specified, the Deleted Items folder will be skipped;
• When Type is omitted or set to All, all folders are scanned, including folders like Conversation History, RSS Feeds, etc.;
• When Quick mode is used and PidTagSearchKey is missing or inaccessible, search will fall back to Full mode;
• For more info on PidTagSearchKey, see http://msdn.microsoft.com/en-us/library/cc815908.aspx. Note that PidTagSearchKey will have duplicate values for copied objects.

So, suppose you want to remove  duplicate Appointments from the calendar of mailbox migtester1 using attribute matching, moving duplicate items to the DeletedItems, using Impersonation and you want to generate extra output using Verbose. In such case, you could use the following cmdlet:

Remove-DuplicateItems.ps1 -Mailbox <MailboxID> -Type Calendar -Impersonation -DeleteMode MoveToDeletedItems -Mode Full -Verbose

In case you want to process multiple mailboxes, you can use a CSV file which needs to contain the Mailbox field. An example of how the CSV could look:

Mailbox
francis
philip

The cmdlet could then be something like:

Import-CSV users.csv1 | Remove-DuplicateItems.ps1 -DeleteMode HardDelete -Impersonation

You can download the script from the TechNet Gallery here.

Feedback
Feedback is welcomed through the comments. If you got scripting suggestions or questions, do not hesitate using the contact form.

Revision History
See TechNet Gallery page.

# Exchange 2013 Unattended Installation Script v1.5 (Updated)

I’m pleased to announce that the Exchange 2013 unattended installation script has been updated and supports fully automated installation of Exchange 2013 on Windows Server 2008 R2 SP1.

The new version contains the following changes:

• Added support for Windows Server 2008 R2 SP1. To fulfill the requirements, code was added to install .NET Framework 4.5, Windows Management Framework 3, disable/enable Internet Explorer Enhanced Security Configuration (IE-ESC), install required hotfixes KB974405, KB2619234 and KB2758857 (which supersedes KB2533623).
• Because of the mandatory reboot after installation of the hotfixes, a phase was inserted; this phase will be skipped when installing on Windows Server 2012.
• Added InstallPath to AutoPilot parameter set (or default path won’t get set).

You can download the updated version of the script via the original Exchange 2013 Unattended Installation Script page (which also contains instructions) or directly from the Technet Gallery.

The script has been tested with Exchange 2013 CU1 but it should work with RTM as well (if you must ..). Your feedback is very much welcomed!

The last version is version 1.53, dated June 15th, 2013; For changes, consult the changelog on the original article or Technet Gallery page.

# Removing Messages by Message Class (Updated)

Recently, I was asked if it is possible to remove stub items. The reason was they were going to transition to a newer version of Exchange and they wouldn’t be using the archiving solution in the new environment. When required, vendor tooling would be used to search through the existing archives.

In such cases it makes sense to remove the stubs from the mailbox, which are shortcut messages that points to a copy of the original message in the archive solution. The new environment won’t contain the required Outlook plugins or extensions to retrieve the original message from the archive using the stub, making the stub lead to a partial or empty message.

To identify stubs, one can filter on an attribute of each item, MessageClass. This attribute defines which kind of item it is (in fact, determines what form Outlook should use in order to present or process the information). Examples of MessageClass definitions are IPM.Note (regular e-mail messages), IPM.Note.EnterpriseVault.Shortcut (message archived by Enterprise Vault) or IPM.ixos-archive (message archived by Opentext/IXOS LiveLink E-Mail Archive).

To identify stubs from Outlook, add the Message Class field to your Outlook view, e.g.:

When you want to remove the stubs using Outlook, you can utilize the Advanced Find function of Outlook, but that is a very labor intensive, tedious and non-centralized per-mailbox procedure:

Now I wouldn’t have started this article if the same thing wasn’t possible with a little bit of scripting against Exchange Web Services and so the script Remove-MessagesClassItems.ps1 was born. Using this script requires Exchange 2007 or later and Exchange Web Services Managed API 1.2 (or later) which you can download here or you can copy the Microsoft.Exchange.WebServices.DLL locally and adjust the DLL path mentioned in the script when necessary. The script has been developed and tested against Exchange 2007, meaning it’s a PowerShell 1.0 script which should be compatible with later versions of PowerShell or Exchange.

Also take notice that since you’ll be processing user mailboxes, you’ll need to have full mailbox access or impersonation permissions; the latter is preferred. For details on how to configure impersonation for Exchange 2010 using RBAC, see this article or check here for details on how to configure impersonation for Exchange 2007.

The script Remove-MessagesClassItems.ps1 uses the following syntax:

Remove-MessageClassItems.ps1 [-Mailbox] <String> [-MessageClass] <String> [-Server <String>] [-Impersonation] [-DeleteMode <String>] [-WhatIf] [-Confirm] [<CommonParameters>]

A quick walk-through on the parameters and switches:

• Mailbox is the name of the mailbox of which to fix the folder structure;
• MessageClass specifies the Message Class to remove, for example IPM.Note.EnterpriseVault.Shortcut (EnterpriseVault);
• Server is the name of the Client Access Server to access for Exchange Web Services. When omitted, the script will attempt to use Autodiscover;
• When the Impersonation switch is specified, impersonation will be used for mailbox access, otherwise the current user context will be used;
• DeleteMode specifies how to remove messages. Possible values are HardDelete (permanently deleted), SoftDelete (use dumpster, default) or MoveToDeletedItems (move to Deleted Items folder).

So for example, suppose you want to remove  IPM.Note.EnterpriseVault.Shortcut items from the mailbox of user1, moving the items to the DeletedItems by Impersonation. In such case, you could use the following cmdlet:

Remove-MessageClassItems.ps1 -Mailbox user1 -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode MoveToDeletedItems -Impersonation –Verbose

Note: By default, Remove-MessageClassItems.ps1 will only process IPF.Note class folders (i.e. containing mail items), so you’ll only see those being processed. If you want all folders scanned (also class-less), use the ScanAllFolders switch.

In case you want to process multiple mailboxes, you can use a CSV file which needs to contain the Mailbox field. An example of how the CSV could look:

Mailbox
francis
philip

The cmdlet could then be something like:

Import-CSV users.csv1 | Remove-MessageClassItems.ps1 -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDelete -Impersonation

You’re feedback is welcomed through the comments; if you got scripting suggestions, please use the contact form.

You can download the script from the Technet Gallery here.

Revision History
See TechNet Gallery page.

# Exchange 2013 Unattended Installation Script v1.1

Coming back from a nice vacation to the beautiful Brittany in France, I thought it was time to collect and process feedback and suggestions on several scripts, starting with the Exchange 2013 unattended installation script for Windows Server 2012.

Changes in version 1.1 of the script:

• When the script was used to also prepare Active Directory, RSAT-ADDS-Tools was uninstalled as part of the cleanup. Per request, I’ve removed the uninstallation of that feature;
• The script now detects pending reboots after installing the required features. When ran in AutoPilot mode, the script will reboot and restart the phase (preparing Active Directory, which can’t be run with pending reboots because Exchange’s Setup won’t like it). When not running in AutoPilot mode, you need to start the script manually. You can omit providing installation parameters as they are saved, even a pending is detected;
• The Windows feature Server-Media-Foundation will be installed explicitly as it is an UCMA 4.0 requirement;
• The credentials provided for AutoPilot mode will be validated;
• The OS version check is changed to a string which should enable installation on non-US Operating Systems.

You can download the updated version of the script via the original Exchange 2013 Unattended Installation Script page or directly from the Technet Gallery. Enjoy!

# Exchange 2013 Unattended Installation Script (Updated)

I’m pleased to announce the availability of Install-Exchange2013.ps1, a PowerShell script to perform a fully unattended setup of Exchange Server 2013 RTM or CU1.

The script takes care of:

• Installing required Windows Server 2008 R2 SP1 / 2012 features and optionally prepare Active Directory (phase 1);
• Install Exchange Server 2013 prerequisites (phase 2);
• Optionally install Exchange Server 2013 (phase 3)
• Optionally – depending on phase 3 – perform post-configuration (phase 4, tailor to your own needs);
• When done, the script will perform some cleaning up, like removing the state file and setting the startup of Transport Service to Automatic (phase 5).

Usage
This script version requires a domain-joined Windows Server 2012 system, an account to perform the installation (and optionally prepare Active Directory) and the location where the Exchange Server 2013 installation files are stored (e.g. an UNC path).

The syntax is as follows:

Install-Exchange2013.ps1 -[InstallCAS|InstallMailbox|InstallBoth] -SourcePath <string> [-Organization <string>] [-MDBName <string>] [-MDBDBPath <string>] [-MDBLogPath <string>] [-InstallPath <string>] [-TargetPath <string>] [-AutoPilot] [-Credentials <pscredential>] [-NoSetup] [<CommonParameters>]

A short description of the parameters:

• Organization (optional): Specifies name of the Exchange organization to create. When omitted, the step to prepare Active Directory (PrepareAD) will be skipped.
• InstallMailbox: Specifies you want to install the Mailbox server role.
• InstallCAS: Specifies you want to install the CAS role.
• InstallBoth: Specifies you want to install both the Mailbox server as well as the CAS role.
• MDBName (optional): Specifies name of the initially created database.
• MDBDBPath (optional): Specifies database path of the initially created database (requires MDBName).
• MDBLogPath (optional): Specifies log path of the initially created database (requires MDBName).
• InstallPath (optional): Specifies (temporary) location of where to store prerequisites, transcript and state file. Default location is C:\Install.
• NoSetup (optional): Specifies you don’t want to perform Exchange setup.
• SourcePath: Specifies location of the Exchange 2013 installation files (setup.exe).
• TargetPath: Specifies the location where to install the Exchange 2013.
• AutoPilot (switch): Specifies you want to automatically restart, logon using credentials specified and continue the installation. When not specified, you will need to restart, logon and start the script manually each time (without parameters).
• Credentials (optional): Specifies credentials to use for automatic logon. Use DOMAIN\User or user@domain. When not specified, you will be prompted to enter credentials.

Note that the script uses an XML file to store the (original) parameters used to start the script but also to keep track of the the process. Of course, if required, you can use predefined XML files to run the script without parameters.

Note that when not present, the script will try to download the prerequisites from the internet. When that isn’t possible or to save bandwidth, you can put them in the location defined by InstallPath and the script will detect and use them.

The post-configuration is currently adding IFilters for OneNote and Publisher (Mailbox) only. There are comments in the script where to add your own additional post-configuration steps.

For example, assume we want to start a fully unattended install of an Exchange Server 2013 Client Access server, using a network location for the Exchange Server 2013 source files. After setting the Execution Policy to Unrestricted and storing the script locally, we start the script using:

 .\Install-Exchange2013.ps1 –InstallCAS –SourcePath ‘\\server\share\isos\Microsoft\Exchange2013\mu_exchange_server_2013_x64_dvd_1112105’ –AutoPilot –Verbose

The script will perform some checks and since AutoPilot was specified without using the Credentials parameter, the script will ask for credentials.

After entering the credentials, the required features will be installed. Since OrganizationName wasn’t specified, Active Directory preparation will be skipped.

After rebooting, the system will automatically log on using the credentials specified earlier and start the script (RunOnce registry key is utilized for this purpose). It will read the last known state from the XML file and will continue with the next phase, which is downloading (when not present) and installing the Exchange prerequisites.

Next, after rebooting and the automatic logon, Exchange will be installed from the source location.

When done, the system will perform post configuration and finalization steps including reboots and logons. Note that it may seem like a lot of reboots, but rebooting after installing Windows features and Exchange prerequisites is required anyway so I put reboots after the other milestones as well.

You can download Install-Exchange2013.ps1 here from the Technet Gallery. Please leave your feedback or bug reports in the comments.

Revision History
1.02: Fixes AD preparation logic and adds checks to see if domain is in native mode. It also fixes a small typo in the post-prepare AD function.
1.03: Tested against Exchange Server 2013 CU1, replaced installing OS features manually by using /InstallWindowsComponents and removed installation of Office Filtering Pack (routines still there because they’re required for the Exchange 2010 SP3 version I’m working on).
1.1: When AD was prepared, RSAT-ADDS-Tools won’t be uninstalled, pending reboot detection (in AutoPilot, script will reboot and restart phase), installs Server-Media-Foundation feature (UCMA 4.0 requirement), validates provided credentials for AutoPilot and checks OS version as string (should accommodate non-US OS).
1.5: Added support for WS2008R2 (i.e. added prereqs NET45, WMF3), IEESC toggling, KB974405, KB2619234, KB2758857 (supersedes KB2533623). Inserted phase for WS2008R2 for installing hotfixes; this phase is skipped for WS2012. Added InstallPath to AutoPilot set (or default won’t be set).
1.51: Rewrote Validate-Credentials due to missing .NET 3.5 Out of the Box in WS2008R2. Testing for proper loading of servermanager module in WS2008R2. Fixed logic in WS2008R2 SP1 detection.
1.52: Fix .NET / PrepareAD order for WS2008R2, relocated RebootPending check.
1.53: Fix phase of Forest/Domain Level check.
1.54: Added parameter InstallBoth to install CAS and Mailbox, workaround as PoSHv2 can discriminate overlapping ParameterSets (resulting in AmbigiousParameterSet).
1.55: Feature installation bug fix on WS2012.

# Fixing Well-Known Folders Troubles (Update)

Depending on your migration scenario, you could be exporting and importing PST files when migrating mailbox contents from one Exchange environment to another. Now folders in PST files are just folders, i.e. Inbox is a folder just like any other folder and well-known folders lose their identification. Because of this, you may end up with unexpected additional folders in your mailbox after importing PST files.

For example, when importing a non-US mailbox PST in a mailbox set to en-US you may end up with well-known folders and their regional counterparts, e.g. “Inbox” and “Postvak In” or “Calendar” and “Agenda”.

It can also happen that you import the PST file before setting the regional settings of the mailbox. In that case the folders with the local names are already present and Exchange will generate sequence number folders, e.g. Inbox1 or Calendar1.

Note that the above behavior goes for all well-known folders, such as Inbox, Calendar, Contacts and Sent Items. Users logging in early, i.e. logging on to their mailbox while they shouldn’t, can also create these situations.

One way to fix this situation is to manually move contents to the proper location using an e-mail client, which is tedious and something you don’t want to trouble end users or admins with. Another way is to set the mailbox’ regional configuration before importing the PST file.

A better way is of course to have an Exchange Management Shell script which talks to Exchange Web Services, essentially doing the same but in an automated kind of fashion. Here’s where the Fix-MailboxFolder.ps1 script comes into play. Using Fix-MailboxFolders.ps1 requires Exchange 2010 SP1 and Exchange Web Services 1.2 (or later), which you can download here. The script hasn’t been tested yet against Exchange 2013.

Since the script will process user mailboxes, it needs to be run by a user with full mailbox permissions or impersonation permissions. Granting full mailbox access can be done on several levels, e.g. mailbox or database. For example, to grant ExAdmin Full Access on a mailbox:

Add-MailboxPermission –Identity User –User ExAdmin –AccessRight FullAccess

However, a more elegant and preferred method is to utilize impersonation through Role-Based Access Control (RBAC). Information on configuring impersonation in your Exchange environment using RBAC is found here. As an example, to grant ExAdmin permissions to impersonate all(!) users in an organization, use the following cmdlets:

New-ManagementRoleAssignment –Name:impersonationAssignmentName –Role:ApplicationImpersonation –User:ExAdmin

After executing this cmdlet, the account ExAdmin can succesfully impersonate mail-enabled users. To limit impersonation to a certain subset of the user population, you could use a write scope; for more information on RBAC and write scopes, check out my past blog on this topic here.

Fix-MailboxFolders.ps1 uses the following syntax:

Fix-MailboxFolders.ps1 [-Mailbox] <String> [[-Language] <String>] [[-FromLanguage] <String>] [[-Server] <String>] [-ScanNumericals] [-Impersonation] [<CommonParameters>]

A quick walk-through on the parameters and switches:

• Mailbox is the name of the mailbox of which to fix the folder structure;
• Language is the language to configure. If this isn’t specified, the script will use a default value of en-US;
• FromLanguage is the language in which to scan for folders. When omitted, the script will use the currently configured mailbox language;
• Server is the name of the Client Access Server to access for Exchange Web Services. When omitted, the script will attempt to use Autodiscover;
• The switch ScanNumericals will tell the script to look for folders in the language specified by FromLanguage with sequence numbers, e.g. Inbox1 or Calendar1;
• When the Impersonation switch is specified, impersonation will be used for mailbox access. When this switch isn’t used, the script will run in the context of the current user.
• Of the common parameters, only the Verbose switch is currently supported and will tell you exactly what the script is doing

So, for example assume you have a mailbox with en-US folders but also Dutch folders and the proper regional setting is en-US, use the following cmdlet:

.\Fix-MailboxFolders.ps1 –Mailbox Francis -Language nl-NL -FromLanguage us-EN –Impersonation

If you want to fix the folders of multiple mailboxes you can use a CSV file. The CSV needs to contain at least the Mailbox, but can optionally settings for Language and FromLanguage since the script will accept both through the pipe.

A sample of how the csv could look:

Mailbox,Language,FromLanguage
francis,en-US,nl-NL
philip,en-US,nl-NL

The cmdlet should then be something like:

Import-Csv .\Users.csv | .\Fix-MailboxFolders.ps1 -Language en-US -ScanNumericals -Impersonation –Verbose

Be advised that the script currently only contains en-US and nl-NL settings; you need to add additional language settings to the $LanguageInfo structure. The$LanguageInfo structure contains localized names for each of the well-known folders and has the following format.

"en-US"= @{
"Inbox"="Inbox";
"SentItems"="Sent Items";
"Notes"="Notes";
"Drafts"="Drafts";
"DeletedItems"="Deleted Items";
"Outbox"="Outbox";
"Contacts"="Contacts";
"Calendar"="Calendar";
"JunkEmail"="Junk E-mail";
"Journal"="Journal";
"DateFormat"="M/d/yyyy";
"TimeFormat"="h:mm tt"
};


To increase readability I’ve created one setting per line in the script; everything could be stored on a single line of course. The values DateFormat and TimeFormat should be provided in a valid format for the related regional setting. Depending on feedback and demand, I can add additional languages to future versions of the script.

I will gladly take feedback on the script in the comments or through the contact form. If you want to start programming against Exchange Web Services yourself, I would highly recommend Glen Scales blog to look for PowerShell sample code.

You can download the script from the Technet Gallery here.

Last version: 1.43, October 16th 2013
(consult Technet Gallery for changes):

# Cluster Name Object Pre-staging

When creating a Database Availability Group (DAG) in Exchange 2010 or Exchange 2013 you leverage Fail-over Clustering from the operating system, e.g. Windows Server 2008 R2.

Behind the scenes Kerberos authentication is used, for which a so called Cluster Name Object (CNO) has to be created in Active Directory. This CNO will be associated with the Cluster Name Resource.

Depending on the situation, like having the ability to create computer accounts in the domain, you may need to create – or pre-stage – the cluster name object as  computer account upfront. For Exchange 2013 on Windows Server 2012, pre-staging the CNO is a requirement. This manual task is described here.

However, there may be circumstances where having the ability to automate the process would be more appropriate, like when you want a fully automated setting up a DAG for example. For this purpose I have created a small script, Create-CNO.ps1. The syntax is as follows:

Create-CNO.ps1 [-Identity] <String> [[-Computers] <Array>] [[-OU] <String>

A small explanation of the available parameters:

• The Identity is used to specify the name of the CNO;
• The optional Computers parameter can be used to specify the computer account which should be granted permissions on the CNO. You can specify multiple accounts seperated by commas (when for example you’re not sure which your will be used to create the DAG). When the Computers parameter is omitted, the Exchange Trusted Subsystem will be granted permissions on the CNO;
• OU is the name of the container to create the CNO in. When not specified, the default container for computer accounts will be used. This is done by querying for the Well-Known GUID for the computers container, aa312825768811d1aded00c04fd8d5cd (more on Well-Known GUIDs here). Note that when specifying the OU, you need to enclose it in quotes otherwise PowerShell will assume the parameter is an array;
• The Verbose parameter is supported.

So, for example assume you want to create a DAG called DAG001 and the first Mailbox Server will be L14Ex1. The computer object for the cluster is to be stored in the OU ou=Temp,dc=litware,dc=com. In that case, you would call the script as follows:

Create-CNO.ps1 –Identity DAG001 –Computers L14EX1 –OU “ou=Temp,dc=litware,dc=com” –Verbose

If you want to grant Exchange Trusted Subsystem permissions as well and let the script look up the CNO name, you can use:

Create-CNO.ps1 –Identity DAG001 –Verbose

You can download the script from the TechNet Gallery here.

# Copying Receive Connectors (update)

Once in a while you may have to execute a task so tedious and repetitive, you end up with an idea for a script to make your life easier. By tidying and publishing that script, I hope to make the life of others easier as well. This is one of those scripts.

When implementing Hub Transport servers on Exchange 2010, you may have to configure multiple receive connectors. Because receive connectors are defined on the Hub Transport server itself, contrary to send connectors, you may end up defining each receive connector on each Hub Transport server. This gets painful when you need to implement the ForeFront Online Protection for Exchange servers in the Remote IP ranges for example.

Yes, you can create a script which configures the connectors for you, but wouldn’t it be nice if you can create definitions on one server using the GUI and then just copy and paste those definitions to the other Hub Transport servers? This script also allows you to simply duplicate existing Receive Connector definitions after adding an additional Hub Transport server afterwards, not only after the initial configuration of the Exchange environment.

Here’s were my Copy-ReceiveConnector.ps1 script may come in handy.

The script is quite simple, and can help you with the following:

• Copy Receive Connectors from one Exchange server to another (CopyFrom);
• Export Receive Connector definitions to an XML file (ExportTo);
• Import Receive Connector definitions from an XML file (ImportFrom).

In addition, you can specify whether you want to overwrite existing Receive Connector definitions (based on name) using the -Overwrite switch or clear all existing Receive Connectors before copying/importing using the -Clear switch.

So, let’s say you have two Hub Transport servers, L12EX1 and L12EX2. You have configured L12EX1 and you need to create the same set of receive connectors on L12EX2.

You can see in the example above, you can use the script to copy definitions from an existing server, e.g.

Copy-ReceiveConnector.ps1 <TargetServer> –CopyFrom <SourceServer>

You can also export and import settings, which may come in handy when you need to troubleshoot (you can have the customer export the receive connectors to a file) or when you want to prepare receive connector definitions off-site, e.g.

Copy-ReceiveConnector.ps1 <TargetServer> –ExportTo .\conn.xml

Copy-ReceiveConnector.ps1 <TargetServer> –ImportFrom .\conn.xml –Clear

Note that when ExchangeServer is specified as AuthMechanism on a receive connector, the FQDN needs to be set to the server’s FQDN, NetBIOS name or $null; in such cases I set it to the FQDN of the target server. Also, it uses the existing name, meaning you may need to rename the Default and Client connectors, which contain the server name, afterwards. Update 24th August, 2012 (v1.1): Added find/replace in Receive Connector name so that “Default L12EX1″ on server L12EX1 will become or match with “Default L12EX2″ on server L12EX2. Click here to download the script from the Technet Gallery. # The case of the missing Free/Busy public folder pt.2 In an earlier blog, I described the situation where a customer had improperly decommissioned Exchange 2003 Administrative Groups and ended up with invalid, orphaned legacyExchangeDN values causing all sorts of issues, most Public Folder / Free Busy related. Read more on this story here. In the blog, I had two options on how to proceed: 1. Edit the legacyExchangeDN attribute of the users affected; 2. Recreate the Free/Busy public folder. In the first blog, I described how to fix the situation using the 2nd option. Here’s how to solve this if you have no Exchange 2003 server left and want to go with the other option. To fix this situation by changing the legacyExchangeDN values, you need to perform the following steps: 1. Identify all mailboxes containing improper legacyExchangeDN values; 2. For all those mailboxes, add the current legacyExchangeDN value as an x500 address; 3. Fix the current legacyExchangeDN. Note that by adding the invalid legacyExchangeDN value as an X500 address, we make sure (responding to) old e-mail messages or nickname entries can resolve properly. You could use tools like ADModify to bulk edit those values. However, you also achieve the same result using a little PowerShell (surprise!), as shown in the following script: Note: Use the script at your own risk. I cannot accept any responsibility for consequences when using this in your production environment. Before using it, prepare it in a lab environment first: test, test, test! Also, this script fixes invalid legacyExchangeDN values; it does not fix any related invalid settings, like delegates; that might be something for a next version when there’s demand for it. $oldDN="/o=ADATUM/ou=First Administrative Group"
$newDN="/o=CONTOSO/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients"$mbx= get-mailbox -Filter "LegacyExchangeDN -like '$($oldDN)/*'"
$mbx | ForEach {$x5= "x500:"+ $_.legacyExchangeDN Set-Mailbox$_.Identity -EmailAddresses @{Add=$x5}$User = [ADSI]("LDAP://"+$_.distinguishedName)$newDN= $newDN+ “/cn=”+$_.Name
$User.Put("legacyExchangeDN",$newDN)
$User.SetInfo() } To use the script, replace the$oldDN value with your old, invalid legacyExchangeDN value (as reported in the Event Log entries with Event ID 14031). Set \$newDN to your new legacyExchangeDN value; the default value of would be in the format “/o=<Organisation Name>/ou=<Administrative Group, i.e. Exchange Administrative Group (FYDIBOHF23SPDLT)>/cn=Recipients/cn=<Name>”.

If you have any questions, drop them in the comments below.

For all the PowerShell purists: Sometimes I prefer readability over trying to fit everything one 1 line. After all, this isn’t an Obfuscated Code Contest