Skip to content

A GALSync powershell script

Here is a script I wrote to do a simple GAL synchronization between two Exchange organizations. The script finds the mail-enabled  users in one domain, and creates contacts for them in the other domain. Existing contacts will also be updated and deleted as needed.


There was a bug in the original script where I had forgotten to populate mailNickname. I have now done so, adding a “c-” to the front of it as a completely optional convention to avoid conflicts.

Update 2

Several people have commented below about needing to enable the contacts in Exchange after creation. I have used the modifcations posted by Mark in the comments to make a new version that I hope will work better with 2007 and 2010, though I have only tested it with 2010. Both versions are linked below, and please keep adding your comments and modifications.

The Script

Now I have two versions the scripts have been moved off to seperate pages. Follow the links below.

Version 2 Added the Update-Recipient command for Exchange 2007 (through local powershell) and Exchange 2010 (through remote powershell).
Version 1 Original version that uses the ActiveDirectory powershell module to create the contact objects.

Other people’s versions

Modified for Distribution Lists:

{ 78 } Comments

  1. David Lundell | May 20, 2010 at 4:23 pm | Permalink

    I suppose without an IIFP edition of FIM people have to find another free way to support this need.

  2. Carol | May 20, 2010 at 5:34 pm | Permalink

    Am I becoming repetitive? 😉

  3. Eric | June 10, 2010 at 5:46 pm | Permalink

    Very nice! How would I modify this script for a one-way sync? DOMAIN2 Users –> DOMAIN1 Contacts only?


  4. Carol | June 10, 2010 at 7:31 pm | Permalink

    Hi Eric. That is simple enough – you see at the bottom of the script I have called the SyncContacts function twice – once for each domain? Just call it once.

  5. Eric | June 11, 2010 at 5:21 pm | Permalink

    Great, thank you Carol!

  6. Nigel Harvey | August 23, 2010 at 3:30 pm | Permalink

    Tried running this – nogo on Exchange server – nogo on DC – currently loading Exchange tools to DC and will try again. RSAT tools loaded in all cases … Any thoughts in the mean time much appreciated 😀


  7. Carol | August 23, 2010 at 3:36 pm | Permalink

    I am not running this on an Exchange server or on a DC – just on a regular member server with the AD powershell plugin available.

    Any errors?

  8. Nigel Harvey | August 23, 2010 at 3:50 pm | Permalink

    Sorry :-( any clues? E2k7SP2 on Win2k8StdSp2 returns:

    PS C:\> .\GALScript.ps1
    The term ‘get-module’ is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
    At C:\GALScript.ps1:179 char:16
    + if(@(get-module <<<< | where-object {$_.Name -eq "ActiveDirectory"} ).count -eq 0) {import-module ActiveDirectory}
    The term 'import-module' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
    At C:\GALScript.ps1:179 char:93
    + if(@(get-module | where-object {$_.Name -eq "ActiveDirectory"} ).count -eq 0) {import-module <<< DOMAIN2 Contacts

    DOMAIN2 Users –> DOMAIN1 Contacts
    The term ‘Get-ADObject’ is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
    At C:\GALScript.ps1:64 char:26
    + $colUsers = Get-ADObject <<< stop-transcript
    Transcription has not been started. Use the start-transcript command to start transcription.
    Stop-Transcript : An error occurred stopping transcription: The console host is not currently transcribing.
    At line:1 char:15
    + stop-transcript <<<

  9. Nigel Harvey | August 23, 2010 at 3:52 pm | Permalink

    Bit of overlap there — which AD plugin please, the QAD one?


  10. Nigel Harvey | August 23, 2010 at 4:05 pm | Permalink

    Ah … You mean the Windows2k8R2/Windows7 one that loads with the RSAT tools …

    try and try again … :-)

  11. Nigel Harvey | August 23, 2010 at 4:16 pm | Permalink

    It now thinks source user password is Null and finds no users in the source domain – this from a Windows7x64 pc on domain using either domain access account set up when I get a 2-box challenge come up when I run it. I’m trying to find a Win2k8 server with ADWeb services running on it …


    PS D:\scripts> .\GALScript.ps1
    DOMAIN1 Users –> DOMAIN2 Contacts

    DOMAIN2 Users –> DOMAIN1 Contacts
    ConvertTo-SecureString : Key not valid for use in specified state.
    At D:\scripts\GALScript.ps1:61 char:64
    + $password = get-content $sourcePWFile | convertto-securestring <<<<
    + CategoryInfo : InvalidArgument: (:) [ConvertTo-SecureString], CryptographicException
    + FullyQualifiedErrorId : ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.Commands.Conv

    New-Object : Exception calling ".ctor" with "2" argument(s): "Cannot process argument because the value of argument "pa
    ssword" is null. Change the value of argument "password" to a non-null value."
    At D:\scripts\GALScript.ps1:62 char:27
    + $sourceCred = New-Object <<<< -Typename System.Management.Automation.PSCredential -Argumentlist $sourceUser,$pa
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    Get-ADObject : Unable to contact the server. This may be because this server does not exist, it is currently down, or i
    t does not have the Active Directory Web Services running.
    At D:\scripts\GALScript.ps1:64 char:26
    + $colUsers = Get-ADObject <<<

  12. Carol | August 23, 2010 at 4:29 pm | Permalink

    You need to create the secure files as descibed in the comments at the top of the script, and also they must be secured with the account that you then run the script with. The idea is that you can store the password in a secure file rather than hardcoding it in the script.

    I suggest you create your cred file and then try and run some of the cmdlets manually using it – such as get-adobject. Once you can do that successfully with the cred file and with the account you mean to run the script with, then the script should work.

  13. Nigel Harvey | August 24, 2010 at 1:16 pm | Permalink

    You also need AD Web Services on a Win2k8R2 DC to target the ActiveDirectory module so this script only seems to work for two Windows 2008 R2 controlled domains.

    Any thoughts/comments?

  14. Nigel Harvey | August 24, 2010 at 1:20 pm | Permalink

    … and of course ‘import-module activedirectory’ needs to work 😉

  15. Nigel Harvey | August 24, 2010 at 1:29 pm | Permalink

    PS Storming work here by the way! Despite the fact I can’t get it to run, it’s the first time I’ve found anyone take on anything like this!! It would seem that despite Microsft lauding the programmability of AD/Exchange using Powershell that they don’t want us to use it for anything worthwhile. It remains the realm of die-hards and technical staff from other areas to even try and come up with a solution like this.

    Now to see if I can get to something similar in QAD Powershell for pre-2k8R2 bodies …

  16. Carol | August 24, 2010 at 2:06 pm | Permalink

    It’s possible. The two domains I’m running it against have Windows 2008r2 DCs in them. It doesn’t mention such a requirement in that Scripting Guys page I linked to, but of course that doesn’t mean it’s not there.

    You could probably re-write the script using the Quest AD cmdlets. Alternatively it is possible to create AD objects with powershell using ADSI – just not as neat as with this plugin.

    And thanks! :)

  17. Nigel Harvey | August 27, 2010 at 12:29 pm | Permalink

    Getting there slowly … QAD command set is a bit twisty and I had to filter using LDAP rather than OLAP so someone will have an opportunity to clean some things up.

    Most of the commands are transferring ok but I’m restricted to doing it in my lunchtime hance the phenominal speed (ha…)

    PS why use target address instead of mail when working with Contacts?

  18. Tom | September 23, 2010 at 5:23 am | Permalink

    Hello Guys,

    I have a situation where Forest 1: Windows 2008 SP2 w/Exchange 2010 and Forest 2:Windows 2003 R2 w/Exchange 2003. Cross forest migration. Ran into a problem with native tool support on GAL Sync and F/B. IIFP does not work and same goes to InterOrg. Decided to try this script. But unfortunately, it requires Windows 2008 R2 DC. With above problem, is there a chance that 3rd forest promoted and link with transitive forest trust and this script is executable?

  19. Carol | September 23, 2010 at 7:25 am | Permalink

    If you can’t get a 2008r2 DC into the 2003 forest (doesn’t require a functional level upgrade or anything) then the only thing I can suggest is re-writing the script so it uses the powershell v1 ADSI methods instead. They’re a bit more long winded but the result should be the same.

  20. Nigel Harvey | October 1, 2010 at 1:23 pm | Permalink

    Hi Tom/Carol … This shouldn’t need a 3rd forest as long as the (unfortunately re-written in Powershell) script can hit both domains using LDAP from the Windows 2008 SP2 end. MIISFP doesn’t work against W2k8 and FIM GalSynch process is chewy. Good luck.

  21. Ron | November 19, 2010 at 9:00 pm | Permalink

    Hi Carol. Awesome script! Thanks for sharing it!

    You don’t need Server 2008 R2 in both forests. You can download Active Directory Management Gateway Service for Server 2003 and 2008 from Microsoft ( Read the entire article because there are prerequisites and it may require a reboot.

    The script creates Active Directory contacts. If you want them to show up in the Global Address List and Offline Address Book, you need to mail enable the contacts. To do that I added this line after the New-ADObject command in the ADDS loop.

    Get-Contact $user.displayName | foreach {Enable-MailContact $_ -externalEmailAddress $_.windowsEmailAddress.toString()}

    Then I ran the script from the Exchange Management Shell. It worked perfectly between a Server 2003 forest and a Server 2008 R2 forest. The only thing I am not sure about yet is the updating part.

  22. Carol | November 20, 2010 at 7:16 am | Permalink

    Hi Ron – thanks that’s great! I’m glad it works with 2003 after all.

  23. Rob | December 13, 2010 at 9:30 pm | Permalink

    Has anyone tried this for use with Live@edu or outlook online?

    I am looking for an alternative to ILM 2007 or FIM for syncing on site AD with outlook online. I want it to create mailboxes and sync passwords.

  24. Arne Verstraete | March 3, 2011 at 2:40 pm | Permalink

    We have two forest that has an two way trust. In both forest the domain controllers are allwindows 2008 R2 servers. The syncs users -> contacts from domain1 to domain2 works. But when users want to send an email to the contact that they find in the GAL they get an error. Unable to relay. When i look into the contact details even with adsi edit everthing looks ok. Have this in two place’s a freshly setup test enviroment and our live “production” enviroment. Are there other people who has the same issue and what can i do against it. I want to make the contacts usable in outlook and not only viewable in “active directory users and computers”.

  25. Carol | March 3, 2011 at 7:06 pm | Permalink

    To start with make sure the contacts appear in the Exchange management console. If they do then the contacts are created correcty and you need to look to your Exchange settings.

  26. Arne Verstraete | March 4, 2011 at 6:48 am | Permalink

    they appear in the exchange management console. What settings can this be?

  27. Arne Verstraete | March 4, 2011 at 7:50 am | Permalink

    I get this failure message back when i send a test mail to an imported contact.

    Diagnostische gegevens voor beheerders:

    #550 5.1.1 RESOLVER.ADR.ExRecipNotFound; not found ##

    Oorspronkelijke berichtkoppen:

    Received: from ([]) by ([]) with mapi; Fri, 4 Mar 2011
    08:32:05 +0100
    Content-Type: application/ms-tnef; name=”winmail.dat”
    Content-Transfer-Encoding: binary
    From: Arne Verstraete
    To: ipmonitor
    Subject: test
    Thread-Topic: test
    Thread-Index: AcvaPj5da7PNA7qdTj2Gx7Q9hT/n8A==
    Date: Fri, 4 Mar 2011 08:32:03 +0100
    Accept-Language: nl-BE, en-US
    Content-Language: en-US
    MIME-Version: 1.0

  28. Carol | March 4, 2011 at 11:44 am | Permalink

    I suggest you create a contact manually through Exchange and see if you get the same problem. If it works then inspect both contacts (the one created manually and the one created with this script) using ADSIEdit to see what the differences are. It’s possible you may need to modify the script to either add some extra attributes, or to run an update-recipient after the contact creation. I haven’t found that necessary but I’ve only run this in one environment. The script is posted here as an example only and you need to adjust it for your own environment.

  29. Arne Verstraete | March 4, 2011 at 2:42 pm | Permalink

    When i create a contact through Exchange I do not have the same problem. But i will test with the update-recipient cmdlet. I keep you informed. Thanks for the help in advance

  30. Jon | March 8, 2011 at 10:02 pm | Permalink

    awesome script! Any reason why I couldn’t lose the credentials portion of this script so that they are not stored in a text file, but stored with the task service? I know there are a few things *cough* that need to be done to enable PS1 files to be ran from EMS as a scheduled task, but I am pretty sure that would work, no?

  31. Carol | March 9, 2011 at 6:34 am | Permalink

    I’m sure you could. The script only uses AD methods deliberately to try and simplify the permission side of things, though a couple of people have commented that they needed to run enable-mailcontact afterwards, which would need the exchange plugins as well as exchange recipient admin, so I guess it depends on the environment. All you can do is try!

  32. Guruprasad | March 10, 2011 at 8:47 am | Permalink

    what explicity exchange permisssion is required to run this script?

  33. Carol | March 10, 2011 at 9:29 am | Permalink

    The script as written requires no exchange admin permission because it is only creating AD objects. However some people have mentioned needing to run enable-mailcontact after the contact has been created. If you find you need to do this then you will also need to load the Exchange powershell snapin and the account will need Recipient Admin permissions.

  34. Arne Verstraete | March 10, 2011 at 1:01 pm | Permalink

    I solved my problem just added the two lines below in the ADDS loop just after New-ADObject

    Get-MailContact $User.displayname | foreach {Disable-Mailcontact $_ -Confirm:$false}
    Get-Contact $user.displayName | foreach {Enable-MailContact $_ -externalEmailAddress $_.windowsEmailAddress.toString()}

  35. Guruprasad | March 11, 2011 at 12:45 pm | Permalink

    Thank you Carol for clarification.

    I am trying to create contacts in my domain (target) of users who are residing from one particular OU only in the remote domain (source) How I can achieve this? Currently the script is creating contacts of all users from the source domain.

    Thanking you,

  36. Carol | March 11, 2011 at 1:07 pm | Permalink

    You need to modify the line that starts “$colUsers =Get-ADObject…” to add a -SearchBase to the Get-ADObject command. See for more info about Get-ADObject.

  37. Guruprasad | March 11, 2011 at 1:28 pm | Permalink

    Thanks Carol, but when i added -SearchBase as below
    $colUsers = Get-ADObject -Filter $strSelectUsers -SearchBase ‘OU=Test,DC=xyz,DC=com’ -Properties * -Server $sourceDC -Credential $sourceCred

    I am getting error as it is only works for and not

    please clarify.

    thanking you, guruprasad.

  38. Carol | March 11, 2011 at 1:43 pm | Permalink

    Then you need to modify the SyncContacts function so you can pass the searchbase as a parameter.

  39. Guruprasad | March 11, 2011 at 2:33 pm | Permalink

    Finally Carol I am running individual scripts respective to each domain.

    Thank you for all the replies and the great work.

    Allow me to get back to you in case if I require your help.

    Thanking you, Guru.

  40. Krishna | March 17, 2011 at 8:24 am | Permalink

    Hi Carol,

    As Guruprasad said the script will find the users in Domain A and create the contacts for the users in Domain B. We can modify the script and allow to find users in only one OU and create the contacts for users only in the OU. My question is I have a dynamic distribution list which contain some users as member. The script need to find all the dynamic distribution list members and create contacts only for those users. How to achieve this. If I directly went to EMC I am able to get all members using the below command.

    $group = Get-DynamicDistributionGroup –identity “AllStaff”

    Get-Recipient –RecipientPreviewFilter $group.RecipientFilter

    How to insert the above commands in script?


  41. Mark | March 23, 2011 at 2:59 pm | Permalink

    Great Script Carol!

    I’m working in test environment to find the best solution for our company merger and came accross your script.

    I have an Exchange 2010 server in each forest.

    FIM2010 had been attempted, but I didn’t get far with it before I found your script and decided to try it out.

    When I ran the GALSync script the contacts were created, but not mail enabled (As Ron stated.. Unfortunately I didn’t see his comments until today)

    I was able to substitue remote Exchange 2010 powershell commands to create mail enabled contacts and wanted to share my tweaks in case anyone was interested. These tweaks only modified the ADD functionality, leaving the DELETE and UPDATE sections alone.

    Added the following two variables to the Global Definitions section:

    I modified the PARAM line of the SyncContacts Function to add $targetURI:
    PARAM($sourceDC, $sourceUser, $sourcePWFile, $targetDC, $targetUser, $targetPWFile, $targetOU, $targetURI)

    The New-ADObject line was commented out in the ADDS line and the following thre lines were added:
    $SO=New-PSSessionOption -SkipCACheck -SkipCNCheck –SkipRevocationCheck –ProxyAccessType None

    $PSSession1=New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $targetURI -Credential $targetCred -SessionOption $SO

    invoke-Command -Session $PSSession1 -ScriptBlock{param ($User,$targetOU,$targetaddress) New-MailContact -Name $User.displayname -OrganizationalUnit $targetOU -ExternalEmailAddress $targetAddress} -ArgumentList $User,$targetOU,$targetaddress

    In the MAIN section the SyncContacts lines were modified to add the $targetURI and $sourceURI parameter
    SyncContacts -sourceDC $DOMAIN_1 -sourceUser $USER_1 -sourcePWFile $PWFILE_1 -targetDC $DOMAIN_2 -targetUser $USER_2 -targetPWFile $PWFILE_2 -targetOU $OU_CONTACTS_2 $targetURI

    SyncContacts -sourceDC $DOMAIN_2 -sourceUser $USER_2 -sourcePWFile $PWFILE_2 -targetDC $DOMAIN_1 -targetUser $USER_1 -targetPWFile $PWFILE_1 -targetOU $OU_CONTACTS_1 $sourceURI

    I was also able to get Free/Busy shared between forests using the Microsoft “Configure the Availability Service for Cross-Forest Topologies” technote.

    Who Needs FIM 2010 :)

    Thanks Again Carol

  42. Carol | March 23, 2011 at 3:16 pm | Permalink

    Thanks very much for this Mark. I have merged the two comments you made so that the line you mentioned is correct in your orignial post. I will update the script in the post as soon as I get a chance to test your modifications. I was hoping to get away with not having to load the Exchange plugin and give the account Exchange permissions, but there you go, can’t have everything. Thanks again!

  43. Mark | March 23, 2011 at 5:42 pm | Permalink


    No problem.

    The Exchange module shouldn’t be required locally on the GALSync Server as the commands are run remotely on the Exchange server.

    In order to get the remote Exchange powershell to work I needed to run a couple Exchange Management Shell commands on the Exchange server in the remote forest. These commands will need to be run for the GALSync service account in each forest on the appropriate Exchange server.

    These commands give the GALSync service account in each forest the appropriate permissions

    To add the GALSync service account to the Recipient Management Role Group:
    add-rolegroupmember “Recipient Management” -member GALSync

    To grant the service account permission to use PowerShell remotely on the remote forest Exchange server:
    set-user -identity “GALSYNC” -RemotePowerShellEnabled $True



  44. Mark | March 23, 2011 at 5:59 pm | Permalink


    Windows Authentication also needs to be enabled and Anonymous Authentication needs to be disabled on the PowerShell virtual directory in IIS on the Exchange Servers.

    If you can combind this with the previous post that would be great

  45. Mark | March 24, 2011 at 2:28 pm | Permalink

    I was able to tweak the modified GALSync Script to create X500 addresess for the contacts to mitigate potential issues with replies to old emails. I also changed the ADD section to be more like Carol’s original with the Alias and disabling the automatic application of address policy on the contact.

    Here’s my current ADDS section:

    ### ADDS

    foreach ($user in $colAddContact)
    write-host “ADDING contact for ” $user.mail

    $targetAddress = “SMTP:” + $user.mail

    $X500Address = “X500:” + $user.LegacyExchangeDN

    $alias = “c-” + $user.mail.split(“@”)[0]

    $hashAttribs = @{‘targetAddress’ = $targetAddress}
    $hashAttribs.add(“mailNickname”, $alias)

    foreach ($attrib in $arrAttribs)
    if ($user.$attrib -ne $null) { $hashAttribs.add($attrib, $user.$attrib) }
    ### Original AD Contact Creation
    ### New-ADObject -name $user.displayName -type contact -Path $targetOU -Description $user.description -server $targetDC -credential $targetCred -OtherAttributes $hashAttribs

    # Create Remote PowerShell Session
    $SO=New-PSSessionOption -SkipCACheck -SkipCNCheck –SkipRevocationCheck –ProxyAccessType None
    $PSSession1=New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $targetURI -Credential $targetCred -SessionOption $SO

    # Create Contact through the Remote PowerShell Session
    invoke-Command -Session $PSSession1 -ScriptBlock{param ($User,$alias,$targetOU,$targetaddress) New-MailContact -Name $User.displayname -Alias $alias -OrganizationalUnit $targetOU -ExternalEmailAddress $targetAddress -PrimarySmtpAddress $user.mail} -ArgumentList $User,$alias,$targetOU,$targetaddress

    # Add X500 Addressing
    $CurrentContact=invoke-Command -Session $PSSession1 -ScriptBlock{param ($User) Get-MailContact -identity $User.displayname} -ArgumentList $User
    $CurrentContact.EmailAddresses += $X500Address
    invoke-Command -Session $PSSession1 -ScriptBlock{param ($CurrentContact,$User) Set-MailContact -EmailAddresses $CurrentContact.EmailAddresses -identity $User.displayname} -ArgumentList $CurrentContact,$User

    # Close Remote PowerShell Session
    Remove-PSSession $PSSession1



  46. Guruprasad | April 6, 2011 at 12:42 pm | Permalink

    Hi Carol,

    The script is working just fantastic.

    When we enumerated 16000+ users it took 4 hours. Does the script behaviour remains same everytime it executes or it reads only delta changes and update the contacts which should reduce the executing time from 4 hours?

    If it does not update delta changes what is the work around.

    first time execution for 16000 user objects – took 4 hours
    second time only 100 existing user objects’ attributes changed – will it take 4 hours or less time?

    Rgds, Guruprasad.

  47. Carol | April 6, 2011 at 2:16 pm | Permalink

    Wow so many users! I haven’t run it on such large numbers myself. The script will check through every contact the next time but should only update it if there’s a change to be made. I don’t know what the time difference will be but maybe you could report back.

  48. Rick | April 11, 2011 at 11:31 pm | Permalink

    I’m having a tricky time creating the secure files..

    running “read-host -assecurestring | convertfrom-securestring | out-file C:\scripts\filename.txt”

    gets me this:

    ConvertFrom-SecureString : Cannot process argument because the value of argument “SecureString” is invalid. Change the value of the “SecureString” argument and run the operation again.
    At line:1 char:53
    + read-host -assecurestring | convertfrom-securestring <<<< | out-file C:\scripts\filename.txt
    + CategoryInfo : InvalidArgument: (:) [ConvertFrom-SecureString], PSArgumentException
    + FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.ConvertFr omSecureStringCommand

    Any assistance?

  49. Rick | April 12, 2011 at 1:02 am | Permalink

    Now I’ve got

    Get-ADObject : Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running.
    At C:\ittools\GALSYNC.PS1:79 char:26
    + $colUsers = Get-ADObject <<<< -Filter $strSelectUsers -Properties * -Server $sourceDC -Credential $sourceCred
    + CategoryInfo : ResourceUnavailable: (:) [Get-ADObject], ADServe rDownException
    + FullyQualifiedErrorId : Unable to contact the server. This may be becaus e this server does not exist, it is currently down, or it does not have th e Active Directory Web Services running.,Microsoft.ActiveDirectory.Management.Commands.GetADObject

    No users found in source domain!

    But there is that service there.. Any clues?

  50. Carol | April 12, 2011 at 5:29 am | Permalink

    Make sure that you have everything working on the powershell side before trying to run the script. Run a powershell session as the user you will use to run the script, import the module (import-module ActiveDirectory), and then make sure you can run Get-ADObject correctly. Note also that the script uses the domain name to find a DC so DNS has to be working properly.

  51. Rick | April 13, 2011 at 12:45 am | Permalink

    Ah, it turned out it was using a 2008 GC for DNS that wasn’t updated with the relevant patches.

    Now that it is, I’m getting a different error. I’m assuming the “No contact found for” is normal.

    ….. (many more above)
    No contact found for
    No contact found for

    Updating using DC
    ADDING contact for
    New-ADObject : Directory object not found
    At C:\ittools\GALSYNC.PS1:155 char:15
    + New-ADObject <<<< -name $user.displayName -type contact -Path $targetOU -Description $user.description -server $targetDC -credential $targetCred -OtherAttributes $hashAttribs
    + CategoryInfo : ObjectNotFound: (cn=Glenn Opray,…C=apisec,DC=com:String) [New-ADObject], ADIdentityNotFoundException
    + FullyQualifiedErrorId : Directory object not found,Microsoft.ActiveDirectory.Management.Commands.NewADObject

    New-PSSession : Cannot validate argument on parameter 'ConnectionUri'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
    At C:\ittools\GALSYNC.PS1:168 char:111
    + if ($PSSession -eq $null) {$PSSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri <<<< $targetURI -Credential $targetCred -SessionOption $SO}
    + CategoryInfo : InvalidData: (:) [New-PSSession], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.NewPSSessionCommand

    Invoke-Command : Cannot validate argument on parameter 'Session'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
    At C:\ittools\GALSYNC.PS1:169 char:28
    + Invoke-Command -Session <<<< $PSSession -ScriptBlock{param ($alias,$targetDC) Update-Recipient -Identity $alias -DomainController $targetDC} -ArgumentList $alias,$targetDC
    + CategoryInfo : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand is Domain_1 is Domain_2

  52. Carol | April 13, 2011 at 5:05 am | Permalink

    It’s probably just that something doesn’t have a value. There isn’t a whole lot of error checking in this script. Try opening it in PowerGUI so you can run one line at a time and check variable values.

  53. Ronnie | April 21, 2011 at 1:07 pm | Permalink

    Whats missing in the V2 script is something to set the $URI_2 variable.

  54. Carol | April 23, 2011 at 1:58 pm | Permalink

    I’ve added the extra declarations. Thanks

  55. cy | May 31, 2011 at 11:26 am | Permalink

    Hi Carol, great script. I got it working between a W2k8DC/E2k7 and a W2k8R2DC/E2010 forest. But I have one little problem.

    Say I already have a contact in E2010, Ext in OU1. A mailbox in E2k7 has smtp address When I run the script and specify the destination OU as OU2, it will create a contact with the same external smtp address.
    If I were to manually create 2 contacts with the same ext email address using EMS, it will fail.
    How can I have the contact creation fail using your script if the same Ext smtp address was detected?

  56. Carol | May 31, 2011 at 11:41 am | Permalink

    The script assumes all contacts are in the OU you specified at the top, and is therefore both searching and provisioning to the same OU. Your options are to put all your contacts from the other forest in that OU (including pre-existing ones), or to rewrite the “Enumerate Contacts” part of the script so it uses a different OU as the search root – eg., the root of the domain. Be careful though – you may also need to modify the “Find Contacts to Delete” section so it doesn’t delete contacts you still wanted.

  57. cy | June 1, 2011 at 3:30 am | Permalink

    Thanks for the reply, I will try that out.
    BTW, why are you appending c- to the alias of the imported contacts?


  58. Carol | June 1, 2011 at 5:09 am | Permalink

    Prefixing, not appending. And I explained that up the top.

  59. Sandy | June 25, 2011 at 5:16 pm | Permalink

    Hi Carol,

    I found very interesting this script.I am new to this. In the video bat is running intead of ps1.Can you please explain me?..

    Also which is the write script to enble the contacts in source domain ie- version1 or version2

  60. Sandy | June 25, 2011 at 5:17 pm | Permalink

    Also which is the right script to enble the contacts in source domain ie- version1 or version2

  61. Carol | June 25, 2011 at 8:49 pm | Permalink

    Version 2 is your best bet for Exch 2007/2010.

  62. Sandy | June 26, 2011 at 4:09 pm | Permalink

    Thanks Carol.Is it possible to get Galsync-curuIT-v3_bat.txt? I am unable to see the files in the mentioned link.

  63. Sandy | June 26, 2011 at 4:26 pm | Permalink

    Sorry for the confusion.. I have got the links from the following site (Gal sync video)

  64. Carol | June 26, 2011 at 5:30 pm | Permalink

    This has nothing to do with my script.

  65. Rick | July 20, 2011 at 12:00 am | Permalink

    Thanks so much for this script, it’s been working like a charm ever since I found it back in April.
    One question, how difficult would it be to adapt it to transfer across mail enabled distribution groups?

  66. Carol | July 20, 2011 at 2:58 am | Permalink

    It shouldn’t be hard. You could try changing the definition for $strSelectUsers so it selects the DLs as well. If that doesn’t work then just define $strSelectDLs with the appropriate lookup string, and loop through again. And if you do post your modified script somewhere be sure to send me the link :-)

  67. Peter | February 7, 2012 at 8:24 am | Permalink

    Just an FYI: in Google Chrome you can only see the comments, not the actual blog article. Thought you’d want to know this.

  68. Scott Hahn | February 13, 2012 at 8:35 pm | Permalink

    I am working on a project that is 3 way

    Before I go too far I want to make sure this script definitely supports Exchange 2003 as both a source and a target?


  69. Mark D | February 17, 2012 at 2:15 am | Permalink

    I’m having problems with the DNS part,

    I can seem to be able to GET-ADDOMAINCONTROLLER from DOMAIN A to DOMAIN B, i tried adding a new zone namely DOMAIN B. but to no avail.

    i can surely ping DOMAIN A to DOMAIN B and vice versa.

    Get-ADDomainController : The specified domain either does not exist or could not be contacted
    At C:\scripts\galsync.ps1:70 char:39
    + $objSourceDC = Get-ADDomainController <<<< -Discover -DomainName $sourceDomain
    + CategoryInfo : ObjectNotFound: (:) [Get-ADDomainController], ADException
    + FullyQualifiedErrorId : GetADDomainController:BeginProcessingOverride:DiscoverDC:1355,Microsoft.ActiveDirectory.

  70. Carol | February 17, 2012 at 2:27 am | Permalink

    You could hard code your domain names. Note I don’t give technical support on these scripts – they’re presented as-is (and FREE) and you will probably need to modify for your environment.

  71. Mark D | February 17, 2012 at 2:30 am | Permalink

    got it.. I really appreciate the code,. thank you! :)

  72. Mitch B | March 1, 2012 at 3:04 am | Permalink


    I want to try to create mailbox instead of contacts. I try to change some of the code to find for existing user instead of contact and changing the -type to “mailbox”. But I cannot get it to work. I am fairly new to powershell scripting and am just experimenting.

    Any suggestions?

  73. John | March 2, 2012 at 7:05 am | Permalink

    Can this work with Exchange 2003 -> 2010?

  74. Rob | May 29, 2012 at 3:53 pm | Permalink

    Will this script (creating the mail enabled contacts in both forests) enable me (in forest A) to see free busy info for those users that are in the remote forest (Forest B). Or does this simply put an entry in the GAL for the users in the remote forest by creating a “dot forward”? thanks

  75. Carol | May 29, 2012 at 10:02 pm | Permalink

    That’s right it’s a GAL sync not a free-busy sync.

  76. Pavel | June 27, 2012 at 3:24 pm | Permalink

    Hi, I got the following error, help to cope with it

    Both controllers domera running Windows Server 2008 r2

    PS C:\Scripts> C:\Scripts\GalSync.ps1
    You must provide a value expression on the right-hand side of the ‘-‘ operator.
    At C:\Scripts\GalSync.ps1:89 char:14
    + $targetCred – <<<< Properties targetAddress
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ExpectedValueExpression

  77. Pavel | June 27, 2012 at 4:13 pm | Permalink

    *domera = domain

  78. Bill Z | August 1, 2012 at 1:19 pm | Permalink

    Any thoughts on enabling this to do the same for Linked Accounts in an Account forest? My environment has a mixture of user accounts in an Exchange Resource Forest and linked accounts from a trusted forest.


misoprostol online pharmacy

cytotec order on line

cytotec without prescriptions in usa

no prescription generic cytotec

indian cytotec

cytotec purchase overnight delivery

generic cytotec without prescription

no prescription cytotec

cytotec sale no prescription

where to purchase cytotec oral cheap

cytotec order on line

200 mg cytotec

buy cytotec online with no perscription

where can i buy cytotec without a prescription

order cytotec overnight

buy cytotec without a percsription

buy misoprostol australia

100 mg cytotec

cytotec no rx

cytotec online purchase

cytotec for sale

cytotec generic

cytotec oral tablet no prescription discount

how to get cytotec

cytotec online cheap

buy misoprostol cheap without perscription

buying cytotec online

buy cytotec 200mg

order cytotec online consultation

cytotec oral tablet no prescription discount

cheap prices on cytotec

non prescription cytotec

how to by cytotec online

online pharmacy no prescription cytotec

do you need a prescription for cytotec in mexico

how to get cytotec online no prescription in 100 days

Where can i buy some cytotec online only using cash or money orders

cytotec no prescription needed 200mg

overnight shipping on generic cytotec

buy cytotec online no prescription

cytotec precio

order cytotec without rx

buy cytotec next day delivery

order cytotec no prescription

generic cytotec no prescription

cytotec generic

generic cytotec online no prescription

cytotec order

real cytotec without prescription

cytotec buy online

order cytotec online overnight shipping

cytotec buy online no prescription

cytotec online order

canada cytotec

order generic cytotec online no prescription

online pharmacy cytotec