How to Synchronize AAD Security Groups with Microsoft 365 Groups

Exploiting Security Groups

Dan Stephenson, one of the Teams program managers, posted an interesting script to synchronize the membership of an AAD security group with an Office 365 group (now called Microsoft 365 Groups). The idea is that you might have invested in security groups to control access to different resources and now want to extend that investment and use the same group membership for collaboration with Teams.

PowerShell is Flexible

One of the wonders of PowerShell is the way that you can come up with different answers to the same problem. Everyone has their own way to attack a problem and code a solution. Here’s the script we include in Chapter 14 of Office 365 for IT Pros, where we deal with the many joys of managing Office 365 Groups and Teams with PowerShell.

The script synchronizes the membership of a security group called eDiscovery Admins with an Microsoft 365 Group called eDiscovery Administrators. The security group is the master, meaning that its membership is what we want to see synchronized to the Office 365 Group. Any members found in the Microsoft 365 Group membership that are not in the security group are removed. You need to connect your PowerShell session to Azure AD and Exchange Online (use the Exchange Online Management module) to access the cmdlets used in the script.

Synchronizing Group Memberships

First, we fetch details of the two groups we want to synchronize.

$M365Group = (Get-UnifiedGroup -Identity "eDiscovery Administrators")
$SecurityGroup = (Get-AzureADGroup -SearchString "eDiscovery Admins").ObjectId
# Grab list of security group members
$SecurityGroupMembers = (Get-AzureADGroupMember -ObjectId $SecurityGroup -All $True | Select UserPrincipalName, UserType)

Now we update the membership of the Office 365 Group based on the members of the security group.

ForEach ($i in $SecurityGroupMembers) {
If ($i.UserType -eq "Member") {
        Add-UnifiedGroupLinks -Identity $M365Group.ExternalDirectoryObjectId -LinkType Member -Links $i.UserPrincipalName }
}

The next step is to check the membership of the two groups and remove any member found in the Microsoft 365 Group who doesn’t exist in the security group.

$GroupMembers = (Get-UnifiedGroupLinks -Identity $M365Group.ExternalDirectoryObjectId -LinkType Member)
ForEach ($i in $GroupMembers) {
 $Member = (Get-Mailbox -Identity $i.Name)
 If ($SecurityGroupMembers -Match $Member.UserPrincipalName)
      { Write-Host $Member.DisplayName "is in security group" }
    Else
      { Write-Host "Removing" $Member.DisplayName "from Office 365 group because they are not in the security group" -ForeGroundColor Red
      Remove-UnifiedGroupLinks -Identity $M365Group.ExternalDirectoryObjectId -Links $Member.Alias -LinkType Member -Confirm:$False}
}
Write-Host "Current Membership of" $M365Group.DisplayName
Get-UnifiedGroupLinks -Identity $M365Group.ExternalDirectoryObjectId -LinkType Member | Select DisplayName

The next step for the budding PowerShell maestro to improve matters is to deal with nested groups (an exercise for the reader), improve error handling, and  come up with a way to run the script every day or so to ensure that the two group memberships remain synchronized.


We have a complete chapter (13) on using PowerShell in the Office 365 for IT Pros eBook. Not that you experts need to read it, but it is nice to know that the chapter is there.

3 Replies to “How to Synchronize AAD Security Groups with Microsoft 365 Groups”

  1. It should be

    $SecurityGroupMembers = (Get-AzureADGroupMember -Top 500 -ObjectId $SecurityGroup.ObjectId | Select UserPrincipalName, Usertype)

    and

    If ($i.UserType -eq “Member”) {

    1. Thanks. I guess some things changed between then and now. In any case, I’ve refreshed the post to reflect your suggestions and some other tweaks of my own.

  2. As per Sebus, if the group membership is more than 100 you need to add the -top switch or the -All $true i.e
    $SecurityGroupMembers = (Get-AzureADGroupMember -ObjectId $SecurityGroup -All $true | Select UserPrincipalName, UserType)

Leave a Reply

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