Synchronize Mail-Enabled Public Folders to Distribution Groups in Office 365

In an Office 365 hybrid deployment, DirSync is typically used to ensure the on-premises and online directories remain synchronized.  DirSync does not however synchronize mail-enabled public folders, so PowerShell scripts must be run periodically to manually publish them to the O365 address book:

What also doesn't synchronize is mail-enabled public folders that are members of distribution groups.  DirSync will synchronize the on-premises group object, but the public folder is not included as a group member.  Therefore, the on-premise public folder does not receive the message when migrated users send email to the distribution group.  The solution is to create a contact which represents the mail-enabled public folder and make it a member of the distribution group so that DirSync can synchronize it.  This can be automated using PowerShell.

Let’s start by setting a few variables for the data files, the SMTP domain, and the target OU where contacts will be created:

$MailFolders = ".\MailFolders.csv"

$MailFolderMembers = ".\MailFolderMembers.csv"

"FolderName,FolderAlias,FolderEmail,GroupName,GroupAlias,GroupEmail" | Out-File $MailFolderMembers

$SmtpDomain = ""

$TargetOu = "OU=PFContacts,DC=contoso,DC=com"

In addition to Exchange Management Shell, the script will require the Active Directory module:

Import-Module ActiveDirectory

If ((Get-Module -Name ActiveDirectory) -eq $null) { Break }

The script will first enumerate all the mail-enabled public folders and export them to a CSV dataset:

Get-MailPublicFolder -ResultSize Unlimited | Select Alias, DisplayName, PrimarySmtpAddress | Export-Csv $MailFolders –NoTypeInformation

Next we will search every distribution group to check if its membership includes a mail-enabled public folder and export the results to another CSV dataset:

$DistributionGroups = Get-DistributionGroup -ResultSize Unlimited

ForEach ($Group in $DistributionGroups) {

$GroupName = $Group.Name

$GroupAlias = $Group.Alias

$GroupEmail = $Group.PrimarySmtpAddress

Get-DistributionGroupMember $Group -ResultSize Unlimited | Where { $_.RecipientType -eq "PublicFolder" } | ForEach {

$MemberName = $_.Name

$MemberAlias = $_.Alias

$MemberEmail = $_.PrimarySmtpAddress

"$MemberName,$MemberAlias,$MemberEmail,$GroupName,$GroupAlias,$GroupEmail" | Out-File $MailFolderMembers -Append } }

Then we need to create a hidden contact which represents the mail-enabled public folder.  However, the mail attributes we need to use for the contact (alias, email address, etc.) are already assigned to the public folder.  Therefore, we need to create the contact using temporary values and forcibly manipulate the AD attributes after creation:

Import-Csv $MailFolders | ForEach {

$ContactName = $_.Alias + "-PFContact"

$ContactAlias = $_.Alias + "-PFContact"

$ContactSmtp = $_.Alias + "-PFContact" + $SmtpDomain

New-MailContact -Name $ContactName -Alias $ContactAlias -ExternalEmailAddress $ContactSmtp -PrimarySmtpAddress $ContactSmtp -OrganizationalUnit $TargetOu

Set-MailContact $ContactAlias -HiddenFromAddressListsEnabled $true

$Contact = Get-ADObject -Filter { MailNickname -eq $ContactAlias }

Set-ADObject $Contact -Replace @{TargetAddress=$_.PrimarySmtpAddress}

Set-ADObject $Contact -Replace @{info=$_.DisplayName} }

And finally, we replace the public folder object found in each distribution group with the corresponding contact.  Exchange cmdlets don't seem to work for adding/removing public folders, so we'll use AD cmdlets instead:

Import-Csv $MailFolderMembers | ForEach {

$GroupEmail = $_.GroupEmail

$FolderEmail = $_.FolderEmail

$ContactAlias = $_.FolderAlias + "-PFContact"

$GroupDN = "LDAP://" + (Get-ADObject -LDAPFilter "(mail=$GroupEmail)").DistinguishedName

$FolderDN = "LDAP://" + (Get-ADObject -LDAPFilter "(mail=$FolderEmail)").DistinguishedName

$ContactDN = "LDAP://" + (Get-ADObject -LDAPFilter "(mailNickname=$ContactAlias)").DistinguishedName

$Group = [ADSI]$GroupDN

$Folder = [ADSI]$FolderDN

$Contact = [ADSI]$ContactDN


$Group.Remove($Folder.ADsPath) }

After a couple runs, DirSync will synchronize the new contacts and update the distribution group membership in O365.  As always, the script should be tested thoroughly before running against a production environment.  Each task can also be split into separate scripts so that the process can be run against a smaller dataset.  RjZ