Table of Contents
Use the Graph APIs to Synchronize Group Memberships
A September 2018 article describes how to synchronize the membership of security groups with Microsoft 365 groups (or Office 365 groups as they were then). Looking at the example code in the article, it’s obvious that much has changed. The demise of the Azure AD PowerShell module and its replacement by the Entra module or the Microsoft Graph PowerShell SDK is probably the biggest change.
A security group is used to manage access to shared resources. It’s often desirable to have a way for members of the security group to share information and collaborate. A Microsoft 365 group is the obvious choice, and that’s what creates the need to synchronize membership between the security group and Microsoft 365 group. The security group is the master, so the processing is to add and remove members of the Microsoft 365 group based on the membership of the security group.
I haven’t looked at the article for a long time and was surprised when a reader asked for an updated solution based on Microsoft Graph APIs. The reader supplied some code that they’d written and hadn’t managed to get working, probably because of the somewhat odd way that the Graph APIs return group membership. It’s just one of the foibles developers should understand when they work with the Graph.
Dealing with Group Membership
The documentation for the List Group Members API shows that member identifiers are returned rather than what you might expect, such as a display name or user principal name. This behavior follows through into the Microsoft Graph PowerShell SDK Get-MgGroupMember cmdlet:
Get-MgGroupMember -GroupId $M365Group.Id Id DeletedDateTime -- --------------- eff4cd58-1bb8-4899-94de-795f656b4a18 53f08764-07d4-418c-8403-a737a8fac7b3 6fd89e40-665a-4efa-9691-da07849cae91 …
Dealing with a set of object identifiers isn’t a problem when you expect to do so. However, if you fetch the additionalProperties property, PowerShell returns a hash table containing the expanded details of the group members.
Get-MgGroupMember -GroupId $M365Group.Id | Select-Object -ExpandProperty additionalProperties
Key Value
--- -----
@odata.type #microsoft.graph.user
businessPhones {}
displayName René Artois
givenName René
jobTitle Chief
mail Rene.Artois@office365itpros.com
officeLocation Nouvon
surname Artois
userPrincipalName Rene.Artois@office365itpros.com
@odata.type #microsoft.graph.user
businessPhones {}
displayName Otto Flick
givenName Otto
jobTitle Head of Department
mail Otto.Flick@office365itpros.com
officeLocation Nouvion
surname Flick
userPrincipalName Otto.Flick@office365itpros.com
@odata.type #microsoft.graph.user
businessPhones {+33 4 94301766}
displayName Lotte Vetler (Paris)
givenName Lotte
jobTitle Property Consultant
mail Lotte.Vetler@office365itpros.com
mobilePhone +33 6 7446 1554
officeLocation Flayosc
surname Vetler
userPrincipalName Lotte.Vetler@office365itpros.com
Note that the table doesn’t include the identifier for each user. However, if you create a hash table from additionalProperties, code can use it as a lookup table to extract details for a user identifier. Here’s an example of using the hash table to find the display name for a group member:
[array]$Members = Get-MgGroupMember -GroupId $M365Group.Id
$GroupMembers = $Members | Select-Object -ExpandProperty additionalProperties
[int]$i = 0
ForEach ($M in $Members.Id) {
Write-output ("Processing member {0}" -f $GroupMembers[$i].displayName)
$i++
}
Processing member René Artois
Processing member Otto Flick
Processing member Lotte Vetler (Paris)
Coding a Solution
Once we’re clear about how Graph APIs return group membership, it’s easy to proceed to code and create a replacement script. Unlike the original, which used both the Azure AD and Exchange Online modules, I chose to do everything with the Microsoft Graph PowerShell SDK to avoid the potential for irritating assembly clashes. The code is enhanced a tad with additional checks to make sure that the right kind of groups are involved and to record the additions and removals for the membership of the Microsoft 365 group (Figure 1).
You can download the full script from the Office 365 for IT Pros GitHub repository.
Synchronizing Group Membership Automatically
The technique used in the script is not limited to synchronizing between a security group and a Microsoft 365 group. It could also be used to synchronize the membership of two Microsoft 365 groups or two security groups.
Processes like this are well suited to execution as a scheduled Azure Automation runbook. Running the synchronization job weekly seems like the correct cadence, but I’ll leave it to you to decide.
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.

