Migrating Outlook profiles with Outlook Profiler!

Table of Contents

Getting from A to B via Z

A while back, I had a requirement to migrate users from old Remote Desktop Session Hosts to a new Windows Server 2019 farm. This was a substantial uplift that needed a "break" from their old roaming profiles - especially since it would uplift users from very old applications to much newer ones.

One of these old applications was Outlook 2010. This posed some challenge - for the most part, user profiles were "disposable" with the setup used; with folder redirection and a lot of Group Policy to control user configurations and settings, you can generally delete a roaming profile without the user noticing any difference. But Outlook profiles are different - they are stored in their roaming profile, and users in this case had multiple inboxes configured in those profiles. We wanted as seamless a migration as possible, so I had a few problems to consider:

  1. Retain the Outlook profile from the old environment, even with a clean break from the old roaming profiles in the new environment
  2. Uplift the Outlook profile to a newer Outlook version, potentially with a different profile name
  3. Import the Outlook profile to the new environment
  4. Keep a log of the migration on a per-user basis

The result was Outlook Profiler!

Outlook Profiler Usage

USAGE: OutlookProfiler Export2010={FilePath} [Options={OptionsFilePath}] [TargetProfile ={ProfileName}] [SourceProfile={ProfileName}] [TargetVersion=2013|2016] [Log={LogPath}] [IgnoreDefault]
     OutlookProfiler Export2013 ={FilePath} [Options={OptionsFilePath}] [TargetProfile ={ProfileName}] [SourceProfile={ProfileName}] [TargetVersion=2013|2016] [Log={LogPath}] [IgnoreDefault]
     OutlookProfiler Export2016={FilePath} [Options={OptionsFilePath}] [TargetProfile ={ProfileName}] [SourceProfile={ProfileName}] [TargetVersion=2013|2016] [Log={LogPath}] [IgnoreDefault]
       OutlookProfiler Import={FilePath} [Options={OptionsFilePath}] [TargetProfile ={ProfileName}] [TargetVersion=2013|2016] [Log={LogPath}]

NOTE: For export operations, you can use IgnoreDefault as an optional parameter to force the SourceProfile to be used instead of the Default Profile.

Most of the challenge in Outlook profiles is in the different registry keys used. Before Outlook 2016, each version of Outlook used a different key to store your profile;

 

VersionRegistry Key
Outlook 2010 or earlierHKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles
Outlook 2013HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook
Outlook 2016 and higherHKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook

From Outlook 2016, it appears Microsoft consistently use the 16.0 key path.

We may also need to look at another key: HKEY_CURRENT_USER\Software\Microsoft\Exchange\Client\Options ... at minimum, this stores the option to "Choose a profile at logon", which you may wish to export to preserve the expected behaviour for a user. An example I encountered is that some users had so many mailboxes to access, they setup multiple Outlook profiles to switch between. I'm not judging 😀

Exporting Profiles

The export is designed to allow transformation during export. We are using Regis3 as a simple way to export the registry keys involved in Outlook profiles, so this provides an opportunity to replace text within the exported file. 

This means that you can (for example) export from an Outlook 2010 profile, specify a new profile name for the destination, and specify that you will be using Outlook 2016 or higher. As noted above, Outlook 2016 and higher appear to use the same registry key, so a single TargetVersion will cover 2016+.

We export the whole Outlook profile key, which means that all Outlook profiles are retained and transformed to new versions, but there's also opportunity to transform the "default" or a specific profile to a new name.

We have a mechanism to examine the user's selected default Outlook profile and carry that selection over, which can be overridden with the IgnoreDefault optional parameter.

Export201x=

For export operations, we provide the Export2010, Export2013, and Export2016 parameters which indicate what version you're exporting from - it simply controls which registry key is selected for the export operation. Usage is simple: Export2013={Path to your export file}. 

Options=

We also provide the Options parameter, which will export the aforementioned Options key (if it exists) to a separate file. Again, usage is Options={Path to options file}

TargetProfile=

Outlook Profiler can rename a profile during export. This is useful when your destination will use a different profile name. 

If you don't specify the optional "IgnoreDefault" parameter, this will be applied to the user's default Outlook profile, even if "SourceProfile" is provided.

SourceProfile=

You can specify the source Outlook profile name to use. However you should note that Outlook Profile will, by default, attempt to select the default profile if it can find it.

By default, Outlook Profiler will ignore this unless the optional "IgnoreDefault" parameter is specified.

TargetVersion=

You can specify the version of Outlook that you will be migrating to. Only 2013 and 2016 is provided; I didn't want to provide a backward migration to Outlook 2010. 

Log=

An optional text log file that can be used to see what happened for a given user's export. Usage is Log={Path to log file}

IgnoreDefault

Optional parameter to specify not to use the default profile to determine the source profile.

Important note before importing - Preventing Outlook first run prompt to create profiles

Newer versions of Outlook have a somewhat annoying behaviour of always prompting to create an Outlook profile, even if one exists (because we imported it!). This posed some difficulty until it could be solved.

Outlook Profiler uses a registry setting called ImportPrf to stop Outlook from performing this prompt on first run. It's instructing Outlook to import a profile from file, rather than doing its default behaviour. For Outlook 2013 and especially Outlook 2016 and higher, the default is autodiscovery from AD, and we're subverting that behaviour to make the import work as intended.

This is based on a Custom15.prf or Custom16.prf existing in your C:\Program Files (x86)\Microsoft Office folder. This file is typically generated by Office tools, but I've included a sample Custom16.prf file in the distribution that includes a couple of customisations, shown below, which help to support the first run behaviour. 

If you place this in the C:\Program Files (x86)\Microsoft Office folder, and name it as either Custom15.prf or Custom16.prf, this should help to mitigate the first run behaviour. You could copy this to machines using Group Policy Preferences.

Depending on your implementation, you may also need to stop Outlook from autoconfiguring the user mailbox from AD - info on this is available from various sites, such as this one

 

;Automatically generated PRF file from the Microsoft Office Customization and Installation Wizard
; **************************************************************
; Section 1 - Profile Defaults
; **************************************************************

[General]
Custom=1
;Set this to your target profile name
ProfileName=Outlook
;Set this to yes
DefaultProfile=Yes
;Don't overwrite
OverwriteProfile=Append
;Don't modify
ModifyDefaultProfileIfPresent=false
;Important - stops multiple profiles being created
BackupProfile=No

Importing Profiles

This is a relatively simple proposition. You're importing the exported registry key from a file to either Outlook 2013 or Outlook 2016+. We default to Outlook 2013 if not specified.

There is no transformation in this step, but the default Outlook profile will be set as part of the import.

To avoid overwriting and possibly corrupting or resetting an existing profile, we do not import Outlook profiles if they already exist. 

Read the above important note about first run behaviour. You should test the behaviour of Outlook on import and modify it using the custom prf file and Group Policy.

 

Import=

A simpler proposition than the export - simply specify the file that you exported using Import={Path to your export file}.

Options=

Specify the path to the options file you exported using Options={Path to your options file}.

Note! It's possible the file doesn't exist since the Export will only export options if they exist. If so, OutlookProfiler will exit with an error - but this is the last step of the import and won't affect the profile that has already been imported.

TargetProfile=

If you recall, we export the whole Outlook profile key. When this is specified with Export, it performs a transformation, but when specified with Import, it specifies the default Outlook profile name within the exported file.

This will cause Outlook Profiler to set your default Outlook profile to this name if it exists.

TargetVersion=

This tells Outlook Profiler where to look for existing profiles, and the default profile setting. This is necessary because Outlook 2013 and Outlook 2016 or higher use different registry keys. 

If not specified, Outlook Profiler will default to Outlook 2013

Log=

An optional text log file that can be used to see what happened for a given user's export. Usage is Log={Path to log file}.

Implementation Example

Being a simple console application, Outlook Profiler can be implemented in a multitude of ways. For example, you could deploy this using the Group Policy Logon Scripts functionality - it works well. You can find this in Group Policy Editor, User Configuration\Policies\Windows Settings\Scripts (Logon and Logoff).

Read the above important note about first run behaviour. You should test the behaviour of Outlook on import and modify it using the custom prf file and Group Policy.

Export

In this example, we've put a copy of OutlookProfiler under the domain controller's NETLOGON folder, and are configuring an export logon script in Group Policy.

This means that every time the user logs in, their Outlook profile will be exported to the file. This is useful when you're preparing for a migration - it keeps the profile up to date with any changes, until you migrate the user over to the new environment.

Logon PropertiesValue
Script Name\\domain.local\NETLOGON\OutlookProfiler\OutlookProfiler.exe
Script Parameters

Export2010=\\fileserver\home\%username%\%username%.profile Options=\\fileserver\home\%username%\%username%.options SourceProfile=CompanyName TargetProfile=Outlook TargetVersion=2016 log=\\fileserver\home\%username%\%username%.export.log

 

Import

On the destination servers, we'll use the same copy of OutlookProfiler to read in the exported profile. Because OutlookProfiler will only import if the profile doesn't already exist, this is a 'safe' operation - we will import the profile on first login only. 

It does provide an interesting side effect - you can delete the user's roaming profile and re-import that exported profile. Handy in some cases.

This assumes you have tested Outlook behaviour on import, and adjusted using custom prf file and group policy (per the above important note).

Logon PropertiesValue
Script Name\\domain.local\NETLOGON\OutlookProfiler\OutlookProfiler.exe
Script ParametersImport=\\fileserver\home\%username%\%username%.profile Options=\\fileserver\home\%username%\%username%.options TargetProfile=Outlook TargetVersion=2016 log=\\fileserver\home\%username%\%username%.import.log

Results

In the above example, we set a group policy in the old environment to run an Outlook Profiler export on login, and another group policy in the new environment to run an Outlook Profiler import on login, which will ensure the first time a user launches Outlook, their profiles will be there.

The below screenshot shows a mock-up of how this works in practice, illustrating an export and import, although I used different settings (such as exporting from Outlook 2016 and importing to Outlook 2013, renaming the profile to Barry).

Outlook Profiler import and export

You can see that, because there was no client options found, the Options file export was not performed, and therefore the Options import errored out. Since this is after the profile import, and is the last operation, I haven't intercepted this exception - it doesn't matter to the success of the profile import.

Get Outlook Profiler!

This is quite a simple application, but it made a huge difference to our migration. The vast majority of users migrated over with all of their Outlook profiles intact, retaining access to their various mailboxes. 

Outlook behaviour makes it imperfect - we ultimately have to resort to a custom prf file to avoid the default first run behaviour - but if you test and modify your prf file and Group Policy settings, you can generally get this going.

I don't expect  that there's a huge requirement for this app out there - it filled a very specific use case - but I've put it up in case someone can use it, or perhaps get ideas from it 😀

Latest version of Outlook Profilers
 
Total downloads of Outlook Profiler

Comments

You may also like:

Playing nicely with others in the Seq ecosystem

You would realise by now that I'm quite a fan of Seq. It's hard not to be, when you can download a free single user license and get started with a trial, a POC, or designing your monitoring and infrastructure. The growth in open source apps for Seq over the...

Lurgle.Alerting - a standardised FluentEmail implementation with extra goodies!

Another Lurgle Around the time that I tackled my original Serilog logging implementation, I also looked at our email alerting. Emails can be used for a variety of reasons, and it's not uncommon that they are sent as a simple string that concatenates or formats variables. In this scenario, the...

Lurgle.Logging - a standardised Serilog implementation with extra goodies!

Logging is important Logging is a really important, oft-neglected, aspect of business applications. I can't state that enough. If you don't have good logging, you can't troubleshoot and debug problems, and you have little chance of seeing what's actually going on in your enterprise. In Structured Logging with Seq and Serilog,...