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.

See this article for a 2026 revision based on the Microsoft Graph PowerShell SDK.

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.


Need help to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

11 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)

  3. It would be great, been trying to get it, but failing so far. Same logic as above should be just enough

  4. I can get this list of members of the AAD group from Graph:

    # Connect to Microsoft Graph
    Connect-MgGraph -Scopes “User.Read.All”, “Group.Read.All”

    $groupName = “GROUP_ACTUAL_NAME”
    Set-UnifiedGroup $groupName -UnifiedGroupWelcomeMessageEnabled:$false
    $O365Group = (Get-UnifiedGroup -Identity $groupName)

    #Get the AAD Group
    $GroupData = @()
    $AADGroupEmail = “GROUP_EMAIL_ADDRESS”

    #Get the Group
    $Group = Get-MgGroup -Filter “mail eq ‘$AADGroupEmail'”

    #Get Members of the AAD Group
    $SecurityGroupMembers = Get-MgGroupMember -GroupId $Group.id -All

    ForEach ($Member in $SecurityGroupMembers) {
    $User = Get-MgUser -UserId $Member.Id -Property ‘UserPrincipalName’,’UserType’,’Id’,’DisplayName’

    # Process user details
    $GroupData += [PSCustomObject]@{
    UserPrincipalName = $user.UserPrincipalName
    UserType = $user.UserType
    Id = $user.Id
    Name = $user.DisplayName
    }
    }

    $GroupData | Format-table

    and can add these members wot O365 group

    ForEach ($i in $GroupData) {
    If ($i.UserType -eq “Member”) {
    Add-UnifiedGroupLinks -Identity $O365Group.ExternalDirectoryObjectId -LinkType Member -Links $i.UserPrincipalName }
    }

    but fail to compare / remove users

Leave a Reply

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