Migrating On-Prem Distribution Groups to Exchange Online in Hybrid Mode

Copper Contributor

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.

47 Replies

@Tony Redmond Is this still valid? We are in hybrid scenario for last few year and we have almost moved all mailbox and DL are dir sync. is there any better option available to move DL to EXO only?

@Bhavesh Shah  Nothing much has changed in the last few years.

@djreit  I am going to try your method and see if this works for me. I'm at the same spot as everyone else on this thread. Please don't let this die. This is more involved than it seems. Sorry, I'm a newbie when it comes to MS Exchange so I'm learning this on the fly.

 

We are in a hybrid set up. Trying to take existing DLs on our on-prem environment to O365. While we aren't doing away with our on-prem environment any time soon, DLs must be in the cloud soon so users can have the ability to manage them again as they did prior.

 

Anything learned since the last post is greatly appreciated.

Any lessons learned/ shortcuts/ better ways to do things? Definitely need assistance for sure. Thank you.

@Tony RedmondHi Tony and thanks for all the great info you've provided in this thread.

 

I'm getting ready to do a migration this weekend it looks like (have a Ex2010 Hybrid /O365 setup) and all my user's mb's are already up in the cloud but we have a lot of on-prem DG's.

 

Is there a PS command to pull the list of users for a specific group and save to a CSV?

Get-DistributionGroupMember ???

 

And then the corresponding  PS-online command to create the DG with the CSV list of users?

 

Maybe I missed it above but thanks!

Jason

@Jason-EV 

 

Get-DistributionGroupMember -Identity MyGroup | Select DisplayName, PrimarySmtpAddress | Export-CSV -NoTypeInformation c:\temp\DL.csv

 

Something like that...

@Tony Redmondgreat! thanks for fast reply! I'll give that try right now. By any chance do you know what it is for writing it or the Exchange Online side?

 

If not I'll open a case. Thanks again!

@Jason-EV  It works for either on-premises or online.

This thread has been a big help as I am planning this currently.

 

Would anyone be willing to share their script or know of a script which I can amend and use?

 

I have a hybrid environment with Exchange 2010 and 2016. 2016 installed to setup the hybrid environment. All user & shared mailboxes etc have been migrated from 2010 to Exchange Online (EOL). Eventually I would like to fully retire on-premises exchange. All objects from on-prem to EOL are synced via Azure AD Sync.

 

I am not proficient in PowerShell so any pointers would be a great help.

@Boe Dillard Synced from AD are not eligible. They have to be copied to the cloud and deleted onprem.

@Mark Dobrzynski 

 

 

######## 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
}

 

 

@Gregory Hall 

 

Thanks for sharing your script.

@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

Hi 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 !

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.

@Tony Redmond 

 

Thanks for sharing this information.

We are trying to migrate thousands of DL's based on Business units. I am sure this will be very helpful. Will be trying some test with few DL's and see how that goes.  

@Marine_Rog We're having the same issue.  How were you able to resolve it?

@KevSH1967 

 

did anyone run this scrip sucessfull and want to share any issues/ tips

@paul_sexstone Not at all Paul. I had to manually delete all the DLs from on-prem and then recreated them online eventually.