How-to – Importing PSTs into Exchange 2010


Importing a PST file into Exchange 2010 requires the use of the Exchange Management Shell (EMS), although (somewhat confusingly) this functionality was originally included in the Exchange Management Console in the Beta release of Exchange 2010.

A PowerShell cmdlet in the EMS is used to perform the action, and the use of this cmdlet requires that the current user has Import Export roles enabled on their profile. In order to run the import and export cmdlets, Exchange 2010 RTM also requires that Outlook 2010 x64 is installed on the machine being used to run said cmdlets, although this is no longer a requirement of Exchange 2010 SP1.

So, to import a single PST file into an exchange 2010 mailbox:

  1. Install Outlook 2010 x64 on the Exchange server. Bear in mind that, by default, on a machine with no pre-existing Office installation, the DVD’s autorun setup will try to install the x86 applications. Be sure to manually run the installer rom the x64 directory to install the x64 version of Outlook, although this step is not necessary for Exchange 2010 SP1, as this now (re)includes a MAPI provider.
  2. Enable Import Permissions for a security group which your user belongs to – In this case, ‘Mailbox Support’ – with the following command:
New-ManagementRoleAssignment -Name "Import Export Mailbox Admins" `
                             -SecurityGroup "Mailbox Support" `
                             -Role "Mailbox Import Export"
  1. Import the desired PST file into the appropriate user’s mailbox with the following command:
Import-Mailbox -PSTFolderPath pstfilepath -Identity exchangealias

Exchange 2010 SP1 differs a little, in that you don’t need to install Outlook 2010 x64, and rather than the synchronous Import-Mailbox cmdlet, the asynchronous New-MailBoxImportRequest can be used, which takes the form of:

New-MailboxImportRequest -FilePath pstfilepath -Mailbox mailbox

The status of current requests can be viewed with the Get-MailboxImportRequest cmdlet, and completed requests can be cleared (or pending/In-progress requests cancelled) with the Remove-MailboxImportRequest cmdlet. One of the advantages of this new cmdlet, other than it being asynchronous, is that you can specify an additional –IsArchive parameter, which will import the PST directly into the users archive mailbox.

I did experience a few problems using these cmdlets during the brief time I spent doing research for this guide. The Exchange 2010 RTM Import-Mailbox on one system simply refused to play nicely, and kept throwing the following error:

 Error was found for <Mailbox Name> because: Error occurred in the step: Approving object. An unknown error
 has occurred., error code: -2147221219
     + CategoryInfo          : InvalidOperation: (0:Int32) [Import-Mailbox], RecipientTaskException
     + FullyQualifiedErrorId : CFFD629B,Microsoft.Exchange.Management.RecipientTasks.ImportMailbox

Not a lot of help in itself, although a little Googling and experimentation revealed four main potential causes for this error:

  1. The appropriate role permissions has not been added to the users security profile.
  2. The version of MAPI being used has somehow got confused, which could be fixed by running thefixmapicommand from the command prompt.
  3. The PST file is password protected.
  4. There is a bug in exchange.

In my case, I’d unfortunately hit the forth problem, and the workaround proved to be pretty horrific – it may simply be worth waiting for a fix from Microsoft. To complete my task, I had to temporarily add a new domain controller to my network to host a new Exchange 2010 server. I then moved the target mailboxes (i.e. the ones for which I had PSTs to import) across to this new server, performed the import, and then moved the mailboxes back to their original Exchange server and removed the temporary server from the network (Like I said, pretty horrific).

Upgrading the system to 2010 SP1 and using the New-MailBoxImportRequest cmdlet on the same system yielded the following error:

Couldn't connect to the target mailbox.
    + CategoryInfo          : NotSpecified: (0:Int32) [New-MailboxImportRequest], RemoteTransientException
    + FullyQualifiedErrorId : 1B0DDEBA,Microsoft.Exchange.Management.RecipientTasks.NewMailboxImportRequest

Again, this appears to be a known issue, and apparently one which is scheduled to be fixed before the final release of SP1.

Finding PST files on the network

So, we’ve seen the process for importing a local PST file into Exchange server, however, in reality, it’s likely that these PST files are scattered liberally around your network on the hard-drives of your users’ machines as a result of Outlook’s new personal archiving functionality. Ideally, so that this mass-import process is transparent to your users, you’d like some way of finding all of these PST files, pairing them up with their users, and then simply importing them into the appropriate mailbox.

There are a few steps required to set up something like that. First, we can query Active Directory for a list of all the machines attached to your domain. We can then use WMI to search each of these machines for PST files, and the file paths for these PSTs should hopefully give us a clue as to which user they belong to (by default, they will be created in a directory path containing the username.) We can also grab the file owner file attribute, which should correlate with the details in the file path.

Naturally, this technique requires that all of the machines in your network are switched on and accessible by WMI, although a list of the machines which could not be queried can be provided as an output.

Notes about WMI

By default, WMI is blocked by the windows firewall in Windows 7 and 2008 R2, so you’ll probably need to open up the ports on all of your users’ machines. This can be done with the netsh command, or through a change to group policy.

You might quite rightly be asking yourself “What are the implications of this?” WMI is a powerful beast which allows remote access to many aspects of a user’s machine. As such, it could be considered a significant security vulnerability. In addition, it’s typically accessed though port 135, which not only permits access to WMI, but also to any other DCOM components which may be installed on a machine, thus opening the way for exploitation by Trojans and the like. Needless to say, the ports are blocked by default for a reason, so carefully consider all of the implications when opening them.

WMI will also not help you if the machines you wish to tinker with are subject to NAT (Network Address Translation) – You’ll simply be unable to reach these machines.

Nevertheless, let’s assume a situation without any NAT, and where the security risks have been minimised. The following script generates a txt file (the filename defined on line 2) of all the computers on your domain to be searched. This file can then be manually edited with notepad to remove any machines you don’t wish to search:

$strCategory = "computer"
$strOutput = "c:\computernames.txt"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
[bool]$firstOutput = $true
foreach ($objResult in $colResults)
      $objComputer = $objResult.Properties;
            Write-output $ | Out-File -filepath $strOutput
            $firstOutput = $false;
            Write-output $ | Out-File -filepath $strOutput `

Listing 1 – A PowerShell script to generate a list of all machines on your domain which are to be searched for PST files.

The next script will generate a CSV (Comma separated values) file detailing the network paths of the PST files you need to import:

$strComputers = Get-Content -Path "c:\computernames.txt"
[bool]$firstOutput = $true
foreach($strComputer in $strComputers)
   $colFiles = Get-Wmiobject -namespace "root\CIMV2" `
                             -computername $strComputer `
                             -Query "Select * from CIM_DataFile `
                                              Where Extension = 'pst'"
   foreach ($objFile in $colFiles)
      if($objFile.FileName -ne $null)
         $filepath = $objFile.Drive + $objFile.Path + $objFile.FileName + "." `
                    + $objFile.Extension;
         $query = "ASSOCIATORS OF {Win32_LogicalFileSecuritySetting='" `
                 + $filepath `
                 + "'} WHERE AssocClass=Win32_LogicalFileOwner ResultRole=Owner"
          $colOwners = Get-Wmiobject -namespace "root\CIMV2" `
                                     -computername $strComputer `
                                     -Query $query
          $objOwner = $ colOwners[0]
          $user = $objOwner.ReferencedDomainName + "\" + $objOwner.AccountName
          $output = $strComputer + "," + $filepath + "," + $user
              Write-output $output | Out-File -filepath c:\pstdetails.csv
             $firstOutput = $false
              Write-output $output | Out-File -filepath c:\pstdetails.csv -append

Listing 2 – A PowerShell script to find and list network paths for PST files to be imported.

This script will take as input a text file containing a list of machine names, which is, conveniently, the output of the first script. It will then generate a .csv file of all the PST files found on those machines, and the owners associated with them. So far, so painless.

Importing the remote PSTs into Exchange

Now that we’ve seen how to gain a list of machines and their respective PST files, we now need to import these files into Exchange. The following script does just that:

# Read in pst file locations and users
$strPSTFiles = Get-Content -Path "c:\pstdetails.csv"
foreach($strPSTFile in $strPSTFiles)
      $strMachine = $strPSTFile.Split(',')[0]
      $strPath = $strPSTFile.Split(',')[1]
      $strOwner = $strPSTFile.Split(',')[2]
      # Get network path for pst file
      $source = "\\" + $strMachine + "\" + $strPath.Replace(':','$')
      # import pst to mail box.
      Import-Mailbox -PSTFolderPath $source -Identity $strOwner
      New-MailboxImportRequest -FilePath $source -Mailbox $strOwner

Listing 3 – PowerShell to import a list of PST files into Exchange from their respective machines.

The yellow highlighted text shows the Exchange 2010 RTM Cmdlet, and the red shops the Exchange 2010 SP1 (delete as appropriate).

The Exchange 2010 SP1 version of the script will execute in far less time than the original RTM version due to the asynchronous nature of the ImportRequest cmdlet. These requests are processed in the background and can be monitored with the Get-MailboxImportRequest cmdlet to observe their status. Once these have completed, as mentioned earlier, it’s necessary to actively clear the requests with the Remove-MailboxImportRequest cmdlet. As easy as this all sounds, there are quite a few potential pitfalls here:

  • The users’ machines must be on,
  • File sharing must be on to allow for the file to be transferred,
  • Outlook must not be running on the remote users’ machines – If outlook is running and has the PST file attached, then the file will be locked and unavailable for importing,
  • Passwords are not supported – The PowerShell cmdlet used by exchange to import PST files simply doesn’t handle passwords.
  • There’s a limit on concurrent requests – With the SP1 asynchronous requests, no more than 10 concurrent requests can be handled per mailbox without manually specifying a unique name for each request (this makes the script a little more complicated, but is not a showstopper, particularly given that most users will only have a single errant PST file to be imported.)

That being said, there are various things you could to augment this script; some suggestions include:

  • Having WMI shut down Outlook on remote users’ machines before attempting import.
  • Generating a further output file detailing a list of all the PSTs which failed to import, with reasons why. It would be useful to know if these files were password protected, or the machine hosting them was shut down or had disconnected since they were identified.
  • In the SP1 case, you could automate the polling of the requests statuses, and the removal of those which have completed.