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
That's the Microsoft way - delete and do it again

@Mark Dobrzynski 

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.

@Tech_Team_Blues 

 

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

@Tech_Team_Blues 

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

# Get the group details from Active Directory
$group = Get-ADGroup -Filter "mail -eq '$SingleGroupToMigratePSMTP'"
$groupMembers = Get-ADGroupMember -Identity $group | Get-ADUser | Select-Object DisplayName, UserPrincipalName

# Export group details
$group | Select-Object Name, GroupScope, GroupCategory | Export-Csv .\singlegroupdetailsbeforedelete.csv -NoTypeInformation

# Export group members
$groupMembers | Export-Csv .\GroupmembersBeforeDel.csv -NoTypeInformation

# Managed by details
$groupOwner = (Get-ADGroup $group -Properties ManagedBy).ManagedBy
$Managedby = Get-ADUser -Identity $groupOwner

# Connect to Exchange Online
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -Credential $UserCredential

# Recreate the group in Exchange Online
$newGroupParams = @{
DisplayName = $group.Name
Alias = $group.SamAccountName
Name = $group.Name
PrimarySmtpAddress = $group.mail
RequireSenderAuthenticationEnabled = $false
ManagedBy = $Managedby.UserPrincipalName
}
New-DistributionGroup @newGroupParams -Verbose

# Add group members in Exchange Online
foreach ($member in $groupMembers) {
Add-DistributionGroupMember -Identity $group.mail -Member $member.UserPrincipalName
}

# Verify group creation
Get-DistributionGroup $group.mail | Select Alias,PrimarySmtpAddress,ManagedBy,IsDirsynced,WhenCreated | Format-Table
}

 

Hi Tony,
How do we create a dynamic group? we don't want to update manually.

Setup: Hybid Mode| EXO | Cloud Sync |Azure P2
Can we create Dynamic Group base on the rule?