Exchange-Processor Query Tool: PowerShell Edition

powershellAnyone sizing for Exchange Server 2013 or even still Exchange Server 2010, using the Server Role Requirements Calculator, has to determine processor requirements at some point. This is accomplished by looking up the SPECint_rate2006 score of the planned processor configuration and matching that against the calculated number of required megacycles by the calculator. To account for fail-over situations, additional overhead needs to be added to the number of megacycles. The process as part of the overall sizing has been explained in detail by Jeff Mealiffe here.

The Exchange consultants’ Swiss army knife when determining SPECint rates is the Exchange Processor Query Tool, an Excel sheet designed by Scott Alexander from Microsoft, which allows you to easily look up and determine the SPECint_rate2006 value by inputting a processor model. While still useful, the tool has been out there since 2011. Also, it would be nice sometimes to see which systems are eligible for a certain sizing specification, rather than validating if the planned processor configuration meets the sizing requirements.

So, I wrote a PowerShell script which can query the SPECint rates for you. Because the rating scores are returned as objects, you can perform additional tasks using PowerShell functionality, such as:

  • Use additional criteria, such as vendor, min/max number of cores, etc.
  • Calculate the average SPECint2006 Rate Value for a certain CPU/cores configuration.
  • You can use the SPECint value calculated by the Server Role Requirements Calculator  to find hardware configurations which meet the required total megacycles requirements, optionally including a required overhead percentage.
  • You can select if you are sizing for Exchange Server 2010 or Exchange Server 2013.

Requirements
The script requires PowerShell and internet access to query the SPECint database.

Usage
The script is called Exchange-PQT.ps1, in honor of the Processor Query Tool (PQT).  The syntax is as follows:

Exchange-PQT.ps1 [-CPU <String>] [-Vendor <String>] [-System <String>] [-Overhead <Int32>] [-MinMegaCycles <Int32>] [-Type <String>] [-MinCores <Int32>] [-MaxCores <Int32>] [-MinChips <Int32>] [-MaxChips <Int32>] [<CommonParameters>]

The information returned and which you can use for post-processing is: Vendor, System, CPU (processor description), Cores, Chips (number of CPU’s), CoresPerChip (number of Cores per CPU), Speed, Result, Baseline, MCyclesPerCore (megacycles per core), MCyclesTotal (total megacycles), OS and Published. Note that megacycles calculations are based on the selected Exchange version, by default this is Exchange Server 2013.

A quick walk-through on the parameters:

  • CPU, Vendor or System can be used for partial matching on the respective attribute.
  • Type specifies what calculation to perform. Possible values are 2010 for Exchange Server 2010 and 2013 for Exchange Server 2013. Default value is 2013.
  • MinCores/MaxCores can be used to only return information for systems with this minimum or maximum number of cores. Note that you can not use MinCores and MaxCores at the same time.
  • MinChips/MaxChips can be used to only return information for systems with this minimum or maximum number of CPU’s. Note that you can not use MinChips and MaxChpis at the same time.
  • MinMegaCycles can be used to specify a treshold for the total megacycles value for returned items, using the specified Type for calculations.
  • Overhead can optionally be used to take into account a certain percentage for megacycles overhead. Default is 0 (0%).

Few notes:

  • MinCores/MaxCores and MinChips/MaxChips are mutually exclusive, because we can not specify both in the query against the SPECint database. However, you can use additional filtering on objects returned in the pipeline to distill information, e.g.
    Exchange-PQT.ps1 –MaxCores 32 –MaxChips 8 | Where { $_.Cores –ge 4 –and $_.Chips –ge 2}. 

    Do note that usage of these parameters is recommended when possibe, as it will minimize the result set from SPECint.

  • Make sure you set Type to 2010 when sizing for Exchange 2010.

Examples

Lookup the specifications of the server used by Jeff in his sizing example (Hewlett-Packard DL380p Gen8 server with Intel Xeon E5-2630 processors @2.30GHz). You will notice 25,481 is slightly off from Jeff’s 25,479, which is due to Jeff rounding numbers:

.\Exchange-PQT.ps1 -System 'DL380p Gen8'  -CPU 2630 | select System,MCycle*

Search all specs for systems from Dell containing x5470 processors and return megacycle information for Exchange 2010 calculations:

.\Exchange-PQT.ps1 -CPU x5470 -Vendor 'Dell Inc.' -Type 2010 | Select System,*cycle*

image

Calculate average SPECint 2006 rate values for  hex-core x5450 systems:

.\Exchange-PQT.ps1 -CPU x5470 | Where { $_.Cores -eq 8 } | Measure -Average Result

Search all specs for Dell systems using x5670 CPUs, with a minimum total of 16,000 megacycles and 20% megacycle overhead:

.\Exchange-PQT.ps1 –Vendor Dell -CPU x5670  -MinMegaCycles 16000 -Overhead 20 

image

Download
You can download the script from the TechNet Gallery page.

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.

Impersonation: To be, or pretend to be

imageAs frequent readers of this blog may know, I made several Exchange-related scripts available to the community. Some of these scripts make use of what is called Exchange Web Services (EWS). I receive lots of questions via e-mail and through the comments about configuring impersonation or permission-related issues when running those scripts, which support delegated access as well as impersonation, against mailboxes. This blog shows how can configure delegation, why you should use impersonation, and how to configure impersonation on Exchange 2007 up to Exchange 2013 and Exchange Online in Office 365.

Introduction

EWS provides functionality to allow client applications, such as Outlook or OWA apps, tools, or in my case scripts, to communicate with Exchange server. Even Exchange itself makes uses of EWS when performing Free/Busy lookups by the Availability services for example. EWS was introduced in Exchange Server 2007 back in December 2006, which now seems decades ago.

Some of these EWS scripts or tools access or even manipulate mailbox contents. In the MAPI era, in order for you to access a mailbox that’s not yours, you required delegated full access permissions. These permissions could be granted at the mailbox, mailbox database or mailbox server level. The latter would grant you access to all mailboxes hosted in that mailbox database. For example, to grant an account Archibald full access permission on the mailbox of Nestor, you would typically use something like:

Add-MailboxPermission –Identity Nestor –User Archibald –AccessRights FullAccess –InheritanceType All

Note: Specifying InheritanceType is sometimes overlooked. Not specifying it only configures an Access Control Entry (ACE) on the top level folder (InheritanceType None), resulting in symptoms like scripts not processing subfolders for example.

EWS enables you to use another access method besides delegation, which is impersonation. Impersonation, as the many online available dictionaries may tell to you, is ‘an act of pretending to be another person for the purpose of entertainment or fraud’ or something along those lines. In the Exchange world, this means you can have an account which has the permission to pretend to be the owner of the mailbox, including being subject to the same effective permissions. So, if for some reason the owner only has Read permission on a certain folder, so will the impersonator. Typical use cases for impersonation are for example applications for archiving, reporting or migration, but also scheduled scripts that need to process mailboxes could be one.

Before we dive into the configuration itself, first some of the reasons why you should should prefer Impersonation over delegated access:

  • No mailbox needed for the account requesting access.
  • Throttling benefits, since the operation is subject to the throttling policy settings configured on the mailbox accessed, not the throttling policy configured on the mailbox requesting access. To bypass these delegate limits, one had to configure and assign a separate throttling policy with no limits for the account. Of course, a bad behaving application could then run without boundaries from a resource perspective, something throttling policies try to limit.
  • In Exchange 2010 and up, impersonation leverages Role Based Access Control, which is better manageable than a collection of distributed  ACEs.
  • Actions performed by the impersonator are on behalf of the impersonated. This may complicate auditing, as logging will come up with actions performed by the impersonated user, not the impersonator.

Note that where ‘user’ is specified below with regards to granting permissions, one could also specify a security group as well unless mentioned otherwise.

Impersonation on Exchange 2007

On Exchange 2007, you configure impersonation by granting the following two permissions:

  • The ms-Exch-EPI-Impersonation permission grants the impersonator the right to submit impersonation calls. It is configured on Client Access Servers. This does not grant the impersonation right, just the right the make the call through a CAS server.
  • The ms-Exch-EPI-May-Impersonate when granted, allows the impersonator to impersonate selected accounts.

To configure these permissions in your Exchange 2007 environment, use:

Get-ClientAccessServer | Add-AdPermission –User svcExchangeScripts –ExtendedRights ms-Exch-EPI-Impersonation

Then, we can configure impersonation permission on the mailbox level:

Get-Mailbox Tintin| Add-ADPermission –User svcExchangeScripts –ExtendedRights ms-Exch-EPI-May-Impersonate

on the database level:

Get-MailboxDatabase MailboxDB1 | Add-ADPermission –User svcExchangeScripts –ExtendedRights ms-Exch-EPI-May-Impersonate

or mailbox server level:

Get-MailboxServer MailboxServer1 | Add-ADPermission –User svcExchangeScripts –ExtendedRights ms-Exch-EPI-May-Impersonate

Be advised that members of the various built-in Admin groups are by default explicitly denied impersonation permissions on the server and database level, and deny overrules allow. You will notice this when querying impersonation configuration settings, for example on the database level (in the screenshot example, olrik was granted impersonation permissions):

Get-MailboxDatabase | Get-AdPermission | Where { $_.ExtendedRights –like ‘ms-Exch-EPI-Impersonation’} | Format-Table Identity, User, Deny, IsInherited, ExtendedRights –AutoSize

 image

Note that permissions assigned on the mailbox may not immediately be reflected as you are administering them in Active Directory. Changes in Active Directory are subject to AD replication, and the Exchange Information Store caches information for up to 2 hours, so worst case it may take up to 2 hours and 15 minutes for new permission settings to be re-read from Active Directory.

Impersonation on Exchange 2010 and 2013

Exchange 2010 introduced Role Based Access Control, better known by its acronym RBAC. For a quick introduction to RBAC, see one of my earlier blogs here. There is a management role associated with impersonation, which is ApplicationImpersonation.

To enable a user impersonation rights, create a new assignment for ApplicationImpersonation and assign it to the user:

New-ManagementRoleAssignment –Name 'AIsvcExchangeScripts' –Role ApplicationImpersonation –User svcExchangeScripts

Note that if we want to assign these permissions to a security group, we need to use the SecurityGroup parameter instead of User, specifying the group name.

Now be careful, when used like this you will have granted that user or group permission to impersonate all users in your Exchange organization. Here is where RBAC comes into play, or more specific the RBAC feature named management role scopes. With write scopes for example, you can limit the scope of where you can make changes in Active Directory. For more information on management role scopes, see here.

Let  us assume we want to limit the scope to a distribution group named ‘All Employees’, using New-ManagementScope in combination with RecipientRestrictionFilter. Note that when specifying MemberOfGroup in the filter, you need to use the distinguishedName of the group:

New-ManagementScope –Name 'Employee Mailboxes' –RecipientRestrictionFilter { MemberOfGroup –eq 'CN=All Employees,OU=Distribution Groups,OU=NL,DC=contoso,DC=com'} 

We can then apply this scope to the assignment created earlier:

Set-ManagementRoleAssignment –Identity 'AIsvcExchangeScripts' –CustomWriteScope 'Employee Mailboxes'

Impersonation on Exchange Online

Impersonation is available in most Office 365 plans, but currently not in the small business plans.  To configure Impersonation in Exchange Online we need to connect anyway, so we’ll first open a remote PowerShell session to Exchange Online:

$EXO= New-PsSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -AllowRedirection -Authentication Basic
Import-PsSession $EXO

Provide tenant administrator credentials when prompted. You can then see if you have the ApplicationImpersonation role at your disposal using:

Get-ManagementRole –Identity ApplicationImpersonation

If nothing is returned, you may need to resort to delegate access permissions.

Configuring impersonation is identical to configuring it in Exchange 2013. Nonetheless, some people may be more comfortable using the Exchange Admin Center. If so:

  1. Open up Exchange Admin Center.
  2. Navigate to Permissions > Admin Roles
  3. Now we can’t directly assign a management role through EAC, so assume we’ll create a role group for our application account by clicking New (+).
  4. Enter a name for your role group, e.g. ExchangeMaintenanceScripts.
  5. Add the role ApplicationImpersonation.
  6. Add the accounts which need Impersonation permissions, e.g. svcExchangeScript.
  7. Optionally, you can also select a Write Scope, which you need to create upfront through Exchange Management Shell.
  8. In Exchange on-premises, instead of a Write Scope you will have the option to select a a specific OU instead (scope filter RecipientRoot parameter) .
  9. When done, Save.

 image

 One word of caution: scopes are not automatically updated when objects referenced are relocated or change names. Now, for your own environment you may have this under control through some form of change management process. For Exchange Online however, your tenant might get relocated without notice. Therefor, should impersonation fail, verify any management scopes you may have defined for distinguishedName references, and check if they require updating, e.g.

Set-ManagementScope -Name 'All Employees' -RecipientRestrictionFilter { MemberOfGroup -eq 'CN=All Employees,OU=contoso.onmicrosoft.com,OU=Microsoft Exchange Hosted Organizations,DC=EURPR05A001,DC=prod,DC=outlook,DC=com'}

Final words

Note that many EWS-based scripts or tools do not natively support EWS but make use of the Exchange Web Services Managed API. This installable package consists of support files (e.g. DLL’s) which provide EWS functions to your PowerShell environment. You can download the current version of EWS Managed API here (2.2). You can read more on developing with EWS Managed API here, or you can have a peek at the source of code of one of my EWS scripts or the ones published by Exchange MVP-fellow Glen Scales’ here.

Script Updates

powershellA small heads-up for those not following me on Twitter of one of the other social media channels. Last week I made updates to the following three scripts:

Install-Exchange2013.ps1, version 1.72

  • Added CU5 support
  • Added KB2971467 (CU5 Disable Shared Cache Service Managed Availability probes)

Remove-DuplicateItems.ps1, version 1.3

  • Changed parameter Mailbox, you can now use an e-mail address as well.
  • Added parameter Credentials.
  • Added item class and size for certain duplication checks.
  • Changed item removal process
  • Remove items after, not while processing folder. Avoids asynchronous deletion issues.
  • Works against Office 365.

Remove-MessageClassItems.ps1, version 1.3

  • Changed parameter Mailbox, you can now use an e-mail address as well
  • Added parameter Credentials
  • Added parameter PartialMatching for partial class name matching.
  • Changed item removal process. Remove items after, not while processing folder. Avoids asynchronous deletion issues.
  • Works against Office 365.
  • Deleted Items folder will be processed, unless MoveToDeletedItems is used.
  • Changed EWS DLL loading, can now be in current folder as well.

Be advised I keep am overview of the scripts and their current versions with publish dates here.

 

Get-MyMailboxStatistics

powershellLast update: Version 1.02, November 21st, 2014.

Those leveraging quota settings to manage their Exchange environments, you are probably periodically running some sort of script or set of cmdlets to retrieve information on mailbox sizes, quota settings and if any mailbox is above any of the quota thresholds. For a quick indication of the current size in relation to the quota settings, StorageLimitStatus may contain one of the following indicators depending on the quota settings on the mailbox or mailbox database hosting the mailbox:

  • BelowLimit – Speaks for itself
  • IssueWarning – Mailbox size above Issue Warning limit
  • ProhibitSend – Mailbox size above Prohibit Send limit
  • NoChecking – No quota checking
  • MailboxDisabled – Mailbox size above Prohibit Send and Receive quota limit

So, to get a list of all mailboxes with any over-quota status, you can use:

Get-MailboxDatabase | Get-MailboxStatistics | Where {$_.StorageLimitStatus -match 'IssueWarning|ProhibitSend|MailboxDisabled'} | Select DisplayName, ItemCount, TotalItemSize, StorageLimitStatus, LastLogonTime

Unfortunately, in Exchange 2013 the StorageLimitStatus gets no longer populated:

image

As KB2819389 explains, this is by design. In Exchange 2013, mailbox quotas are no longer cached. By not being cached, retrieving quota information may result in poor performance as it queries Active Directory for quota related attributes. The argument is a bit puzzling, considering there is a NoADLookup switch which directs the cmdlet to retrieve information from the mailbox database (cache) instead of Active Directory. Perhaps a better workaround would have been to make NoADLookup a parameter, make it $true by default and leave StorageLimitStatus unpopulated when NoADLookup is $true.

Of course, that does not help customers who want a quick quota report. For this purpose I have created two things in 1 script:

  1. A helper function Get-StorageLimitStatus() which will take a mailbox statistics object and return a StorageLimitStatus object.
  2. A script Get-MyMailboxStatistics.ps1, a proxy function for Get-MailboxStatistics which will use the Get-StorageLimitStatus helper function to populate the StorageLimitStatus.

Get-StorageLimitStatus
When you want to use the helper function, extract it and include it in your quota reporting script or PowerShell profile (making it available when firing up a shell). To use the helper function in the cmdlet shown earlier, use:

Get-MailboxDatabase | Get-MailboxStatistics | Select -ExcludeProperty StorageLimitStatus DisplayName, ItemCount, TotalItemSize, @{n="StorageLimitStatus"; e={ Get-StorageLimitStatus $_}}, LastLogonTime | Where {$_.StorageLimitStatus -match 'IssueWarning|ProhibitSend|MailboxDisabled'}

This will remove StorageLimitStatus from the output and add a calculated field bearing the same the name, calling the Get-StorageLimitStatus helper function with the current mailbox statistics object to set its value.

Get-MyMailboxStatistics.ps1
This is a proxy function for the Exchange Management Shell cmdlet Get-MailboxStatistics. This means that the current, original cmdlet was used to create a wrapper which will call the original cmdlet. Having a wrapper allows you to restrict or enhance the original cmdlet and tailor it to your needs.

A quick tip on how to create a proxy script in the clipboard (more information on creating proxy commands here):

$data= New-Object System.Management.Automation.CommandMetaData (Get-Command Get-MailboxStatistics) 
[System.Management.Automation.ProxyCommand]::create($data) | clip.exe

Downside is that future changes to the Get-MailboxStatistics cmdlet will not be automatically incorporated in the wrapper. Feeding it objects also doesn’t work, but you can work around that by temporary storing the objects in a variable and passing that to the script (see examples below).

To populate the StorageLimitStatus, we will post-process each object in the output of Get-MailboxStatistics, using Add-Member to overwrite (-Force) its current value and –PassThru to pass it along in the pipeline. Being a proxy command, the parameter options are identical to the original Get-MailboxStatistics. Some examples:

.\Get-MailboxStatistics.ps1 -Database MDB2
$m= Get-Mailbox –Database MDB2 
$m | .\Get-MailboxStatistics.ps1 | ft –AutoSize DisplayName,TotalItemSize,StorageLimitStatus

image

Do be aware that this will incur Active Directory queries and thus performance of the script may not seem fast. However, in previous versions of Exchange you got immediate results as all the quota information was readily available from the cache. On the plus side, the status you see will be non-cached, current information.

On a final note and maybe needless to say that in order to use this you need to run it from the Exchange Management Shell and since it’s an unsigned script you need to set ExecutionPolicy to Unrestricted.

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

Download
You can download the script from the TechNet Gallery here.

Revision History
See Technet Gallery page.

Cmdlet Extension Agents & XML Case Sensitivity

Ex2013 LogoOccasionally, I get requests to come to the aid of a fellow IT professional (I seldomly get requests to come to the aid of fair maidens. Oh, well). This weekend I responded to one of those distress calls by someone who couldn’t get his Cmdlet Extension Agent to work. This post is a quick heads-up for the collective memory of IT Professionals as it took me quiet a bit of screen staring to spot the issue.

For those unfamiliar with Cmdlet Extension Agents, they are modules which allow you to enhance or customize the behavior of cmdlets in Exchange. For example, the built-in Mailbox Resources Management agent is responsible for picking the database when creating new mailboxes when a database hasn’t been specified. By means of the Script Agent and an XML file named ScriptingAgentConfig.xml containing PowerShell code fragments, you can enhance and tailor Exchange cmdlets to your own needs. For those interested in Cmdlet Extension Agents, I did two earlier articles on Cmdlet Extension Agents, here and here.

The code provided was a simple agent to enhance New-Mailbox with enabling SingleItemRecovery after the mailbox was created:

<?xml version="1.0" encoding="utf-8" ?>
<Configuration version="1.0">    
        <Feature Name="Mailbox Provisioning" cmdlets="new-mailbox">
         <ApiCall Name="OnComplete">
             if($succeeded) {
                $newmailbox = $provisioningHandler.UserSpecifiedParameters["Name"]
                set-mailbox $newmailbox -SingleItemRecoveryEnabled $true
                }
        </ApiCall>
        </Feature>
</Configuration>

The Scripting Agent was enabled using Enable-CmdletExtensionAgent. Yet, for some reason after creating a new mailbox, SingleItemRecoveryEnabled wasn’t enabled. Running New-Mailbox in Verbose mode showed that the Scripting Agent did not come into play:

Capture1Now know that debugging and troubleshooting Cmdlet Extension Agents can be an unpleasant task since you put PowerShell code in XML files and there is no way to easily perform simple tests except running the Cmdlet you’re customizing or developing script fragments in an external PowerShell script and copy/paste it in the Scripting Agent XML file when you think it’s ready for and want to perform some final tests.

I didn’t immediately spot it, so to see if the problem was actually in the XML I picked the example of my 2nd article on Cmdlet Extension Agents and I compared it with the non-working XML using WinMerge (which by the way is an excellent tool to compare code or plain texts):

WinMergeIt took some time to discover why the Scripting Agent wouldn’t pick up the XML and it can be easily overlooked. The culprit in underlined in red: the C of the Cmdlets attribute in the Feature tag should be uppercase. Doh! This case sensitivity is perhaps not a primary suspect by Windows folks, as mostly we don’t have to worry about it and things “just work”, but in the case of XML it is essential. The XML standard prescribes that element names (<Feature> .. </Feature>) and attribute names (<Feature Cmdlets=..>) are case-sensitive entitling the Scripting Agent to be strict.

Configuring Anti-Affinity in Failover Clusters

powershellMany 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.

image

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:

image

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

Download
You can download the script from the TechNet Gallery here.

Revision History
-

Removing Duplicate Items from a Mailbox

powershellLast version: 1.41, November 26th, 2014.

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.

image

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>] [-Retain <String>] [-Server <String>] [-Impersonation] [-DeleteMode <String>] [-Credentials <PSCredential>] [-Mode <String>] [-MailboxOnly] [-ArchiveOnly] [-WhatIf] [-Confirm] [<CommonParameters>]

A quick walk-through on the parameters and switches:

  • Mailbox is the e-mail address or name of the mailbox to process. If name is used, it is matched against cn/SAMAccountname/email address of local AD.
  • Type determines what folders are checked for duplicates. Valid options are Mail, Calendar, Contacts, Tasks, Notes or All (Default).
  • Retain determines which item to retain by comparing last modification times. Valid options are Newest (default) or Oldest.
  • 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).
  • Credentials specifies credentials to use (provide credentials using Get-Credential).
  • 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, Size
Distribution List FileAs, Number of Members, Size
Calendar Subject, Location, Start & End Date, Size
Task Subject, Start Date, Due Date, Status, Size
Note Contents, Color, Size
Mail Subject, Internet Message ID, DateTimeSent, DateTimeReceived, Sender, Size
Other Subject, DateTimeReceived
  • MailboxOnly specifies you only want to process the primary mailbox of specified users. You als need to use this parameter  when running against mailboxes on Exchange Server 2007.
  • ArchiveOnly specifies you only want to process personal archives of specified users.

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.
  • You need to specify MailboxOnly when running against mailboxes on Exchange Server 2007 as the Exchange 2010 personal archive options in EWSare not support in Exchange 2007 mode.

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 migtester1 -Type Calendar -Impersonation -DeleteMode MoveToDeletedItems -Mode Full -Verbose

image

Alternative, you can use an e-mail address and specify credentials.  This allows the script to run against mailboxes in Office 365, for example:

Remove-DuplicateItems.ps1 -Mailbox olrik@office365tenant.onmicrosoft.com -Type Mail -DeleteMode MoveToDeletedItems -Mode Full -Credentials (Get-Credential) -Retain Oldest

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

Download
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)

Ex2013 LogoI’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)

powershellLast version: 1.41, November 26th, 2014.

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.:

StubsOutlook

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:

SearchFromOutlook

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] [-Credentials <PSCredential>] [-PartialMatching] [-DeleteMode <String>] [-ScanAllFolders] [-Before <DateTime>] [-MailboxOnly] [-ArchiveOnly] 
 [-WhatIf] [-Confirm] [<CommonParameters>]  

A quick walk-through on the parameters and switches:

  • Mailbox is the name or e-mail address of the mailbox.
  • MessageClass specifies the Message Class to remove, for example IPM.Note.EnterpriseVault.Shortcut (EnterpriseVault).
  • Switch PartialMatch specifies if you want to use exact matching of MessageClass or partial matching. When enabled, specifying ‘EnterpriseVault’ will select all items containing ‘EnterpriseVault’ substring in message class.
  • Server is the name of the Client Access Server to access for Exchange Web Services. When omitted, the script will attempt to use Autodiscover.
  • Switch Impersonation specifies if 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). Note that the Deleted Items folder will be processed, unless MoveToDeletedItems is used.
  • Credentials specifies the user credentials to use.
  • Parameter Before can be used to only remove items received before specified date.
  • MailboxOnly specifies you only want to process the primary mailbox of specified users. You als need to use this parameter  when running against mailboxes on Exchange Server 2007.
  • ArchiveOnly specifies you only want to process personal archives of specified users.

So for example, suppose you want to remove  IPM.Note.EnterpriseVault.Shortcut items from the mailbox of user1 and personal archive when enabled, 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 

SampleOutput

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.

The script also supports Office 365. For example, to remove all items with ‘Enterprise’ in their message class text, received before 1/1/2014, and only from the primary mailbox, you can use:

$Credentials= Get-Credential
Remove-MessageClassItems.ps1 -Mailbox olrik@office365tenant.com -DeleteMode MoveToDeletedItems -MessageClass EnterpriseVault -PartialMatching -Before 1/1/2014 -MailboxOnly -Credentials $Credentials

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

Ex2013 LogoComing 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!