Note: This is part 2; part 1 can be found here.
After the post on experiences regarding Cross-Forest Mailbox Move, the problems with the “sample” Powershell script and the script created in good ol’ VB, I got lots of requests to publish the script. After thinking this over, I made it ready for publishing. That means stripping excessive code and changing domain names etc. to a more descriptive labels.
The script does require some explanation:
- The script uses 1 input file (users.txt) and produces 1 output file (output.log) (included below);
- The reason for sending output to screen (optional) as well as file is to be able to check it properly (e.g. using notepad) when running it for a set of users;
- users.txt contains a single line with the source and target account names. This is the same file we used for ADMT imput. Reason for having a source as well as a new account name is that in the migration process account are renamed. ADMT can do this for you, but the script will need both the old and the new name name to connect to the objects and copy/set several attributes;
- It’s VB, had been kept simple and didn’t went through a code beautifier. That means no full function headers, input/output descriptions or extensive error handling;
- Modify the constants using information from your environment, e.g. source/target domain, servers, LegacyExchangeDN etc.
- The script uses fixed servers. This is to make sure we’re talking to the same server(s) as ADMT and to prevent replication issues because of lag;
- Use the script at your own risk. I cannot accept any responsibility for consequences when using this in your production environment;
- Use it in a lab environment first; test, test, test!
Users.txt
SourceName,TargetSAM jtest,jtest
CrossForestMovePrep.vbs
'*-------------------------------------------------------------------------------- '* Name : PrepareForestMove '* Created By : Michel de Rooij '* E-mail : michel@eightwone.com '* Date : 20100217 '* Version : 0.22 '*-------------------------------------------------------------------------------- '* Changes: '* 0.21 Initial version '* 0.22 Made changes to address single-value proxyAddress attributes '*-------------------------------------------------------------------------------- Option Explicit Const strUserfile = "users.txt" Const strOutputFileName = "output.log" Const DEBUGOUTPUT = 1 Const conSourceServer = "dc.olddomain.nl" Const conSourceDomain = "olddomain.nl" Const contargetServer = "dc.newdomain.com" Const conTargetDomain = "newdomain.com" Const conLegacyExchangeDN = "/o=NEWEXORG/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=" Const conTargetEmailDomain = "target.com" ' AD putex cmds Const ADS_PROPERTY_CLEAR = 1 Const ADS_PROPERTY_UPDATE = 2 Const ADS_PROPERTY_APPEND = 3 Const ADS_PROPERTY_DELETE = 4 ' FileSystem Const ForWriting =2 Const ForReading =1 '********************************************************* ' MAIN '********************************************************* Dim oFSO, strFile, objFile, hOutputFileHandle, bProcessLine, strLine Dim arrUsers, strUser, strCmd, strNewUser Set oFSO = CreateObject("Scripting.FileSystemObject") Set hOutputFileHandle= oFSO.OpenTextFile( strOutputFileName, ForWriting, True) debug("Start") strFile= strUserFile if NOT oFSO.fileExists( strFile) then die( "Input file "& strFile& " does not exist") end if debug("Reading names from "& strFile) set objFile= oFSO.OpenTextFile( strFile, ForReading, True) while not objFile.atEndOfStream bProcessLine= True strLine= objFile.readLine if isEmpty(strLine) then bProcessLine= False Else If left(strLine, 1)= ";" Then bProcessLine= False Else If inStr( strLine, ",") > 0 Then ' Line OK arrUsers= split( strLine, ",") strUser= arrUsers(0) strNewUser= arrUsers(1) If strUser= "SourceName" Then ' Input file header, skip bProcessLine= False End If Else bProcessLine= False debug("** INFO: Skipping line "& strLine) End If End If End If If bProcessLine Then debug(strNewUser& ": Syncing Exchange Attributes from "& struser) syncAttributes strUser, conSourceServer, conSourceDomain, strNewUser, conTargetServer, conTargetDomain End If Wend debug("Finished") objFile.Close hOutputFileHandle.Close set hOutputFileHandle= Nothing set objFile= Nothing set oFSO= Nothing wscript.quit(0) '******************************************************************* ' Purpose: output to screen when DEBUGOUTPUT is 1, always to file '******************************************************************* Function debug(strMsg) hOutputFileHandle.write ("["& FormatDateTime(now(),4) & "] "& strMsg & chr(13)& chr(10)) if DEBUGOUTPUT=1 then wscript.echo strMsg end if End Function '********************************************************* ' Purpose: terminate with message '********************************************************* Function die(strMsg) wscript.echo strMsg wscript.quit (1) End Function '********************************************************* ' displayString ' Returns string from varType item/elements '********************************************************* Function displayString( varObj) Dim tmp, item tmp= "" select case VarType( varObj) Case vbEmpty tmp= "(Empty)" Case vbNull tmp= "(Null)" Case vbInteger, vbLong, vbSingle, vbDouble, vbByte, vbDecimal, vbCurrency, vbDate, vbBoolean tmp= cStr( varObj) Case vbString tmp= varObj Case vbObject tmp= "(Object)" Case vbvariant tmp= "(Variant)" Case 8209 tmp= "("& OctetToHexStr( varObj)& ")" Case vbArray, 8204 For each item in varObj If tmp="" Then tmp= tmp+ item Else tmp= tmp+ ", "+item End If Next tmp= "["& tmp& "]" Case Else End Select displaystring= tmp& " #"& varType( varObj) End Function '********************************************************* ' OctetToHexStr ' Convert OctetString (byte array) to Hex string. '********************************************************* Function OctetToHexStr (arrbytOctet) Dim k OctetToHexStr = "" For k = 1 To Lenb (arrbytOctet) OctetToHexStr = OctetToHexStr & Right("0" & Hex(Ascb(Midb(arrbytOctet, k, 1))), 2) Next End Function '********************************************************* ' syncAttributes ' migrates attributes from source to target '********************************************************* Function syncAttributes (strUser, SourceServer, SourceDomain, strNewUser, TargetServer, TargetDomain) dim strDNSource, strDNTarget, objSource, objTarget, n, strMail strDNSource= getDN( struser, SourceServer, SourceDomain, "") strDNTarget= getDN( strNewuser, TargetServer, TargetDomain, "") If strDNSource<>"" AND strDNTarget <> "" Then set objSource= getObject( "LDAP://"& SourceServer& "/"& strDNSource) set objTarget= getObject( "LDAP://"& TargetServer& "/"& strDNTarget) copyAttribute "mail", objSource, objTarget, False copyAttribute "mailNickname", objSource, objTarget, False copyAttribute "msExchMailboxGuid", objSource, objTarget, False setAttribute "targetaddress", objSource.get( "mail"), objTarget copyAttribute "proxyAddresses", objSource, objTarget, True addAttribute "proxyAddresses", "X500:"& objSource.get( "LegacyExchangeDN"), objTarget strMail= objSource.get( "mail") n= instr( strMail, "@") debug( strMail) addAttribute "proxyAddresses", "smtp:"& left( strMail, n-1)& "@"& conTargetEMailDomain, objTarget setAttribute "msExchRecipientDisplayType", -2147483642, objTarget setAttribute "msExchRecipientTypeDetails", 128, objTarget setAttribute "legacyExchangeDN", conLegacyExchangeDN& objSource.get("cn"), objTarget objTarget.setInfo Else debug("*** ERR: Cannot retrieve DNs for Source or Target") syncAttributes= False End If End Function '********************************************************* ' getDN ' Retrieves the DN for a user object '********************************************************* Function getDN( struser, strServer, strDomain, strOU) dim objConn, objCmd, strQuery, objRS, strAttr, strRDNLDAP, strDNSLDAP strRDNLDAP= RDN2LDAPPATH( strOU) strDNSLDAP= DNSDomain2LDAPPath( strDomain) strAttr= "distinguishedName" set objConn= createObject( "ADODB.Connection") set objCmd= createObject( "ADODB.Command") objConn.Provider= "ADsDSOObject" objConn.Open "ADs provider" objCmd.ActiveConnection= objConn strQuery= "<LDAP://"& strServer If strServer <> "" Then strQuery= strQuery& "/" End If strQuery= strQuery& strRDNLDAP If strOU <> "" Then strQuery= strQuery& "," End If strQuery= strQuery& strDNSLDAP& ">" strQuery= strQuery+ ";(&(objectCategory=person)(objectClass=user)(SAMAccountName="& strUser&"));"& strAttr& ";subtree" objCmd.CommandText = strQuery on error resume next set objRS= objCmd.execute if err.number <> 0 Then debug( "*** ERR: Error "& err.number& " executing ["& strQuery& "]") getDN= "" Else on error goto 0 Select Case objRS.recordCount Case 0 debug( "*** ERR: User object "& struser& " not found") getDN= "" Case 1 getDN= objRS.Fields( strAttr) 'debug( getDN) Case Else debug("*** ERR: Ambigious user object "& struser) getDN= "" End Select End If set objRS= Nothing set objCmd= Nothing set objConn= Nothing End Function '********************************************************* ' DNSDomain2LDAPPath( str) ' Makes an LDAP notation for a DNS domain name ' e.g. corp.local => DC=corp,DC=local '********************************************************* Function DNSDomain2LDAPPath( str) Dim tmp1, tmp2, tmp3 tmp1= split( str, ".") tmp2= "" For each tmp3 in tmp1 If tmp2<>"" then tmp2= tmp2& "," End If tmp2= tmp2& "dc="& tmp3 Next DNSDomain2LDAPPath= tmp2 End Function '********************************************************* ' RDN2LDAPPath( str) ' Makes an LDAP notation for a Relative Distinguished Name ' e.g. Domain Accounts/3rd party => OU=3rd party,OU=Domain Accounts '********************************************************* Function RDN2LDAPPath( str) Dim tmp1, tmp2, tmp3 tmp1= split( str, "/") tmp2= "" For each tmp3 in tmp1 If tmp2<>"" then tmp2= ","& tmp2 End If tmp2= "ou="& tmp3& tmp2 Next RDN2LDAPPath= tmp2 End Function '********************************************************* ' copyAttribute ' Copies attribute(s) to target '********************************************************* Function copyAttribute( strAttribute, objSource, objTarget, boolMulti) dim boolUpdate, varItem If isEmpty( objSource.get( strAttribute)) Then debug( strAttribute& " not set, clearing") objTarget.PutEx ADS_PROPERTY_CLEAR, strAttribute, 0 Else varItem= objSource.get( strAttribute) If boolMulti Then if isArray( varItem) Then debug( "Setting "& strAttribute& " to multi-value "& displayString( varItem)) objTarget.PutEx ADS_PROPERTY_UPDATE, strAttribute, varItem Else debug( "Setting "& strAttribute& " to single-value "& displayString( varItem)) objTarget.PutEx ADS_PROPERTY_UPDATE, strAttribute, array( varItem) End If Else debug( "Setting "& strAttribute& " to "& displayString( varItem)) objTarget.Put strAttribute, varItem End If End If on error goto 0 objTarget.SetInfo End Function '********************************************************* ' setAttribute ' Sets attribute to target '********************************************************* Function setAttribute( strAttribute, strValue, objTarget) debug("Setting "& strAttribute& " to "& displayString(strValue)) objTarget.Put strAttribute, strValue objTarget.SetInfo End Function '********************************************************* ' addAttribute ' Adds attribute to target '********************************************************* Function addAttribute( strAttribute, varAttribute, objTarget) dim boolUpdate, tmp boolUpdate= True If isEmpty( varAttribute) Then ' not set, skipping Else 'on error resume next If isEmpty( objTarget.get( strAttribute)) Then boolUpdate= True Else If isArray( objTarget.get( strAttribute)) Then For each tmp in objTarget.get( strAttribute) If tmp = varAttribute Then boolUpdate= False End If Next Else boolUpdate= varAttribute= objTarget.get( strAttribute) End If End If on error goto 0 If boolUpdate Then debug("Adding "& varAttribute& " to "& strAttribute) objTarget.PutEx ADS_PROPERTY_APPEND, strAttribute, array( varAttribute) Else debug( varAttribute& " already in "& strAttribute) End If End If objTarget.SetInfo End Function
Output.log (sample)
[16:24] Start [16:24] Reading names from users.txt [16:24] jtest: Syncing Exchange Attributes from jtest [16:24] Setting mail to jtest@source.nl #8 [16:24] Setting mailNickname to jtest #8 [16:24] Setting msExchMailboxGuid to (70C2360FB0330346A925172CA0473B9F) #8209 [16:24] Setting targetaddress to jtest@target.com #8 [16:24] Setting proxyAddresses to [SMTP:jtest@source.nl, X400:c=US;a= ;p=DemoOrg;o=Exchange;s=Old;g=Mr;] #8204 [16:24] Adding X500:/o=DemoOrg/ou=First Administrative Group/cn=Recipients/cn=jtest to proxyAddresses [16:24] jtest@source.nl.nl [16:24] Adding smtp:jtest@target.com to proxyAddresses [16:24] Setting msExchRecipientDisplayType to -2147483642 #3 [16:24] Setting msExchRecipientTypeDetails to 128 #2 [16:24] Setting legacyExchangeDN to /o=NEWORG/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Mr Old #8 [16:24] Finished
Pingback: Cross-Forest Mailbox Move « EighTwOne (821)
Now i can copy user and sync attribute with script and move a test mailbox (Smal Size) successfully.
I do have problem moving Big mailboxes in production, i get bellow message
Error: An Error occured while updating a user object after the move operation -> The operation coudn’t be performed because object ‘sdcdom.local/User/Alison Durnford’ coudn’t be found on ‘sdc-ad01.sdcdom.local’
my target domain is sdcdom.local
my target Active Directory is sdc-ex01.sdcdom.local
and my user is Alison Durnford
Any help is much appreciated.
Regards,
LikeLike
The script doesn’t care about the size.
What is the exact message?
LikeLike
fantastic, I shall test this and let you know how i get on. This will save my hours if it works. cheers.
LikeLike
Reza check if a proxy address has been set and what is the primary SMTP set ?
LikeLike
What an excellent script. Thanks very much.
LikeLike
Welcome. Spread the word 🙂
LikeLike
Running this script just works 🙂 – however there are differences whether the ADMT has taken all attributes or I just excluded the Exchange and takes msExchMailboxGUID, msExchArchiveGUID, msExchArchiveName. It works when just having the three attributes above, but when taking all attributes it says” Database is mandatory on usermailbox” and “You must use the remotetargetdatabase for remote push move request” – any experience on this ?
LikeLike
@PeterD: If you read the first post, you probably noticed we had schema mismatches causing ADMT to only sync a basic set of attributes. This script is to turn the target user object into a mail-enabled user with the proper attributes (linking it to the mailbox in the source forest), after which new-moverequest can do its job. With identical schemas, sync of all attributes will probably cause the target object to become a mailbox user because you’re syncing the attribute identifying the object type (msExchRecipientDisplayType) as well.
Regarding the “Database is mandatory on usermailbox”, is the mailbox you are trying to move corrupt and it the mailbox specified in AD (with the account) still valid (i.e. no decommissioned mailbox server)? Can you check with get-mailbox | fl
Regarding the “You must use the remotetargetdatabase for remote push move request”, is the target user already associated with a mailbox (or has one)? The mailbox pull request will only work on target users without a mailbox.
(See also part 1 and the related technet article http://technet.microsoft.com/en-us/library/ee633491.aspx)
LikeLike
Brilliant! Thank you for taking the time to post it what MS should have in there migration toolkit.
LikeLike
You’re welcome.
LikeLike
Could I ask how you got the users in forest A to browse their mailboxes in forest B. At the moment I’m going down the outlook Anywhere path. Any thoughts on this ? Thanks
LikeLike
In our case the resource/account forest is temporary so we’re using a custom .prf which can reconfigure (default) outlook profiles. We’re migrating from Exchange 2003 so autodiscover is not an option. In our case external users connect through VPN so haven’t examined RPC/HTTPS to Anywhere. But if that was the case, properly configure autodiscover and outlookanywhere in Exchange 2010. See http://technet.microsoft.com/en-us/magazine/ff381470.aspx
LikeLike
Hi
This script is exactly what i need to complete my migration and as everybody else i too couldnt get the MS ps1 script to work. So thank you for your great work:)
But as PeterD i also get the “you must use the…” error:
You must use the RemoteTargetDatabase parameter for remote push move requests.
+ CategoryInfo : InvalidArgument: (admttest12:MailboxOrMailUserIdParameter) [New-MoveRequest
], RecipientTaskException
+ FullyQualifiedErrorId : 13BF919D,Microsoft.Exchange.Management.RecipientTasks.NewMoveRequest
LikeLike
Did you read my response to PeterD’s comment?
LikeLike
Yes i did, but i properly didnt understand it correctly:)
You are properly right about syncronising all attributes are causing exchange to believe that the user allready have an mailbox. Even though the user object have just been created.
Doing what Peter also did, only include the msExchMailboxGUID attribute in ADMT, helped me to complete the new-moverequest.
Again thank you for your time and for the great work:)
A bit off topic: Have any of you an idea about what transfering speeds i cant expect with the moverequest? I just comleted moving a very small mailbox (10-20MB) and it took about 3 minutes. And have any of you experienced that a moverequest fails?
LikeLike
Welcome. The script follows TechNet article ee633491 and copies/configures the mandatory attributes (see part 1), msExchMailboxGuid included. After that, the target object is a mail-enabled user, a valid target for move requests.
Speed depends, lots of variables here (source and target hardware, network speed). Options are things like disabling anti-virus software (@target) and enabling circular logging during bulk transfers but this depends on your situation and requirements (i.e. don’t do this when users are actively working on the target mailbox server).
Personally I haven’t experienced any move request failures so far.
LikeLike
i tried following:
– migrated the user with admt
– enable users mailbox
– used your script to set attributes
– new move request error:
You must use the RemoteTargetDatabase parameter for remote push move requests.
+ CategoryInfo : InvalidArgument: (MSCXA1:MailboxOrMailUserIdParameter) [New-MoveRequest], RecipientTaskException
+ FullyQualifiedErrorId : 10101896,Microsoft.Exchange.Management.RecipientTasks.NewMoveRequest
for some reasons it worked with 2 testusers before with the same process. i saw, when using your script the mailbox changed from user mailbox to mail user. that ok?
LikeLike
Mailbox changed from user mailbox to mail user?
1. The script changes user object in AD, it does nothing with mailboxes;
2. The script’s purpose is to transform a user object (NO mail attributes) into a mail enabled user, so it can be used as a move request target.
The procedure we use (but it is specific, your mileage may vary) is as follows:
– forest A, mailbox-enabled user
– Create a clone in forest B using ADMT – only basic attributes, we have schema mismatch (see part 1). So I’d suggest you exclude all Exchange attributes
– use the script to prepare the clone in forest B
– execute move requests
LikeLike
solved, dont do enable mailbox 😉
http://social.technet.microsoft.com/Forums/en/exchange2010/thread/fa8c9c24-4c2b-4012-9c1f-9028b474ae00
LikeLike
its not allowed to enable the mailbox before your script. without its working.
from another forum: http://social.technet.microsoft.com/Forums/en/exchange2010/thread/fa8c9c24-4c2b-4012-9c1f-9028b474ae00
….This problem occurs when the target user is already associated with a mailbox. Often this is because the account is created through the EMC.
thanks for you great work. lets see if MS upgrades the Prepare-MoveRequest.ps1 so it could run after the user is migrated with ADMT. I think most of the AD User Migrations need to migrate with ADMT because of the SID History.
LikeLike
ok, dont enable mailbox before setting attributes, see http://social.technet.microsoft.com/Forums/en/exchange2010/thread/fa8c9c24-4c2b-4012-9c1f-9028b474ae00
so far good work, thx for the script. Hopefully MS updates their powershell script
to use after the users are migrated with ADMT (SID History..).
LikeLike
ok, dont enable the mailbox of the user, see http://social.technet.microsoft.com/Forums/en/exchange2010/thread/fa8c9c24-4c2b-4012-9c1f-9028b474ae00.
thanks for your script and work. Lets see if MS will change the prepare.ps1 script so it can be used when the user is migrated with admt (SID History).
LikeLike
Did found out what was wrong with “all attributes” enabled in ADMT. You should take all attributes except “MSExchHomeServerName”. If you migrates all users via ADMT WITH all attributes, then run a script that “clear” the “MSExchHomeServerName” attribute. The “MSExchHomeServerName” property links to the old server and therefor it does not work. Great works with this script.
LikeLike
You can also exclude (Exchange) attributes in ADMT (so you don’t need to clear it).
LikeLike
I enabled the mailbox before i used the script to set the attributes… also see here: http://social.technet.microsoft.com/Forums/en/exchange2010/thread/fa8c9c24-4c2b-4012-9c1f-9028b474ae00 Now it work fine. Thanks for the script.
btw. for some reasons i dont have any Exchange Attributes to exclude when using ADMT. Any ideas why?
LikeLike
iam sorry for the mega double posts, but the system didnt show me my post, so i thought theres something wrong. Maybe someone could delete the double stuff.
LikeLike
No problem, I found several comments in the moderation queue. Akismet (antispam) is a bit strict when users post (too much) links .. should be ok now (for you).
LikeLike
Hello Michel,
fantastic job. Thank you very much for publishing this. You earned a crate of beer when you’ll ever visit Braunschweig, Germany 🙂
Keep Exchanging!
LikeLike
In that case I’d prefer Lager vom Fass 😛
LikeLike
additional info for the Prepare-MoveRequest.ps1 script. You CAN migrate the users with ADMT, but if the Prepare-MoveRequest.ps1 creates a new user that means, there a some attributes missing. So the script cant match the attributes and instead creates a new user.
cheers,
Marco
LikeLike
I know. I’d rather see a switch to influence this behaviour.
LikeLike
Hi Marco,
I was the one with the thread about “preparemoverequest.ps1” in social technet Exchange 2010. Your script is great, but I am looking for a solution for this scenario:
Old AD Domain with Exchange 2003/2007. New AD Domain with Exchange 2010. I want to setup mailboxes in the new domain which are from type Linked mailbox, to move then the mailboxes from the old AD domain to the new AD Domain and Exchange 2010 Organization. I’ll not move any user accounts from old to new and I’ll not use ADMT.
tia
Bernd
LikeLike
I’d convert the linked mailbox to an mail-enabled user, i.e. remove associated external account etc., and then initiate the script. Or modify the script to do that for you.
LikeLike
Hi,
Have you moved any public folders cross-org using interorg to the exchange 2010 setup ? I can get top level folders replicating after creating them but the sub folders do not automatically get created and replicated like how interorg interacts with Exchange 2007. Any ideas on this ?
LikeLike
We haven’t reached that stage yet. Should work though (except for F/B it seems); did you check out this topic:
http://social.technet.microsoft.com/Forums/en/exchange2010/thread/544ddd21-6931-443c-a01e-fded6925f683
LikeLike
Btw, have you tried using the AddReplicaToPFRecursive.ps1 script?
LikeLike
sorry for the delay in coming back to you. I replicated to e2k7 using interorg then replicated to e2k10.
LikeLike
Dear mdrooji,
when there is only one smtp address configured for a user in the source domain, it is not adding the x.500 in the target domain for the user. the log file says the x.500 already in proxy addresses.
when we added a dummy address in the source domain to a test user account, the script worked perfectly.
LikeLike
That’s odd, I made it work for object with a single smtp address (and tested it).
(see 1st part of blog, 02/17/2010 at 12:10 am | #18 )
LikeLike
We added objTarget.PutEx ADS_PROPERTY_APPEND, strAttribute, Array(varAttribute) again in the else condition and it worked
If boolUpdate Then
debug (“Adding ” & varAttribute & ” to ” & strAttribute)
objTarget.PutEx ADS_PROPERTY_APPEND, strAttribute, Array(varAttribute)
Else
debug (varAttribute & ” already in ” & strAttribute)
objTarget.PutEx ADS_PROPERTY_APPEND, strAttribute, Array(varAttribute)
End If
LikeLike
That would mean adding the attribute if its present or not, which makes no sense. Weird.
LikeLike
What about resource mailboxes? can we move room mailboxes
LikeLike
We won’t be moving resource mailboxes so haven’t tried it yet.
LikeLike
Is thıs script suitable for a cross forest migration from exc2007 to another exch 2007?
LikeLike
No time to test that unfortunately. But if I recall correctly, the lookup algorithm for Exchange 2007 is different but that shouldn’t pose a problem as those attributes are cloned as well.
LikeLike
this script is great to prepare the users on the target domain! thank you
now, does anyone have a good script to move the actual mailboxes?
LikeLike
A simple get-credential / import-csv / move-mailbox will do
LikeLike
Pingback: Some 2010 Statistics « EighTwOne (821)
THANK YOU SO MUCH !!!!!!!!!!!!!! 🙂
LikeLike
Hi, I can’t comprehend how you can add your website in my rss reader. Are you able to Aid me, please
LikeLike
Add the feed to your RSS reader or aggregator:
https://eightwone.com/feed/
LikeLike
Hi,
We did not use ADMT since our users already exist in the new forest
didnt setup trust between the forests
tried the prepare-moverequest… modified so that it dont create the user
when using the new-moverequest… getting the guid error (cannot find the recipient…)
wanted to try your script
not expert on VB… how can we pass the credential to the remote forest ?
TIA,
François
LikeLike
The script is made to run from the same account, used to connect both to the source and target forest.
LikeLike
Dear mdrooji,
I got this error when run New-MoveRequest
“Error: The destination Active Directory forest isn’t up to date, which prevents the move from proceeding. Verify that Active Directory replication is working”
But my AD running properly and now error on replication.
Are you have any clue?
Thankyou before
Yanuar
LikeLike
Are you moving the mailbox to a different forest/domain?
Is that forest/domain properly prepared?
You can inspect the current levels using this table.
LikeLike
sorry that’s totally my wrong, typo on LegacyDN
Thanks
LikeLike
Pingback: Exchange 2010 Cross Forest Migration: The case of the missing User Account Attributes « More Coffee Anyone?
Odd problem, when I run the script it works and the mailbox is moved. Mail flows and I can use OWA to access the mailbox. Outlook will not open the mailbox however. This happens everywhere for every user I move. It does not for new users/mb.
Microsoft Outlook
Cannot display the folder. The attempt to log on to Microsoft Exchange has failed.
P1: 300032
P2: 14.0.4763.1000
P3: mqhw
P4: 0x8004011D
LikeLike
If working in cached mode, you tried removing the OST file and let Outlook spool it in again?
LikeLike
Not using cached mode and it does it on every machine i have tried. On with Microsoft right now and they do not seem to know either.
LikeLike
looks like Microsoft is unable to determine the issue. Right now our migration is on hold.
LikeLike
it seems like the newly migrate mailbox’s do not have the x400 and x500 addresses that my previously migrated accounts have.
LikeLike
looks like i figured it out. i was using domain.hq instead of the external email address domain.com. Once I did this in the script and the new move cmdlet the migration works. i must have changed my notes somewhere.
LikeLike
Hi Ron, glad you solved it.
LikeLike
so turns out it isnt the delivery domain that was incorrect, it is the legacydn. When I leave /o=NEWEXORG in there it works. when I put a legit name in there the mailboxes break. I don’t understand.
LikeLike
Is the old LegacyExchangeDN present in the new forest as an X500 proxy address?
LikeLike
yes
LikeLike
Well done Michel – your technical expertise in Exchange is shining.
LikeLike
In my environment your script crashed on line 185:
C:\PrepareForestMove.vbs(185, 2) (null): 0x80005000
This is the function syncAttributes… it line 185 was:
set objSource= getObject( “LDAP://”& SourceServer& “/”& strDNSource)
After some investigation using your debug function I discovered that my user’s common name had a / in it. I added the following code to deal with it:
If instr(strDNSource,”/”) 0 or instr(strDNSource,”\”) 0 or instr(strDNTarget,”/”) 0 or instr(strDNTarget,”\”) 0 Then
Exit Function
End If
Here is the entire function with my modified code:
‘*********************************************************
‘ syncAttributes
‘ migrates attributes from source to target
‘*********************************************************
Function syncAttributes (strUser, SourceServer, SourceDomain, strNewUser, TargetServer, TargetDomain)
dim strDNSource, strDNTarget, objSource, objTarget, n, strMail
strDNSource= getDN( struser, SourceServer, SourceDomain, “”)
strDNTarget= getDN( strNewuser, TargetServer, TargetDomain, “”)
If strDNSource”” AND strDNTarget “” Then
If instr(strDNSource,”/”) 0 or instr(strDNSource,”\”) 0 or instr(strDNTarget,”/”) 0 or instr(strDNTarget,”\”) 0 Then
Exit Function
End If
set objSource= getObject( “LDAP://”& SourceServer& “/”& strDNSource)
set objTarget= getObject( “LDAP://”& TargetServer& “/”& strDNTarget)
copyAttribute “mail”, objSource, objTarget, False
copyAttribute “mailNickname”, objSource, objTarget, False
copyAttribute “msExchMailboxGuid”, objSource, objTarget, False
setAttribute “targetaddress”, objSource.get( “mail”), objTarget
copyAttribute “proxyAddresses”, objSource, objTarget, True
addAttribute “proxyAddresses”, “X500:”& objSource.get( “LegacyExchangeDN”), objTarget
strMail= objSource.get( “mail”)
n= instr( strMail, “@”)
debug( strMail)
addAttribute “proxyAddresses”, “smtp:”& left( strMail, n-1)& “@”& conTargetEMailDomain, objTarget
setAttribute “msExchRecipientDisplayType”, -2147483642, objTarget
setAttribute “msExchRecipientTypeDetails”, 128, objTarget
setAttribute “legacyExchangeDN”, conLegacyExchangeDN& objSource.get(“cn”), objTarget
objTarget.setInfo
Else
debug(“*** ERR: Cannot retrieve DNs for Source or Target”)
syncAttributes= False
End If
End Function
Great script mate – Keep up the good work 🙂
LikeLike
I created a new version that also mail enables destination groups, enters the object in the global address list in the destination forest and has additional bug fixes.
Mail_Users.csv
Mail_Groups.csv
CrossForestMovePrep.vbs
LikeLike
If the destination user account is a mailbox user, the script currently changes the msExchRecipientDisplayType and msExchRecipientTypeDetails attributes back to those of a Mail Enabled User istead of a Mailbox User. As a result this breaks any mailbox users declared in the CSV file. Version 0.31 of the script checks to see if the destination user account is a mailbox user, if so then do not change these two attributes for the mailbox.
Enjoy!
LikeLike
Great script.,
The old version works perfect.
In the new version i get an error
Line 235
Char 17
Eroor: expected ‘Then’
Code 800a03F9
source: microsoft vbscript compilation error
Pleas can you help me!
LikeLike
Version 0.32 of the script below. Additional error handling and the ability to check if destination account is mailbox enabled.
LikeLike
I foolishly ran an older version of the script 0.22, with users in the new mail server and it has changed them from mailbox to Mail User.
Is it possible to reverse this?
LikeLike
Do the mailboxes appear in the list of disconnected mailboxes? If so (You might need to wait for the maintenance cycle), you should be able to reconnect them to their related accounts. For reconnecting, follow the link provided in this blog
https://eightwone.com/2010/09/24/connecting-the-disconnected-in-exchange-2010/
LikeLike
No they dont, i can still archive them to pst files though, which is bizarre. So the mailbox still exists, its just the user comes up as “Mail User” rather than “User Mailbox”. Luckily it was only old users, however if i can get them back to how they were, it would be preferred.
Cheers.
LikeLike
Sounds like attributes are messed up. Try on of the following. What happens if you try (re)enabling the mailbox user using Enable-Mailbox ?
LikeLike
If i run that on one of the users, I get: “This task does not support recipients of this type. The specified recipient domain.local/Old Users/User Name is of type UserMailbox. Please make sure that this recipient matches the required recipient type for this task.”
Then right clicking on that user in the EMC, it gives “The operation couldn’t be performed because object ‘domain.local/Old Users/User Name’ couldn’t be found on ‘dc.domain.local’. It was running the command ‘Get-Mailbox -Identity ‘domain.local/Old Users/User Name’ -ReadFromDomainController”
However i can see the user on the dc.
Cheers for your help.
LikeLike
*Fixed* Had to open ADUC and in the attribute editor for the problem user, change msExchRecipientType from 128 to 1. It then turned the user back to UserMailbox and i can now open then as normal.
LikeLike
Glad it’s fixed. Note that other attributes might still be missing for a “true” mailbox enabled user (eg msExchangeHomeServerName, msExchangeMailboxGuid) but those might still be present with values from before the issue started.
LikeLike
Just came across this script after doing some digging on migrating Ex2010 to Ex2010 across forest (obviously) – looking at testing this out in my lab before looking at production but VBS was never my strong point and I’m getting an error in the script and I’m not sure what’s causing it?
When I run it I get the following
Script: F:\BuildFiles\Extras\User Migration Scripts\PrepareForestMove.vbs
Line: 242
Char: 17
Error: Expected ‘Then’
Code: 800A03F9
Source: Microsoft VBScript compilation error
Looking at line 242 in the script it reads as follows
If strDNSource”” AND strDNTarget “” Then
Any thoughts folks (nothing like reviving a blog post that hasn’t been commented on for over 2 years 🙂
LikeLike