Microsoft introduced (Entra ID) Group insights in the beginning of the year. Still in public preview, the feature provides “at a glance” dashboard to highlight some common scenarios, as well as more detailed views in each supported category. While there is nothing revolutionary in it, and it does not address some of the most asked for questions (“where is this group used” or “when was this group last used”), any new reporting/insights/analytics functionality is a good thing, especially those that come with proper API support.
Speaking of which, the Group insights feature is one of the rare instances where Microsoft actually follows their promise for Graph-first approach to new products. While the release itself went under the radar, without any documentation on the API front, a quick browser trace was enough to reveal the underlying calls to the Graph API that powered it (see for example this article by Tony, or this one by Jan Bakker). The corresponding documentation was finally released at the end of June, so it is now time to cover the corresponding endpoints in more detail.
As this is still a preview functionality, you can find the corresponding method under the /beta branch of the Graph API. Two methods are currently supported: LIST (/reports/identityAnalytics/groups) is the primary one you’d use, giving you the set of (supported) groups and analytics for each entry; whereas the GET method (/reports/identityAnalytics/groups/{id}) can be used to fetch the analytics for specific group. Working with either of them requires the Reports.Read.All permission, in either application or delegate flavor.
Here is a simple example of using the /reports/identityAnalytics/groups endpoint. The query below will fetch the full set of groups (supported by Entra ID), along with the properties exposed by the Group analytics feature. In large tenants, multiple pages of output will have to be handled, via the standard pagination mechanism.
#Fetch the Group insights GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups #Include the count GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$count=true
The output of the above query will contain an entry for each (supported) Entra ID group object, including soft-deleted ones. For each entry, a set of predefined properties is returned. The list includes some “calculated” values, which would otherwise require additional calls to the Graph API, per object. On the other hand, you might find that some “common” properties are missing compared to a “standard” Graph API group object, for example any of the mail-related ones. You can find the full list of supported properties in the official documentation.
The biggest benefit of having a proper documentation is not only seeing the full set of properties currently supported for the Group insights feature, but also knowing which of them supports filtering. In fact this is one of the rare cases where the team has done a good job and every property support the $filter operator. The only outlier seems to be the groupType one, which gives is information whether the group object in question is cloud-created, synced from on-premises AD or soft-deleted.
So, let’s examine some filter examples. We will skip the ones that you can readily get via the UI and focus on few less obvious ones. For example, one of the recommendations to ensure a proper lifecycle of Microsoft 365 Groups is to have at least two owners assigned and in most cases you want these to be “member” users. While you can leverage the existing Graph methods to get a list of such groups, you will have to write a rather complicated query. The insights endpoint makes it very easy:
#Filter Microsoft 365 Groups with two or more member users as owner GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=isCloudM365Group eq true and memberOwnerCount ge 2&$count=true #Filter Microsoft 365 Groups with less than two member users as owner GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=isCloudM365Group eq true and memberOwnerCount lt 2&$count=true
Special attention should be paid to role assignable groups, that is groups that support the assignment of administrative roles. The insights endpoint allows you to quickly filter such groups, as well as apply additional filters based on their membership. For example, you might not want to have any guest as member, or any nested groups.
#Filter role-assignable groups GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=assignedRoleCount ge 1&$count=true #Filter role-assignable groups with guest members GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=guestTransitiveUserCount gt 0 and assignedRoleCount ge 1&$count=true #Filter GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=guestTransitiveUserCount gt 0 and directGroupMemberCount eq 0 and assignedRoleCount ge 1&$count=true
Do note that the query above will only return groups with at least one directory role assigned and as least one member in the membership list. Groups with no current members or current role assignments are omitted.
Another scenario where the insights can help is the overall lifecycle of the group. While they are unfortunately not able to get us an answer to questions like “is the group active”, “where was the group last used” or “where across Microsoft 365 or Azure services is the group used”, they can still be useful for evaluating the group’s current state as part of a renewal or deprovision workflow. For example:
#Expired groups with no owners GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=groupExpirationDateTime le 2026-07-01T00:00:00Z and memberOwnerCount eq 0&$count=true #Groups that will expire soon and have no members GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=groupExpirationDateTime ge 2026-08-01T00:00:00Z and directGroupMemberCount eq 0&$count=true
Above, we mentioned “current state” of the group. It is important to understand that the group insights are not generated in real time, and can lag few days behind the actual data in the directory. This in turn can result in mismatches between counts or the properties themselves. Make sure to always check the value of the calculatedDateTime property in the output. And to reiterate again, the output includes soft-deleted groups by default, if you want to filter out such entries, you can make use of the isValidGroup property.
#Filter out soft-deleted groups GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=isValidGroup eq true$count=true #List soft-deleted groups with more than one member GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups?$filter=isValidGroup eq false and memberOwnerCount ge 1&$count=true
While we did not include it in any of the above examples, the LIST method also supports the $select operator, which can be used to fetch only the properties you are interested in, thus slightly improving performance of the endpoint.
Apart from the LIST method, the insights API supports a per-group GET method. This can be useful in larger organizations, where fetching the full list of insights can take substantially more time than querying individual entries. The robust filtering support we examined above does help on that front, but it’s not a Hail Mery. Moreover, the GET method uses the exact same identifier as the Graph’s GET /groups one even though the id property is being referenced as groupAnalyticsId within the examples in the official documentation. Here’s an example:
GET https://graph.microsoft.com/beta/reports/identityAnalytics/groups/528a4052-fa6c-4495-b39f-2820f8e1e8db
As a side note, the GET method does not seem to support the $select operator, but given the number of properties we get in the output, it’s not that big of a deal. The $count operator is also supported, and we’d recommend adding it to your queries, as it’s an easy way to get the number of results via the @odata.count facet.
And that in a nutshell is how to work with the group insights endpoints on the Graph API. The two supported methods are as easy as it gets to work with. The output itself exposes few properties that might otherwise require additional or complicated queries, and combined with the robust filtering support, can be useful for variety of scenarios.
That said, there are some limitations you need to be aware of, starting from the fact that not every group type is supported. A surprise to no one used to working with Entra ID for sure, but it is worth mentioning that ExO’s dynamic distribution groups will not be present in the output. The data itself can lag behind a day or more, so make sure to account for that. And since we are using a separate endpoint, unrelated to the /v1.0/groups one, we can’t leverage relationships and navigational properties to “enrich” the output, without making additional queries.
With that said, having more reporting/analytics/insight endpoints is always a good thing, and Microsoft has done a good job on the API front with this one. Perhaps in the future they will expand the output with additional properties and even better filtering support (we cannot use the not operator currently). Time will tell.