Forum Discussion
Migrating On-Prem Distribution Groups to Exchange Online in Hybrid Mode
I have come across a number of articles on migrating distribution groups to Office 365 when you're not in hybrid mode. My situation is having hundreds of DLs on my on-prem Exchange environment that I need to move to the cloud. We don't want these converted to Office 365 groups, we want true distribution groups in the cloud. Is this possible with scripting or some other method? I really don't want to have to re-create these by hand.
48 Replies
- David_RichardCopper ContributorIt's best to migrate (recreate) the on-premises Distribution Groups to Exchange Online in steps. So you can validate them before you do the cutover.
All the PowerShell scripts and the steps are shown in the post:
https://www.alitajran.com/migrate-distribution-groups-to-microsoft-365/ - Tech_Team_BluesCopper Contributor
Not sure if this is a unique issue, but I thought I'd ask since it seems like it's still an ongoing issue. I just started at a new company, and they have a hybrid environment, but have removed their Exchange server already so I have no way to manage old DL's except by using the attribute editor in AD. I'd like to get them all to be managed through Exchange admin center. I'm hoping there's a genius script that can pull everything from AD so I don't have to manually convert everything, so I've come here to kindly ask for assistance if anyone happens to know of a way.
- Gregory HallCopper Contributor
######## Global Variables ############ Set-Location -Path $env:USERPROFILE\Documents #Get-PSSession | Remove-PSSession $UserCredential = Get-Credential $ListOfGroupsToMigrate = Import-csv .\GroupMigration.csv ####################################### $ListOfGroupsToMigrate | ForEach-Object { $SingleGroupToMigratePSMTP = $_.GroupEmailAddress $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://ExhangeServerFQDN.com/PowerShell/ -Authentication Kerberos -Credential $UserCredential Import-PSSession $Session -Allowclobber $distrotomovedetails = Get-DistributionGroup -Identity $SingleGroupToMigratePSMTP $distrotomovedetails | Export-csv .\singlegroupdetailsbeforedelete.csv -NoTypeInformation $distrotomovedetails | ForEach-Object{ $DistributionGroupName = $_.PrimarySmtpAddress Get-DistributionGroupMember -Identity $DistributionGroupName | ForEach-Object{ [PSCustomObject]@{ DistributionGroup = $DistributionGroupName MemberName = $_.DisplayName EmailAddress = $_.PrimarySmtpAddress #Other recipient properties here } } } | Export-csv .\GroupmembersBeforeDel.csv -NoTypeInformation ######################################### $groupowner = $distrotomovedetails.ManagedBy $GroupOwnerCorrected = $groupowner.split('/')[-1] $GroupOwnerCorrected.ToString() $Managedby = Get-ADuser -Filter "displayName -eq '$GroupOwnerCorrected'" $distrotomovedetails | fl # Extract All Email Addresses for single group one per line so you can easily add back in EXO $addresses = @() Foreach ($mbx in $distrotomovedetails) { Foreach ($address in $mbx.EmailAddresses) { $obj = "" | Select-Object Identity,Alias,EmailAddress,RecipientType $obj.Alias = $mbx.Alias $obj.Identity = $mbx.Identity $obj.RecipientType = $mbx.RecipientType $obj.EmailAddress = $address.ToString().SubString(0) $addresses += $obj } } $addresses | Export-Csv .\SingleDistroGroupsmtpaddresses.csv -NoTypeInformation $addresses #Remove OnPremises Group Remove-DistributionGroup -Identity $distrotomovedetails.Identity -Confirm:$false -Verbose Start-Sleep 120 #Create On-Premises Contact $ExternalEmailAddress = $distrotomovedetails.Alias + "@tenant.mail.onmicrosoft.com" New-MailContact -DisplayName $distrotomovedetails.DisplayName -Alias $distrotomovedetails.Alias -ExternalEmailAddress $ExternalEmailAddress -OrganizationalUnit "OU=Contacts,OU=Exchange Accounts,DC=lge-mdr,DC=com" Set-MailContact -Identity $distrotomovedetails.PrimarySmtpAddress -HiddenFromAddressListsEnabled $true #Close Session with OnPremise Exchange Get-PSSession | Remove-PSSession -Verbose #Connect EXO Import-Module ExchangeOnlineManagement Connect-ExchangeOnline -Credential $UserCredential Write-Host -ForgroundColor Cyan "Running AAD Connect Sync" #Remotely Run AAD Connect Delta Sync $session = New-PSSession -ComputerName FQDNAADServer.com Invoke-Command -Session $session -ScriptBlock {Import-Module -Name 'ADSync'} Invoke-Command -Session $session -ScriptBlock {Start-ADSyncSyncCycle -PolicyType Delta} Remove-PSSession $session #Pause until AAD Connect Sync can run and Azure AD to catch up Write-Host -ForgroundColor Green "Pausing for AAD Connect to run and then we check EXO for the group" function Start-Sleep($seconds) { $doneDT = (Get-Date).AddSeconds($seconds) while($doneDT -gt (Get-Date)) { $secondsLeft = $doneDT.Subtract((Get-Date)).TotalSeconds $percent = ($seconds - $secondsLeft) / $seconds * 100 Write-Progress -Activity "Sleeping" -Status "Sleeping..." -SecondsRemaining $secondsLeft -PercentComplete $percent [System.Threading.Thread]::Sleep(500) } Write-Progress -Activity "Sleeping" -Status "Sleeping..." -SecondsRemaining 0 -Completed } Start-Sleep 200 Write-host -ForegroundColor Cyan "Verifing the group is gone from Azure AD, command will proceed once group is not showing in EXO" $distrotomovedetails.PrimarySmtpAddress while($GetDistroFinder -ne $null){ Write-Host -ForegroundColor Cyan "Checking EXO for the old synced Distro Group" $GetDistroFinder = Get-DistributionGroup -Identity $distrotomovedetails.PrimarySmtpAddress Start-Sleep 10 } Write-Host -ForegroundColor Green "Group Appears to have been removed please verify on Azure AD" Read-Host -Prompt "Hit Enter To Proceed" ############ Recreate Group in the cloud ########################################################################################################################################### Write-Host -ForegroundColor Cyan "Recreating the group in EXO" #Create the EXO Distro group New-DistributionGroup -DisplayName $distrotomovedetails.DisplayName -Alias $distrotomovedetails.Alias -Name $distrotomovedetails.Name -MemberDepartRestriction $distrotomovedetails.MemberDepartRestriction -MemberJoinRestriction $distrotomovedetails.MemberJoinRestriction -PrimarySmtpAddress $distrotomovedetails.PrimarySmtpAddress -RequireSenderAuthenticationEnabled:$false -ManagedBy $Managedby.UserPrincipalName -Verbose #Add additional SMTP and x500 addresses to the group. Write-Host -ForegroundColor Green "Adding SMTP and x500 Addresses Back to" $distroprimarysmtp $EmailAddressesToAdd = Import-Csv .\SingleDistroGroupsmtpaddresses.csv $EmailAddressesToAdd | ForEach-Object { $AliasOfGroup = $_.Alias $AddressToAdd = $_.EmailAddress $x500 = "x500:"+ $distrotomovedetails.LegacyExchangeDN $GroupLookup = Get-DistributionGroup -Identity $AliasOfGroup Set-DistributionGroup -Identity $GroupLookup.Identity -EmailAddresses @{Add=$AddressToAdd} -Verbose } #Add Group Members Back to Group Write-Host -ForegroundColor Magenta "Adding Members Back to Group" $GroupMembers = Import-Csv .\GroupmembersBeforeDel.csv $GroupMembers | ForEach-Object { $Distroprimarysmtp = $_.DistributionGroup $MemberPrimarySMTP = $_.EmailAddress Write-Host "Adding $memberprimarysmtp to $Distroprimarysmtp" Add-DistributionGroupMember -Identity $Distroprimarysmtp -Member $MemberPrimarySMTP } #Confirm Group Get-DistributionGroup $distroprimarysmtp | Select Alias,PrimarySmtpAddress,ManagedBy,IsDirsynced,WhenCreated | FT }
- Tech_Team_BluesCopper Contributor
Thanks for the script, but the $Session string has http://ExchangeServerFQDN.com as a session option, and I no longer have an on-prem exchange server to connect to, so the script doesn't flow. I have a feeling I'm going to have to bite the bullet and start transferring them all manually.
- Gregory HallCopper Contributor
######## Global Variables ############ Set-Location -Path $env:USERPROFILE\Documents #Get-PSSession | Remove-PSSession $UserCredential = Get-Credential $ListOfGroupsToMigrate = Import-csv .\GroupMigration.csv ####################################### $ListOfGroupsToMigrate | ForEach-Object { $SingleGroupToMigratePSMTP = $_.GroupEmailAddress $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://ExhangeServerFQDN.com/PowerShell/ -Authentication Kerberos -Credential $UserCredential Import-PSSession $Session -Allowclobber $distrotomovedetails = Get-DistributionGroup -Identity $SingleGroupToMigratePSMTP $distrotomovedetails | Export-csv .\singlegroupdetailsbeforedelete.csv -NoTypeInformation $distrotomovedetails | ForEach-Object{ $DistributionGroupName = $_.PrimarySmtpAddress Get-DistributionGroupMember -Identity $DistributionGroupName | ForEach-Object{ [PSCustomObject]@{ DistributionGroup = $DistributionGroupName MemberName = $_.DisplayName EmailAddress = $_.PrimarySmtpAddress #Other recipient properties here } } } | Export-csv .\GroupmembersBeforeDel.csv -NoTypeInformation ######################################### $groupowner = $distrotomovedetails.ManagedBy $GroupOwnerCorrected = $groupowner.split('/')[-1] $GroupOwnerCorrected.ToString() $Managedby = Get-ADuser -Filter "displayName -eq '$GroupOwnerCorrected'" $distrotomovedetails | fl # Extract All Email Addresses for single group one per line so you can easily add back in EXO $addresses = @() Foreach ($mbx in $distrotomovedetails) { Foreach ($address in $mbx.EmailAddresses) { $obj = "" | Select-Object Identity,Alias,EmailAddress,RecipientType $obj.Alias = $mbx.Alias $obj.Identity = $mbx.Identity $obj.RecipientType = $mbx.RecipientType $obj.EmailAddress = $address.ToString().SubString(0) $addresses += $obj } } $addresses | Export-Csv .\SingleDistroGroupsmtpaddresses.csv -NoTypeInformation $addresses #Remove OnPremises Group Remove-DistributionGroup -Identity $distrotomovedetails.Identity -Confirm:$false -Verbose Start-Sleep 120 #Create On-Premises Contact $ExternalEmailAddress = $distrotomovedetails.Alias + "@tenant.mail.onmicrosoft.com" New-MailContact -DisplayName $distrotomovedetails.DisplayName -Alias $distrotomovedetails.Alias -ExternalEmailAddress $ExternalEmailAddress -OrganizationalUnit "OU=Contacts,OU=Exchange Accounts,DC=lge-mdr,DC=com" Set-MailContact -Identity $distrotomovedetails.PrimarySmtpAddress -HiddenFromAddressListsEnabled $true #Close Session with OnPremise Exchange Get-PSSession | Remove-PSSession -Verbose #Connect EXO Import-Module ExchangeOnlineManagement Connect-ExchangeOnline -Credential $UserCredential Write-Host -ForgroundColor Cyan "Running AAD Connect Sync" #Remotely Run AAD Connect Delta Sync $session = New-PSSession -ComputerName FQDNAADServer.com Invoke-Command -Session $session -ScriptBlock {Import-Module -Name 'ADSync'} Invoke-Command -Session $session -ScriptBlock {Start-ADSyncSyncCycle -PolicyType Delta} Remove-PSSession $session #Pause until AAD Connect Sync can run and Azure AD to catch up Write-Host -ForgroundColor Green "Pausing for AAD Connect to run and then we check EXO for the group" function Start-Sleep($seconds) { $doneDT = (Get-Date).AddSeconds($seconds) while($doneDT -gt (Get-Date)) { $secondsLeft = $doneDT.Subtract((Get-Date)).TotalSeconds $percent = ($seconds - $secondsLeft) / $seconds * 100 Write-Progress -Activity "Sleeping" -Status "Sleeping..." -SecondsRemaining $secondsLeft -PercentComplete $percent [System.Threading.Thread]::Sleep(500) } Write-Progress -Activity "Sleeping" -Status "Sleeping..." -SecondsRemaining 0 -Completed } Start-Sleep 200 Write-host -ForegroundColor Cyan "Verifing the group is gone from Azure AD, command will proceed once group is not showing in EXO" $distrotomovedetails.PrimarySmtpAddress while($GetDistroFinder -ne $null){ Write-Host -ForegroundColor Cyan "Checking EXO for the old synced Distro Group" $GetDistroFinder = Get-DistributionGroup -Identity $distrotomovedetails.PrimarySmtpAddress Start-Sleep 10 } Write-Host -ForegroundColor Green "Group Appears to have been removed please verify on Azure AD" Read-Host -Prompt "Hit Enter To Proceed" ############ Recreate Group in the cloud ########################################################################################################################################### Write-Host -ForegroundColor Cyan "Recreating the group in EXO" #Create the EXO Distro group New-DistributionGroup -DisplayName $distrotomovedetails.DisplayName -Alias $distrotomovedetails.Alias -Name $distrotomovedetails.Name -MemberDepartRestriction $distrotomovedetails.MemberDepartRestriction -MemberJoinRestriction $distrotomovedetails.MemberJoinRestriction -PrimarySmtpAddress $distrotomovedetails.PrimarySmtpAddress -RequireSenderAuthenticationEnabled:$false -ManagedBy $Managedby.UserPrincipalName -Verbose #Add additional SMTP and x500 addresses to the group. Write-Host -ForegroundColor Green "Adding SMTP and x500 Addresses Back to" $distroprimarysmtp $EmailAddressesToAdd = Import-Csv .\SingleDistroGroupsmtpaddresses.csv $EmailAddressesToAdd | ForEach-Object { $AliasOfGroup = $_.Alias $AddressToAdd = $_.EmailAddress $x500 = "x500:"+ $distrotomovedetails.LegacyExchangeDN $GroupLookup = Get-DistributionGroup -Identity $AliasOfGroup Set-DistributionGroup -Identity $GroupLookup.Identity -EmailAddresses @{Add=$AddressToAdd} -Verbose } #Add Group Members Back to Group Write-Host -ForegroundColor Magenta "Adding Members Back to Group" $GroupMembers = Import-Csv .\GroupmembersBeforeDel.csv $GroupMembers | ForEach-Object { $Distroprimarysmtp = $_.DistributionGroup $MemberPrimarySMTP = $_.EmailAddress Write-Host "Adding $memberprimarysmtp to $Distroprimarysmtp" Add-DistributionGroupMember -Identity $Distroprimarysmtp -Member $MemberPrimarySMTP } #Confirm Group Get-DistributionGroup $distroprimarysmtp | Select Alias,PrimarySmtpAddress,ManagedBy,IsDirsynced,WhenCreated | FT }
- andydextrusCopper Contributor
Thank you for sharing this script Gregory Hall. I'm pretty much new to this as well.
In line with BrentPirolli's questions, please from where should this be run, Exchange on-prem or EXO? I shall also appreciate if you can please point out any customizations that need to be done to the script before running it in a new environment.- paul_sexstoneCopper Contributordid you get it working?
- Marine_RogCopper ContributorHi All,
I'm on Exchange Hybrid mode at the start of the Ex Migration.
We are starting to syncronize all DL On Premises to Online before migrate mailbox people to Online.
But we have a lot of request user to create new DL.
We try to create them directly on the Ex Online and we have doing a contact on the On Premise server.
But People who are still have mailbox on the On Premises server can't send email to this new DL created on the Online.
Can you help me please ?
Thanks in advance !- KevSH1967Copper Contributor
Marine_Rog We're having the same issue. How were you able to resolve it?
- BrentPirolliCopper Contributor
Gregory HallThis is pretty incredible! thanks for sharing. For a relative rookie at this, what of your script needs customized for my environment? I see a line referencing:
http://ExhangeServerFQDN.com/PowerShell/
which I'm guessing needs my URL... and
"@tenant.mail.onmicrosoft.com"
which I'm guessing I need to fill in with my tenant name in place of "tenant"... correct? Are there other areas?
Also... seeing this kicks off referencing:
$ListOfGroupsToMigrate = Import-csv .\GroupMigration.csv
Is there a specific script/format you are using to get that GroupMigration.csv or are you just pulling from something like:
$groups = get-adgroup -filter { (GroupCategory -eq 'Distribution') } -properties mail, name
$groupscsv = "C:\logs\GroupMigration.csv"
$groups | export-csv -Path $groupscsv
To make sure that I understand what you want to do: you have a set of DLs on-premises and you now want to move them to Exchange Online without changing any of the members. You don't want to create new Office 365 Groups based on the DLs - you just want to use the DLs as they are.
Do you have a hybrid environment? If so, you can use AADConnect to synchronize the on-premises DLs with AAD so that they appear in the cloud GAL. The groups will have to be managed on-premises because they are "owned" by that environment.
If you don't want to synchronize the groups, you can recreate the DLs in the cloud. This is a manual process as there is no tool that I know of to move a DL from one environment to the other. However, it's easy enough to use PowerShell to export DL membership to something like a CSV file (on-premises) and then read that CSV file to create the DL in the cloud.
- Christian BruyereCopper Contributor
Hello TonyRedmond,
Sorry, your answer is quite old now but I have a question regarding the situation.
I'm in a similar situation with the customer here:
- Made a hybrid deployment to migrate from Exchange 2010 to Office 365;
- Have AAD Connect (and ADFS)
- Migrated almost all users (and shared mailboxes, etc.)
- The end goal is to shut down the Ex10 server since I've told uninstalling it is a terrible idea. We'll be managing things like SMTP Addresses from the AD Attribute Editor.
Now about the DGs: We've sync'ed them all with AADC. We can see them in o365 AC and ExO but not manage them as expected.
If I would like to manage them there, can I simply remove the DG OU from AADC Sync?
EDIT: Thinking about it, I've realized doing what I've described above should change the ID of the DG since it isn't a migrated object but a created one. Therefore, I suppose people who use Autocomplete (meaning everybody ^^) will be screwed.
Users will have to get the DG from the Online GAL, correct?
Thanks in advance for your answer
If you have a bunch of DLs on-premises, you will need to:
1. Recreate them in the cloud.
2. Delete them on-premises.
If you don't do this, you'll end up having to keep an on-premises server to manage the objects that remain there. It is easy enough to script the recreation and deletion with PowerShell. You'll need to have a session connected to both on-premises and the cloud when you run it.
- SimarCopper Contributor
Can you please elaborate more on, "how to export DL (on-premises) and then create in Cloud using PowerShell"?
Thanks!
Use the Get-DistributionGroupMember to get the membership of the on-prem DL and then create the new DL inside Exchange Online (a cloud object) before populating its membership (Add-DistributionGroupMember) with the objects you fetched from the original DL. You can connect PowerShell to both the on-prem and cloud environments at the same time, so you can run these commands in a single session.