Cross-Forest Mailbox Move

Note: This is part 1; part 2 can be found here.

Currently I’m working with Johan Veldhuis on a project where we’ll be moving mailboxes from Exchange 2003 to Exchange 2010. Accounts and mailboxes will be migrated from forest to forest and to make things more interesting, the account migration is isolated from the mailbox migration. In other words, we start with a single forest after which we’ll create a account/resource forest setup from where we’ll consolidate things again. The account/resource forest setup is not completely by the book as the accounts in the resource forest won’t be disabled. This is because ActiveSync devices will be migrated at a later stage; this means they can use their existing authentication information.

Note: In the remainder of this article the following variables are used:

  • olddomain.nl is the source forest with Exchange 2003;
  • newdomain.com is the new forest with Exchange 2010;
  • Jan Test is our to be migrated test user with User ID jtest;
  • source.nl is an e-mail domain for which Exchange 2003 is authoritative;
  • target.com is an e-mail domain for which Exchange 2010 is authoritative.

The starting point of this blog is the situation where both forests olddomain.nl and newdomain.com are up and running. There is a trust between olddomain.nl and newdomain.com, ADMT and the Password Export Server are configured and running properly. This means we can successfully migrate user accounts from olddomain.nl to the new forest newdomain.com; the user’s mailbox will remain on Exchange 2003 in olddomain.nl.

Here is where the first problem was encountered. The Active Directory schemas mismatch, so ADMT will only migrate a basic set of attributes. This means that although newdomain.com has Exchange attributes, these won’t be migrated. Also, to be able to use the mailbox properly from their newdomain.com account, we need to add mailbox permissions for the newdomain.com account. For this purpose we created a post-migration script which adds Full Mailbox Access and External Account permissions to the mailbox for the newdomain.com account. After that we have reached an intermediary state, with accounts in newdomain.com and resources (mailboxes) in olddomain.nl; Users will log on using an account from the newdomain.com account forest and access their mailbox in the olddomain.nl resource forest.

The next phase will be migrating Exchange 2003 mailboxes from olddomain.nl to Exchange 2010 mailboxes in newdomain.com. Microsoft provides a nice TechNet article (633491) on how to prepare mailboxes for cross-forest mailbox move requests. The article also links to a (sample) PowerShell script, part of ILM 2007 SP1, located here. The script, Prepare-MoveRequest.ps1, should prepare the target forest for mailbox move request. Unfortunately, we couldn’t get it working properly.

First, the script complained it couldn’t find the specified Identity in the target forest. This was odd; the account is present, it was created by ADMT. Diving into the code it looks for the identity in various attributes:

filterParm = “(& (objectClass=user)” +
”   ( (| (mailnickname=$escapedIdentity)” +
”        (cn=$escapedIdentity)” +
”        (proxyAddresses=SMTP:$escapedIdentity)” +
”        (proxyAddresses=smtp:$escapedIdentity)” +
”        (proxyAddresses=X500:$escapedIdentity)” +
”        (proxyAddresses=x500:$escapedIdentity)” +
”        (objectGUID=$escapedIdentity)” +
”        (displayname=$escapedIdentity))))”

$srcObject = findADObject $srcdc $filterParm

No matter what we tried, no match. Odd. Also, the script contains code which creates objects when it determines it needs to and there’s no switch to turn this behaviour off. That switch would be nice, because the last thing we want is to end up with hundreds of mail-enabled user objects when there are already user account objects present. Since we had little time to debug and fix the script, we decided to prepare the target account in newdomain.com ourselves. I took a script from one of my earlier projects (VB), which I created for similar merger / split scenarios in the past, and checked it against the information in the TechNet article mentioned earlier. In the end we had a script which transforms users into mail-enabled users as follows:

  1. Copy the following atributes from the account in olddomain.nl to the account in newdomain.com: mail, mailNickname, msExchMailboxGuid, proxyAddresses;
  2. Adds the LegacyExchangeDN attribute from olddomain.nl account as an X500 address to the account in newdomain.com;
  3. Sets msExchRecipientDisplayType to -2147483642;
  4. Sets msExchRecipientTypeDetails to 128;
  5. Sets targetAddress in newdomain.com to the value of mail attribute in the olddomain.nl;
  6. Adds a constructed e-mail address (e-mail account olddomain.nl + @ + new e-mail domain, e.g. j.test@target.com) to the proxyAddresses in newdomain.com;
  7. Sets legacyExchangeDN on the new account in newdomain.com to “/o=<ORG>/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=<CN>”, where <CN> is the cn of the account.

This is more or less the same as the Prepare-MoveRequest.ps1 should do, but this one worked like expected. Note that the last three actions are important for getting the move request to work. The targetAddress should be set properly, because new-moveRequest will set the target Address of the olddomain.nl account to the primary SMTP address of the newdomain.com account. LegacyExchangeDN and the proxyAddress should be added because new-moveRequest uses these for lookup and matching (I assume, because we will also will specify the identity itself). The proxyAddress e-mail address for which Exchange 2010 in newdomain.com is authoritative will be promoted to primary SMTP e-mail address.

Now, after preparing the newdomain.com account and transforming it into a mail-enabled user, we can use new-moveRequest to transfer the mailbox from Exchange 2003 to Exchange 2010:

$cred = get-credential
new-moverequest -identity <userid> -RemoteLegacy -TargetDatabase “<Mailbox Database Name>” -RemoteGlobalCatalog dc.olddomain.nl -RemoteCredential $cred -TargetDeliveryDomain <target.com>

An explanation:

  • We need to specify RemoteLegacy because the source forest isn’t Exchange 2010;
  • TargetDatabase identifies the target database;
  • Using RemoteGlobalCatalog we specify a GC from the remote forest;
  • With RemoteCredential, in combination with get-credential, is used to prompt for credentials and use them in the cmdlet;
  • TargetDeliveryDomain specifies the external e-mail domain that is used to set the targetAddress in the source forest, so normally this should be an e-mail domain for which the source forest isn’t and the target forest is authoritative.

Presto! This is what the objects look like after performing the new-moverequest:

olddomain.nl
cn=Jan Test
mail=jtest@target.com
legacyExchangeDN=/o=<ORG>/ou=First Administrative Group/cn=Recipients/cn=jtest88083336
mailNickname=jtest
proxyAddresses=X500:/o=<ORG>/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Jan Test;
smtp:J.Test@source.nl;
SMTP:jtest@target.com;
X400:c=NL;a= ;p=<org>;o=Exchange;s=Test;g=Jan;;
targetAddress=SMTP:jtest@target.com

newdomain.com
cn=Jan Test
mail=jtest@target.com
legacyExchangeDN=/o=<ORG>/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Jan Test;
mailNickname=jtest
proxyAddresses=smtp:jtest@newdomain.com
;SMTP:jtest@target.com;X400:C=NL;A= ;P=<org>;O=Exchange;S=Test;G=Jan
;smtp:J.Test@source.nl
;smtp:J.Test@target,com
;X500:/o=<ORG>/ou=First Administrative Group/cn=Recipients/cn=jtest88083336
targetAddress=SMTP:jtest@target.com

Notice that new-moveRequest adds a random number “88083336” to legacyExchangeDN in olddomain.nl and to X500 in newdomain.com. This is to promote the newdomain.com account for legacyExchangeDN usage and enables mailflow from the old olddomain.nl to newdomain.com when replying on old e-mail.

Update: Also worth noting is we an additional challenge because the source domain name equaled their e-mail namespace, which they want to keep using during and after the migration. Therefor we had to introduce a bogus local namespace for which the Exchange 2003 becomes authoritative and 2010 is not. We can use recipient policies to stamp this address to mail-enabled objects and by creating a connector from Exchange 2010 to Exchange 2003 using this namespace, we can route e-mail from migrated users to non-migrated users.

I hope this information is useful. For more information on new-moveRequest for cross-forest moves, check here. When you have questions, post them in the comments below or send me an e-mail.

42 thoughts on “Cross-Forest Mailbox Move

  1. Like you I was unable to get the Prepare-MoveRequest.ps1script to work. It creates a second account in our target domain. We already have an account that was migrated using ADMT. Would you be able to provide me with the script you used to add the exchange attributes from the source domain? Thanks!

  2. Hey mdrooij,

    same here too, it keeps creating two acounts, i still have the same issue and i really could use ur script, i hope u published it really soon 😉

    thanks in advance.

  3. hi, i also would like to see the script of yours, it seems to really do what MS script is supposed to do. Do you have an estimate, when will it be published?

  4. My scripts works ok and i prepared mailbove to move, after ran new-moverequest, i got below error:
    PS C:\Users\administrator.SDCDOM\Desktop\2> new-moverequest -identity user9 -remotelegacy -targetdatabase “Mailbox Store
    ” -remoteglobalcatalog syd-ex01.traveledge.local -remotecredential $cred -Targetdeliverydomain sdcdom.local
    New-MoveRequest : Service ‘net.tcp://sdc-ex01.sdcdom.local/Microsoft.Exchange.MailboxReplicationService’ encountered an
    exception. Error: MapiExceptionNetworkError: Unable to make connection to the server. (hr=0x80004005, ec=2423)
    Diagnostic context:
    ……
    Lid: 12952 dwParam: 0x6BA Msg: EEInfo: prm[3]: Long val: 1722
    Lid: 16280 dwParam: 0x6BA Msg: EEInfo: ComputerName: n/a
    Lid: 8600 dwParam: 0x6BA Msg: EEInfo: ProcessID: 2532
    Lid: 12696 dwParam: 0x6BA Msg: EEInfo: Generation Time: 2010-02-08 11:55:30:859
    Lid: 10648 dwParam: 0x6BA Msg: EEInfo: Generating component: 8
    Lid: 14744 dwParam: 0x6BA Msg: EEInfo: Status: 1722
    Lid: 9624 dwParam: 0x6BA Msg: EEInfo: Detection location: 1442
    Lid: 13720 dwParam: 0x6BA Msg: EEInfo: Flags: 0
    Lid: 11672 dwParam: 0x6BA Msg: EEInfo: NumberOfParameters: 1
    Lid: 8856 dwParam: 0x6BA Msg: EEInfo: prm[0]: Unicode string: SYD-EX01
    Lid: 45169 StoreEc: 0x977
    Lid: 52465 StoreEc: 0x977
    Lid: 60065
    Lid: 33777 StoreEc: 0x977
    Lid: 59805
    Lid: 52209 StoreEc: 0x977
    Lid: 19778
    Lid: 27970 StoreEc: 0x977
    Lid: 17730
    Lid: 25922 StoreEc: 0x977
    At line:1 char:16
    + new-moverequest <<<< -identity user9 -remotelegacy -targetdatabase "Mailbox Store" -remoteglobalcatalog syd-ex01.tra
    veledge.local -remotecredential $cred -Targetdeliverydomain sdcdom.local
    + CategoryInfo : NotSpecified: (0:Int32) [New-MoveRequest], MailboxReplicationTransientException
    + FullyQualifiedErrorId : 6FAA02B8,Microsoft.Exchange.Management.RecipientTasks.NewMoveRequest

  5. @Reza: I think the request is failing because it cannot find the source mbx server using NetBIOS (SYD-EX01). Try setting the DNS suffixes so you can ping using shortname. Let me know if this helps.

  6. @manu: No, for the simple reason that there are certain costs involved with ILM. Also, the situation is only temporary so it would be of limited use to implement a meta-directory product like ILM.

  7. Thank you . we have a situaltion where we need to merge 2 domains into one source domains running exchange 2003 and exchange 2007 merging into 2010.

    I really look forward to your script 🙂

    Thank you for article

    Regards
    Manu

  8. Pingback: Cross-Forest Mailbox Move (2) « EighTwOne (821)

  9. Dear Mdrooji,

    When we execute this script error is shown as below

    Script c:\SCRIPT\exchatri\CrossForestPrep.vbs
    Line : 311
    Char : 2
    Error : Unspecified error
    Code : 80004005
    Source : Active Directory

    Below is the output.log

    [09:00] Start
    [09:00] Reading names from users.txt
    [09:01] user5: Syncing Exchange Attributes from user5
    [09:01] Setting mail to user5@sourcedomain.com #8
    [09:01] Setting mailNickname to user5 #8
    [09:01] Setting msExchMailboxGuid to (B8CD22F9B38BF54F81E625A2746D7C3A) #8209
    [09:01] Setting targetaddress to user5@sourcedomain.com #8
    [09:01] Setting proxyAddresses to SMTP:user5@sourcedomain.com #8

    It fails in the setting proxyattribute

    Regards
    Manu

    • That error 80004005 could be caused by setting proxyAddresses (a multi-value attribute) to a string instead of an array of strings (with 1 or more elements). Which is odd, because I got that covered (hence the boolMulti parameter which is set to True for proxyAddresses .. don’t know if you can read code).
      I’ll have a look into it later today (work work work).

      • Dear Mdrooji,

        I am not a programmer,hence i dont understand the code :-).

        Thanks for the reply. I Will wait for your findings

        Regards
        Manu

    • I’ve made modifications which should deal with proxyAddresses containing a single value (copyAttribute & addAttribute). Feedback welcome.

  10. Hi
    I tried your script and it worked fine, great job!
    But then when i tries to run new-moverequest i got this error message:

    Service ‘net.tcp://xxx.xxx.xxx.se/Microsoft.Exchange.MailboxReplicationService’ encountered an exceptio
    or: MapiExceptionNetworkError: Unable to make connection to the server. (hr=0x80004005, ec=2423)
    Diagnostic context:
    ……
    Lid: 11672 dwParam: 0x6BA Msg: EEInfo: NumberOfParameters: 0
    Lid: 16280 dwParam: 0x6BA Msg: EEInfo: ComputerName: n/a
    Lid: 8600 dwParam: 0x6BA Msg: EEInfo: ProcessID: 6612
    Lid: 12696 dwParam: 0x6BA Msg: EEInfo: Generation Time: 2010-02-24 06:36:50:842
    Lid: 10648 dwParam: 0x6BA Msg: EEInfo: Generating component: 18
    Lid: 14744 dwParam: 0x6BA Msg: EEInfo: Status: 11001
    Lid: 9624 dwParam: 0x6BA Msg: EEInfo: Detection location: 320
    Lid: 13720 dwParam: 0x6BA Msg: EEInfo: Flags: 0
    Lid: 11672 dwParam: 0x6BA Msg: EEInfo: NumberOfParameters: 1
    Lid: 8856 dwParam: 0x6BA Msg: EEInfo: prm[0]: Unicode string: DC01
    Lid: 45169 StoreEc: 0x977
    Lid: 52465 StoreEc: 0x977
    Lid: 60065
    Lid: 33777 StoreEc: 0x977
    Lid: 59805
    Lid: 52209 StoreEc: 0x977
    Lid: 19778
    Lid: 27970 StoreEc: 0x977
    Lid: 17730
    Lid: 25922 StoreEc: 0x977
    + CategoryInfo : NotSpecified: (0:Int32) [New-MoveRequest], MailboxReplicationTransientException
    + FullyQualifiedErrorId : 84E8C81C,Microsoft.Exchange.Management.RecipientTasks.NewMoveRequest

  11. Pingback: Kerberos Max Token Size « EighTwOne (821)

  12. it is a great tool, do you still have a copy of the post-migration script which adds Full Mailbox Access and External Account permissions to the mailbox?

    thanks a lot

    • Yes, but that’s nothing special really. Add the EX_FULL_MAILBOX_ACCESS and EX_ASSOCIATED_EXTERNAL permissions to the ACL on the mailbox object. When already present, remove any existing Associated External Account permissions since its exclusive (i.e. can’t halve multiple associated external accounts).

      It’s not ready for publishing, but when requested I’ll have a look into it.

  13. I apologize for being novice, but I’m in this exact situation but am required to have the source mail domain be the same as the target mail domain. As such, I’m a bit unclear as to how to run the tool to get around this (proxyAddress?). I assume this will probably be a phased process…one that eludes me at the moment.

    Any help is welcome.

    • proxyAddress contains (alternative) e-mail addresses for a given mailbox user, mail-enabled user, distribution group, etc.. One of these is the primary address, which is used to send mail to foreign systems. Also important is the targetAddress which is the actual e-mail address; this is the address mail will be delivered to.

      On global steps and co-existence, I suggest you read this (2 part!) article by Exchange fellow Jaap on migrating from Exchange 2003 to Exchange 2010:
      http://www.simple-talk.com/sysadmin/exchange/upgrade-exchange-2003-to-exchange-2010/

  14. Pingback: MapiExceptionNotFound: Unable to delete mailbox « EighTwOne (821)

  15. Hello MDrooij,
    I would like to get a copy of the script you created to replace the preparemoverequest.ps1. Do you think you can make that available to me please? I am running into issues with the preparemoverequest.ps1- and if yours can help-it would be most valuable and appreciated. Let me know and thanks-Nice Article BTW

  16. Hello MDrooij,
    this article has help me to solve a critical issue.
    The script working very fine.
    I got only an error when you have a disconnected user mailbox that was associated to the same account in the same Mailbox Store, but when I restarted the information store the user has worked fine.

    Thank you very much,
    Nunzio

  17. Pingback: Some 2010 Statistics « EighTwOne (821)

  18. hi,

    we are presently doing a exchange 2003 to exchange 2010 sp1 migration and have used successfully used the preparemove-request.ps1 and the new-moverequest.ps1 to move the mailboxes from one forest to another but need to have the email account created and delivering mail messages before we use the new-moverequest.ps1 script. is there any way new-moverequest can move messages into an existing mailbox ?

    thanks in advance for any help

    • I think you meant the New-MoveRequest cmdlet instead of “new-moverequest.ps1”
      Depends on the reason for wanting that.
      If you want to provision the (new) mailbox first, and then really flick the switch (and mail-flow, after copying the latest e-mail as well), you could use New-MoveRequest’s SuspendWhenReadyToComplete option to do the initial provisioning and then use Resume-MoveRequest to complete the transfer. It is advisable to complete the transfer ASAP, because latest changes need to be copied and the suspended mailbox is subject to the mailbox retention period.

      If you start with an initial empty – but working – mailbox which you’re filling with data later on, you are looking more or less at a dial-tone recovery but with a twist. I haven’t seen this being used in this context. Note that New-MoveRequest does lots of stuff on the attributes (on both sides so to speak), changes which you have to perform yourself. I’d also doubt if this would be wise from a support stance.

  19. sorry you are correct, i used the New-MoveRequest to try to move the mailbox over. The problem we have is that we are going cross forest and going from exchange 2003 so when i try to use the SuspendWhenReadyToComplete I get error message saying it can only be used with online mailbox move so i am still stuck with an mailbox’s that are only 1/2 moved if there is some kind of server error while migrating with no way to restore the migrated users besides using the restore from backup.

    PS thanks for the all your help.

  20. Pingback: 2011, a short Retrospective « EighTwOne (821)

  21. How can we have coexistence for Distribution groups in cross forest 2003 to 2010
    Ex: user A is part of Dl in source forest now users A is moved to B forest .if users A want to send email to DL in source forest how it can be achieved

    • Until you migrate that DL as well, the DL will work and – depending on strategy – mail to migrated mailboxes will be send to the remaining mail-enabled contact in source which will forward it to the mailbox in target. When you migrate the DL and there are still mailboxes in source, options depend on things like whether you have mail-enabled contacts (GAL Sync?) in target.

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