Skip to main content


Office 365 – Script to Move Mail Distribution Groups to the Cloud

One of the common limitations that organizations discover when deploying Exchange Online is the inability of users to “self-manage” distribution groups that are synchronized.
I’ve written about this in the past including putting together a workaround for environments that have Exchange 2013 on-premises. However, if you want users to be able to do what they’ve always done and manage Distribution Groups via Outlook, those groups will need to be moved to the cloud.
Below is a script that helps automate the process of moving the groups.

Quick Overview

The limitation is essentially that objects that are synced from on-premises can only be modified in the on-premises AD. When a user tries to modify a Distribution Group via Outlook, it is trying to modify the object in the cloud Global Address List (GAL) and not the on-premises one. As a result, the operation fails.
Moving the Distribution Group involves recreating the group in the cloud GAL however there are some attributes such as the email addresses that cannot be duplicated. This means you need to export out the existing groups configuration, remove the old group and then create the new group.


The assumption is that you have no on-premises mailboxes at this point or people in the on-premises environment do not need the Distribution Group in their GAL. If users still need to see the object, you could do something where you create a mail contact on-premises that points to the Distribution Group in the cloud and exclude that mail contact from the sync scope so there is not a conflict on the address.

My Process

There are a number of ways this can be done, this process has worked for me and provides some ability to validate your changes before removing the old group completely.
At a high level, the process is as follows:

  • Create a “placeholder group” in the cloud with all attributes populated except the email addresses
  • Export out the current email addresses to a CSV
  • Remove the on-premises by filtering it from the sync scope
  • Finalize the placeholder group by changing it’s name to the proper name and importing in the email addressees
  • Permanently remove the on-premises group by deleting it

Running the Script

To create the placeholder group and export the CSV, you’re going to run the following command:

.\Recreate-DistributionGroup.ps1 -Group "DL-Marketing" -CreatePlaceHolder

This will create a group in the cloud with the name “Cloud-PreviousDisplayName” and hide it from the GAL.
After the placeholder group is created, you will want to validate the new group. You can then delete the old group from the cloud by removing it from the sync scope. For current versions of AAD Connect, this can be done by populating the “adminDescription” attribute with the value “Group_NoSync”. See “Office 365 – The (Previously) Undocumented AAD Connect Filter” for more information on this attribute.
Once your sync cycle completes, you can run the following command to make the final cutover:

.\Recreate-DistributionGroup.ps1 -Group "DL-Marketing" -Finalize

Assuming everything looks good with the new cloud group, you can delete the on-premises one and setup a mail contact if desired.


The script for this post can be found in the Microsoft Script Center at the following link: Recreate-DistributionGroup.ps1
Did you find this article helpful?
Leave a comment below or follow me on Twitter (@JoePalarchio) for additional posts and information on Office 365.
Looking to do some more reading on Office 365?
Catch up on my past articles here: Joe Palarchio.

Thoughts on “Office 365 – Script to Move Mail Distribution Groups to the Cloud”

  1. Hi Joe, I’m getting the below message when i run your script on Exchange online Powershell, any advise please?
    The group “EURPR0.PROD.OUTLOOK.COM/Microsoft Exchange Hosted
    Organizations/” can’t be managed by recipient “Organization
    Management”. The owner of the group should have the following recipient type details: UserMailbox,LegacyMailbox,SharedM
    + CategoryInfo : NotSpecified: (EURPR02A004.PRO…oud-#BA Mentors:ADObjectId) [New-DistributionGroup], Re
    + FullyQualifiedErrorId : [Server=VI1PR02MB,RequestId=1234,TimeStamp=5/25/2017
    6:35:03 AM] [FailureCategory=Cmdlet-RecipientTaskException] C391DC6E,Microsoft.Exchange.Management.RecipientTasks
    + PSComputerName :

  2. I’m curious how the script actually works, when get-distributiongroup is both a On-prem and an EOL cmdlet.
    Running the get-distributiongroup in a remote PS EOL session will return the properties for the sync’d DL.
    Hence why you get the error above Stefan

  3. Thanks for this script. Exactly what I needed.
    Minor note: I had to add the -ignorenamingpolicy switch for new-distributiongroup in my environment. We enforce a naming policy for users, but the policy needs to be ignored for this admin action.

  4. Stefan, the issue you are having, has to do with a line in the powershell that sets the -ManagedBy parameter to the current AD synced groups value. Replace this with a valid email address of the person who should manage this group. You can temporarily set it to an administrator address until everything is done and then use the GUI to adjust accordingly.

  5. I have the same question as LukeWilkins –>
    How is the script supposed to work, since both Get-Distributiongroup and New-DistributionGroup are On-prem and an EOL cmdlet’s?
    There is no way this can work in hybrid Exchange environment when there is active connection to on-prem Exchange and to EOL from the same Powershell session (script needs both to work).
    What am I missing?

  6. Hi Joe I ran through the script but when I get to finalize I get errors.
    You cannot call a method on a null-valued expression.
    At C:\scripts\Recreate-DistributionGroup.ps1:162 char:9
    + $NewPrimarySmtpAddress = ($NewAddresses | Where {$_ -clike “SMTP:*”}).Re …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
    Cannot process argument transformation on parameter ‘PrimarySmtpAddress’. Cannot convert null to type
    + CategoryInfo : InvalidData: (:) [Set-DistributionGroup], ParameterBindin…mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-DistributionGroup
    + PSComputerName :
    Cannot process argument transformation on parameter ‘EmailAddresses’. Cannot convert value
    “System.Collections.Hashtable” to type “Microsoft.Exchange.Data.ProxyAddressCollection”. Error: “The address
    ‘AddressString : /o=ExchangeLabs/ou=Exchange Administrative Group ‘ is invalid: The e-mail address prefix (type)
    cannot exceed 9 characters.”
    + CategoryInfo : InvalidData: (:) [Set-DistributionGroup], ParameterBindin…mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-DistributionGroup
    + PSComputerName :
    The operation couldn’t be performed because object ‘Test Distro’ couldn’t be found on
    + CategoryInfo : NotSpecified: (:) [Set-DistributionGroup], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : [Server=CY4PR11MB1957,RequestId=55e7b889-1ee1-4c89-83ad-d7fa743de540,TimeStamp=9/1/2017
    10:11:45 PM] [FailureCategory=Cmdlet-ManagementObjectNotFoundException] B921029C,Microsoft.Exchange.Management.Rec
    + PSComputerName :

  7. Hi am getting below give error and its happning in multiple DL’s
    How can this be fixed?
    There are multiple recipients matching the identity “Brad Williamson”. Please
    specify a unique value.
    + CategoryInfo : NotSpecified: (:) [Set-DistributionGroup], Manag
    + FullyQualifiedErrorId : [Server=AM5PR0601MB2417,RequestId=af947047-22d2-
    45de-9054-8918925d8b8d,TimeStamp=05-01-2018 18:46:06] [FailureCategory=Cmd
    let-ManagementObjectAmbiguousException] 5A67B137,Microsoft.Exchange.Manage
    + PSComputerName :

  8. Chris Richardson

    DOOD – this was downloaded by me in DEC … then another guy helped me get PS setup so anyone can start a pssession to current onprem exchange AND connect MSOL … and we’ve moved like 200+ onprem DL’s to online DG’s!
    ONE big issue we’re now seeing/realized is the “memberof” – it doesn’t capture the memberof… and we are trying to hack your script to ADD that to the newly created cloud- DG so it doesn’t get “lost” – because when you “lose” that – a lot of interrelationships get broken. 🙁
    Any suggestion on this one?
    Also… for DL’s that were able to be msg’d from outside org, since we’re in hybrid mode and mail routing hasn’t changed yet … we thought we needed to ADD a local contact with proxy address so onprem knew where to route mail – and I’m SURE this has been working for us … but latest DL’s we moved, getting mail from SF is just not working now … ideas on the onprem contact issue to get around this routing to cloud issue? (we use messagelabs for routing)

  9. This worked great for me however for groups that have over 1000 members, it only migrates 1000.
    i know you have to use the -resultsize unlimited parameter, but there’s no way to apply it on the command line when running the script.

  10. I’m running into the same errors as JMC, you cant apply it to the line and I had no success with applying it to the a script

  11. One of the problems we’ve encountered when moving accounts to cloud-only is that it appears Exchange resolves “recent/suggested contacts” by unique identifier not email address. This means when we we move a group or mailbox from on-prem to cloud, emails will bounce because staff are selecting the group from the resent list (first option). Is there a way to migrate the unique ID used by exchange or is there another solution for this issue?

  12. thanks – script worked great. for those having the issue ‘There are multiple recipients matching the identity “user”. Please
    specify a unique value.’ for us its becasue we’re syncing multiple domains to 365 that have the same users duplicated (ie user exists multiple times in 365).

    just had to change this line –

    $OldMembers = (Get-DistributionGroupMember $OldDG.PrimarySmtpAddress).PrimarySmtpAddress

    to use primarysmtp address instead of name so it stores their primarysmtp in the variable.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Joe Palarchio

More from this Author

Follow Us