Blog Post

Core Infrastructure and Security Blog
6 MIN READ

How-To Sync EntraID Group Memberships Into Any System

jonasoh's avatar
jonasoh
Icon for Microsoft rankMicrosoft
Mar 14, 2025

A guide on how to synchronize EntraID group memberships into any system

Hi, Jonas and Roland here!

Or as we say in the north of Germany: "Moin Moin!"

We recently worked on a mechanism to synchronize EntraID group memberships into an Azure SQL table and would like to share our learnings.

If you haven’t seen my other articles yet, feel free to check them out here: https://aka.ms/JonasOhmsenBlogs

Challenge

We needed a fast way to check if a device or a user is  member of one or multiple EntraID groups for an application we were building.
Our application will have a list of EntraID groups we periodically would need to check the memberships for. The number of groups will vary depending on how the app is used.
The app should not have a hard limit for the maximum number of groups, so our solution needs to scale.
Another main requirement of the application is a fast response time.
With that in mind, we looked at different options to check EntraID group memberships.
In this article we would like to explain our thought process and the solution we used at the end.

Options available

There are different options available to check if an EntraID object is a member of a group. Each option has its advantages and disadvantages.

Option #1: “Get Members”

Get all the members of a group and check if a given object is part of that list.
Either one of the below graph calls will return the list of members.
While “transitive” means the call will also get the members of nested groups.

GET: https://graph.microsoft.com/v1.0/groups/<GroupID>/members
GET: https://graph.microsoft.com/v1.0/groups/<GroupID>/transitiveMembers

NOTE: Use Graph Explorer to test the call in your environment: https://aka.ms/ge

Conclusion

We decided against this method, because we would need at least one graph call for every group in the group list of our application. We would also need to implement paging to get the full membership list for groups with more than 1000 members. This would further increase the number of graph calls and would slow down app response time in large environments.

Option #2: “checkMemberGroups”

Another option is to use the “checkMemberGroups” function of a given EntraID object.
We can pass a list of group IDs to the function to get only the ones back the object is a member of.

In the example below we POST a list of group IDs to the “checkMemberGroups” function of a device. In return we get a list of group IDs the device is a member of. Which in our case are two groups.

POST: https://graph.microsoft.com/v1.0/devices/<DeviceID>/checkMemberGroups

NOTE: Use Graph Explorer to test the call in your environment: https://aka.ms/ge

This method is quite fast but limited to 20 group IDs per call. (Limit at the time of writing)
That means, if we need to check more groups we would need more graph calls.

Conclusion

Even though this method works quite fast, we also decided against it. We anticipated a lot of such calls from our application, and we would need to take more than 20 groups into account. We also must consider the possibility that an Entra ID object might be just a member of one group, while we have a list of hundreds of groups to check against. This would create unnecessary graph calls.
All those factors would slow down application response time.

Option #3: “Delta Pull”

The third option is to use a delta query to track changes in Microsoft Graph data.
For groups the delta query would look like this:

GET: https://graph.microsoft.com/v1.0/groups/delta/?$filter= id eq '<GroupID>'

NOTE: Use Graph Explorer to test the call in your environment: https://aka.ms/ge

We filter by group ID to track the changes for the particular group.
(At the time of writing the filter can have a maximum of 50 group IDs)

If we run the get request from above with a group ID, we will get a member list and an “@odata.nextlink”. (Two devices are member of the example group)

We then must “follow” the “@odata.nextlink” to get all the members of the group until we get no members back (empty “value”), but an “@odata.deltaLink”:

We can then use the “DeltaLink” later in time, to get the changes that happened between our initial call and the use of the “DeltaLink”.
In the below example one device was removed (red) and another one was added (green) to the group:

We then repeat the process with the new “DeltaLink” to track changes over time.

So, in summary, the following steps are required for the delta pull method:

  1. GET the delta group resource from Microsoft Graph
  2. Follow each next link until we have a delta link
  3. Store the delta link
  4. Use the delta link later in time to get a list of changes made to the group members

Conclusion

This method is ideal for our scenario, but it also means that we need to build some logic (Logic App, Azure Automation or Function App etc.) to keep the group memberships in our Azure SQL database in sync and schedule the sync process.

Option #4: “Delta Notification”

The fourth option is to get notified when changes happen.
For this to work we need to create a subscription to subscribe to change events for an Entra ID object. In our case a group.
We do this with the following statement:

Request-body properties explained:

  • ChangeType: What type of event we want to subscribe to.
  • NotificationUrl: Where we want to receive the notification from Microsoft Graph.
    This could be an Azure Function App we own and maintain.
  • LifecycleNotificationUrl: Where we want to receive service notifications from Microsoft Graph about the subscription itself. (not mandatory) (This could be an Azure Function App we own and maintain)
  • Resource: Entra ID object we are interested in. In our case a group ID
  • ExpirationDateTime: When the subscription will expire. 
  • ClientState: A string we can validate each time we receive a notification to make sure the notifications are coming from our subscription.

The subscription endpoint will only “activate” the subscription if the “NotificationUrl” (and if applicable our “LifecycleNotificationUrl”) are available.
They also must accept the first notification and reply with the extracted string called “ValidationToken” to the subscription service.
If that works, we should get a subscription ID back.

We can now make changes to the group we subscribed to.
The subscription service will then send notification to our “NotificationUrl” as shown below.
Notice the “ClientState” value, the group ID (id) and the changes in the green box.
We see one object added and one removed.

 

We can now consume the change info and update our system to keep the group memberships in our system up to date.

So, in summary, the following steps are required for the subscription notification method

  1. We need to have a notification endpoint to receive change notifications. This could be an Azure Functions App
  2. We POST a subscription request to the subscription endpoint
  3. The subscription endpoint will test our notification endpoint by sending a “ValidationToken”
  4. Our notification endpoint needs to reply with the “ValidationToken” to “activate” the subscription
  5. The subscription endpoint will then send us changes made to the group
  6. The subscription needs to be renewed before the “ExpirationDateTime” is reached to continue to function. (Maximum value is 29 days)

Conclusion

This would be the fastest way to synchronize group memberships into our Azure SQL database.
But we also need to build some logic (Logic App, Azure Automation or Function App etc.) to receive change notifications and act on them.

 

 

We explored the technical details of option four in more detail in How-To Use Graph Object Change Notifications
Another article about option three will follow. So, stay tuned...
We hope you enjoyed our article. Let us know in the comments.

Stay safe!

Roland Spindeler and Jonas Ohmsen

 

Sources

The following sources describe each method in more detail.

Option #1 source:
https://learn.microsoft.com/en-us/graph/api/group-list-members

Option #2 source:
https://learn.microsoft.com/en-us/graph/api/directoryobject-checkmembergroups

Option #3 source:
https://learn.microsoft.com/en-us/graph/api/group-delta
https://learn.microsoft.com/en-us/graph/delta-query-groups

Option #4 source:
https://learn.microsoft.com/en-us/graph/change-notifications-overview

Updated Apr 15, 2025
Version 4.0

1 Comment

  • dermicro's avatar
    dermicro
    Copper Contributor

    Mensch Jonas, bin grad über einen Blogeintrag von dir gestolpert und dann ist mir mal bewusst geworden das wir uns ja schon 9 jahre nicht mehr "gehört" haben :) Das letzte Mal in Zürich, Döner für 30CHF :D

    Alles andere via "in" :)

    Gruess Mirco