Fixing Well-Known Folders Troubles (Update)

powershellDepending 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”.

image

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.

image

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

image

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"; 
     "Tasks"="Tasks"; 
     "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):

45 thoughts on “Fixing Well-Known Folders Troubles (Update)

  1. Excellent! At last a script that is easy to understand for this problem. This has been a problem forever on Exchange when doing migration on Exchange environments at companies who are multilanguage. I hope Microsoft one day simply doesn’t change the actual folder names according to the language but rather the “interface” just shows the language the user has chosen. Would be much easier to perform migrations if you don’t have to care about language setting of a mailbox…

  2. For those inquiring; yes I know there’s a script from Steen Kirkby/Tamas Flamich (Cleanup-Mailbox) but that’s very limited; it handles limited folder depth and doesn’t do sequence folders for instance. Also, it’s less readable.

  3. Hi Michel,

    I gets “The script failed due to call depth overflow. The call depth reached 1001 and the maximum is 1000.” error when I run the script.

    The strange thing is that I could do 3 mailboxes, and now I get this error every time, I tried to both exchange servers, still the same error.

    Do you have any idea how to fix that?

    Cheers

  4. Pingback: The UC Architects Podcast Ep16 | EighTwOne (821)

  5. Great post!
    Michel, do you think I can use impersonation to execute “Get-MailboFolder” against a different mailbox from the one executing the powershell?

  6. Hey,
    Looks to be a great script for my foldertroubles but im running into trouble on Exchange2013 with error “Cannot find an overload for “makegenerictype” and the argument count: “1”

    Any idea if this command isn’t supported on exchange 2013?

    • Sounds like a PowerShell v3 thing. Try replacing
      $ItemIds= [activator]::createinstance(([type]‘System.Collections.Generic.List`1′).makegenerictype([Microsoft.Exchange.WebServices.Data.ItemId]))

      with

      $type = (“System.Collections.Generic.List”+’`’+”1″) -as “Type”
      $type = $type.MakeGenericType(“Microsoft.Exchange.WebServices.Data.ItemId” -as “Type”)
      $ItemIds = [Activator]::CreateInstance($type)

      and let me know if that works for you.

      • The solution you gave is correct for me :) Thank you so much!!

        I am using Windows Server 2012 R2 and this works:
        $type = (“System.Collections.Generic.List”+’`’+”1″) -as “Type”
        $type = $type.MakeGenericType(“Microsoft.Exchange.WebServices.Data.ItemId” -as “Type”)
        $ItemIds = [Activator]::CreateInstance($type)

        On Windows Server 2008 R2 worked with this (as you also said above):
        $ItemIds= [activator]::createinstance(([type]‘System.Collections.Generic.List`1′).makegenerictype([Microsoft.Exchange.WebServices.Data.ItemId]))

        Thank you so much Michel

  7. We have the same issue here. When running the script we get the message: Can’t access mailbox information store. We set the Full Access permission for the Exchange administrator and we set the impersonation options using the command above. Our environment has two backend exchange servers configured as DAG and two frontend exchange servers. The servers are all in a resources domain, all users are in separate domains. All domains are in one forest. Every users domain has it’s corresponding mail database. Any idea?

      • running the script as the same user who installed Exchange, running from either frontend and backend server. I cannot find any denies . What do you mean: protected groups?

        thanks for your quick response!

        • Is the WebServices virtual directory configured properly? Try bypassing Autodiscover by specifying Server (just server name, not the full URL). Have you run the script with ‘verbose’ to see what EWS url is used and checked if that works in IE? Is there a proxy configured and is that bypassed for local addresses (EWS)?

          • yep, we ran the script with ‘verbose’. We also tried the -server switch and checked the link in IE.
            There’s no proxy in place. When testing the EWS url we had to supply credentials and after that an XML page appeared…. Might the login prompt be the problem?

        • No, the prompt is ok – that’s what your credentials or the impersonation should take care of. Can you replace the line:
          Write-Error “Can’t access mailbox information store”
          with:
          Write-Error “Can’t access mailbox information store”
          Write-Error $error[0]
          and see what it throws?

          • it throws: Can’t access mailbox information store Exception calling “Bind” with “2” argument(s
            ): “Exchange Server doesn’t support the requested version.”
            At line:1 char:25
            We tried all versions of EWS api: 1.2 and 2.0

          • the last script was run on backend server 1. When the script is run from backend server 2 the response is:
            Can’t access mailbox information store Write-Error Exception calling “Bind” with “2”
            argument(s): “Exchange Server doesn’t support the requested version.” No Windows PowerShell snap-ins matching the patt
            ern ‘Microsoft.Exchange.Management.PowerShell.E2010′ were found. Check the pattern and then try the command again. Cann
            ot bind parameter ‘FilterScript’. Cannot convert the “” value of type “System.String” to type “System.Management.Automa
            tion.ScriptBlock”. Cannot find path ‘HKLM:\Software\microsoft\ExchangeServer\v14\CentralAdmin’ because it does not exis
            t. Cannot find path ‘HKLM:\Software\microsoft\ExchangeServer\v14\CentralAdmin’ because it does not exist.[0]
            10:53

        • Ah. There’s a line which reads:
          $ExchangeVersion= [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
          (defines minimum version when binding to EWS)

          You seem to be running it against a lower version (I wrote SP1 required in the article). I haven’t tested it against SP1; try replacing Exchange2010_SP2 with Exchange2010_SP1. I’ll make a note of it; see not why it shouldn’t work for 2007 as well now I think of it – I’ll test and update where required.

          Thanks for your feedback!

  8. C:\Users\xaner\Desktop\Fix-MailboxFolders.ps1 : Can’t access mailbox information store (Exception calling “B
    specified object was not found in the store.”)
    At line:1 char:25
    + .\Fix-MailboxFolders.ps1 <<<< -Mailbox ed-joaed -Language sv-SE -FromLanguage en-US -verbose
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Fix-MailboxFolders.ps1

  9. Great script, but I’m getting an error when I try to run it. The mailbox DOES exist
    [PS] C:\software>.\fix-mailboxfolders.ps1 -mailbox camilla.gagnum -language nb-NO -fromlanguage us-EN -impersonation -verbose
    VERBOSE: Loading EWS Managed API DLL
    VERBOSE: Loading C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll
    Processing mailbox camilla.gagnum
    VERBOSE: Set to trust all certificates
    C:\software\Fix-MailboxFolders.ps1 : Specified mailbox camilla.gagnum not found
    At line:1 char:25
    + .\fix-mailboxfolders.ps1 <<<

  10. Michel, I’ve been working with Kim trying to resolve their issue with this. I’ve explicitly assigned our administrative account full permissions on the mailboxes we’re running the script against, I’m running Powershell as administrator, Autodiscover is working correctly, and we’ve tried this with and without impersonation, with and without the -server switch, on the mailbox server where the mailboxes reside, and on the CAS server, and the result is always the same – “specified mailbox not found” – even though a get-mailbox for that same works perfectly. Any suggestions would be very welcome at this point.

      • Yes, we can browse to the EWS using servername\EWS\exchange.asmx. I checked both AD and mailbox permissions, and this account is a member of Exchange Organization Admins with full rights on server/database. The AD rights are a bit more limited, but do have Send As. I see no Deny’s checked. Is there a specific permission that we should be checking?

        Thanks for responding, this one is extremely frustrating. The user would really like to have a better solution than mfcMAPI, but so far, none of us can get the script to find the mailboxes in question.

          • (by the way, I really appreciate your responding to these; thank you so much)
            HiddenFromAddressBook: False
            and for the account I’m using:
            AccessRights: {FullAccess}
            Deny: False
            InheritanceType: All
            IsInherited: False
            IsValid: True
            I explicitly assigned the account I’m using Owner permissions to Top of Information Store. This is what the results look like when I run the command:
            VERBOSE: Loading EWS Managed API DLL
            VERBOSE: Loading C:\program files\microsoft\exchange server\v14\scripts\Microsoft.Exchange.WebServices.dll
            Processing mailbox camilla.gagnum
            VERBOSE: Set to trust all certificates
            C:\program files\microsoft\exchange server\v14\scripts\Fix-MailboxFolders.ps1 : Specified mailbox camilla.gagnum not found
            At line:1 char:25
            + .\fix-mailboxfolders.ps1 <<<< -mailbox camilla.gagnum -language nb-no -fromlanguage us-en -server mailcas02 -impersonation -verbose
            + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
            + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Fix-MailboxFolders.ps1

          • Interesting! It got a little further. Now, the result is “can’t determine source language setting” (even though the mailbox regional settings let us use nb-NO):

            VERBOSE: Loading EWS Managed API DLL
            VERBOSE: Loading C:\program files\microsoft\exchange server\v14\scripts\Microsoft.Exchange.WebServices.dll
            Processing mailbox cgagnum
            VERBOSE: Set to trust all certificates
            VERBOSE: Using cgagnum@contoso.com for impersonation
            VERBOSE: Using Exchange Web Services URL https://mailcas02/EWS/Exchange.asmx
            VERBOSE: From language selected is us-en
            C:\program files\microsoft\exchange server\v14\scripts\Fix-MailboxFolders.ps1 : Can’t determine source language setting
            s
            At line:1 char:25
            + .\fix-mailboxfolders.ps1 <<<< cgagnum -language nb-NO -fromlanguage us-en -server mailcas02 -impersonation -verbose
            + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
            + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Fix-MailboxFolders.ps1

          • (by the way, my response is awaiting moderation…if you can edit it first, please redact the customer’s email address, which I accidentally included in the error message I pasted.)

          • I’ve made a little more progress, but it’s hanging up on the destination language now. I fixed it to be -fromlanguage en-US, as it should be, and now the error is “can’t determine destination language settings.” But it’s definitely processing the mailbox using EWS now. The mailbox regional configuration is definitely set to nb-NO. What am I still missing?

          • The article contains instructions on how to extend the languages (currently only has EN, NL and SE), i.e. you need to add a section where you define all localized names to be used to look up existing folders, e.g.
            “nb-NO”= @{
            “Inbox”=”;
            .. etc.
            };

  11. Hello Michel,
    first of all: thanks for this wonderful script, we also ran into the import problem with duplicate folders. I tested it in my lab (english) and it worked very well, but when I try to run it in the customer environment, I run into problems with Journal and Junkmal-Folders, though i checked thoroughly that the neames were correct. For me it seems that the user (exchange admin with impersonation role) does not have access rights to the folder, as it shows the usual “bind”-error.
    German addition to the script looks like this:

    “de-DE”= @{
    “Inbox”=”Posteingang”;
    “SentItems”=”Gesendete Elemente”;
    “Notes”=”Notizen”;
    “Drafts”=”Entwürfe”;
    “DeletedItems”=”Gelöschte Elemente”;
    “Outbox”=”Postausgang”;
    “Contacts”=”Kontakte”;
    “Calendar”=”Kalender”;
    “Tasks”=”Aufgaben”;
    “JunkEmail”=”Junk-E-Mail”;
    “Journal”=”Journal”;
    “DateFormat”=”dd.MM.yyyy”;
    “TimeFormat”=”HH:mm”;
    };

    The script runs through, copying mails from inbox, contacts, calendar etc. until it gets to JunkEmail (there is no JunkEmail1, so it tells me that there is no folder but shows me the destination folder as “[]” instead of “[Junk-E-Mail]” and stops with a write error (as supposed, as a folder [] does not exist). Any idea what i might wanna check ? Access rights to folders and subfolders look completely normal.
    We are currently working on Exchange 2013 CU1, german language.

    Kind regards,
    Nicole

      • Hello Michel,
        thanks a lot.
        Some update here: leaving Journal and Junk-E-Mail out (and eliminating it from the array of well known folders) makes the script work (it is OK, because these folders are not doubled), but we stumbled into another problem: big mailboxes (talking about 10 GB+) with a lot of subfolders and mails in some folders start copying but after about 10000 items, the script stops with an error about missing IDs (will send you my transcript of that session later, have to get access to the servers). Seems to me that the script – while collecting the item IDs – stumbles because of too many items. The big problem then is: you can restart the script, it goes on copying but when still around 3000-4000 mails should be moved, it just moves on to deleting the folder (like Posteingang1/Inbox1) and a lot of mails are missing (compared the folders before and after move). We tried altering the batchsize down to 100, but still same problem. Could this be a problem of Exchange throttling, as the script uses EWS ? Exchange 2013 is known to be using a harsh throttling policy out of the box and we didn’t change anything there.

        • It is recommended to disable throttling (EWS) for the account you use to impersonate mailboxes. I just did some testing with a mailbox configured to German (back and forth) and without the issue mentioned, so I can only guess it’s a permission thing. Revision of the deletion process is put on the to do list.

          • Hello Michel,
            thanks for testing again. Permission issue sounds weird with impersonation and full access. Are Junk-E-Mail- and Journal-Folders (without any policy or journaling in place) handled differently ? We tried using full access and impersonation, same result.To disable throttling for this user sounds like a plan (already thought about it), but as the customer does not have any test environment, we are always working in productive environment and this might result in customer impact if things go bad.
            Anyway, for further migration they will nail all mailboxes down to fixed language and regional settings to avoid future problems with imported .pst files. We were just trying to fix the first problem.

            You are doing a really good work here, so thank you again for solving problems like these. Keep on the good work.
            A merry christmas and a happy new year to you and your beloved ones.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s