Table of Contents
Microsoft Graph API Result Size Limit
PowerShell scripting with the Microsoft Graph API is a nice thing because you can access some pieces of information that you cannot gather elsewhere with scripting. If you never did it, please visit the detailed blog post which describes Graph Scripting for Beginners.
What has always annoyed me is Graph’s habit of only returning a maximum of 100 objects for standard queries. This applies, for example, when you query the list of user accounts, but also for querying the members of a group. Or events in the audit log (1000). So, if you’re just doing this, don’t be surprised that your result list is only complete in tiny environments:
Microsoft describes this paging technique is here: https://docs.microsoft.com/en-us/graph/paging. There are different limits depending on the API. Below is an extract from the paging doc.
Paging behavior varies across different Microsoft Graph APIs. Consider the following when working with paged data:
- Different APIs might have different default and maximum page sizes.
- Different APIs might behave differently if you specify a page size (via the
$top
query parameter) that exceeds the maximum page size for that API. Depending on the API, the requested page size might be ignored, it might default to the maximum page size for that API, or Microsoft Graph might return an error. - Not all resources or relationships support paging. For example, queries against directoryRoles do not support paging. This includes reading role objects themselves as well as role members.
When we run a query, and the results are under the limit there will be two objects returned. If we use count on the $results variable it returns 100 items.
$url = 'https://graph.microsoft.com/v1.0/users'
$userResults = Invoke-RestMethod -Method GET -Headers $headers -Uri $url
$results = @()
$results += $userResults.Value
PS C:\> $results.Count
100
Specify a page size: Adding a larger page size to the request. The API for user objects in Microsoft Graph allows us to set a page size of 999 max:
$url = 'https://graph.microsoft.com/v1.0/users?$top=999'
$userResults = Invoke-RestMethod -Method GET -Headers $headers -Uri $url
$results = @()
$results += $userResults.Value
PS C:\> $results.Count
999
Please note also that some other APIs of Graph may differ in terms of the maximum page size. Or in their behavior, if you specify a page size that is too large. Either the page size is automatically reset to the permitted maximum, or the request comes back with an error, or (worst case) no error is thrown, and no result is returned. So, choose the $top value carefully.
Getting it all – Paged Result Queries
The ultimate solution for this problem is this easy. Use the @odata.nextLink property in the result – if it not empty, then there are more result entries to read. Fortunately, the content of this @odata.nextLink is just the URL that you use in the looped next query. Do this until @odata.nextLink is empty. Then you have the complete list!
For instance, we call the /users endpoint in Graph Explorer. By default, the output shows first 100 users only. The output shows @odata.nextLink, we can call that link to get information of the next 100 users.
When followingthe nextLink, at the end, we see no more nextLink. It means the lasts results are being shown.
The following script is a basic example for paged results: Getting the complete user list. It’s starting with business as usual, but then we loop the GET request as long as there is no more data left. In the loop, we fill our result array. You can use this method with every other Graph request that you want to perform with paging:
$results = @()
$url = 'https://graph.microsoft.com/v1.0/users'
while ($null -ne $url ) {
$userResults = Invoke-RestMethod -Method GET -headers $headers -Uri $url
$results += $userResults.Value
$url = $userResults.'@odata.nextLink'
}
PS C:\> $userList.Count
1329
When we do the above script the $results count go up to 1329.
Getting it all – Graph PowerShell SDK
If using the Microsoft Graph PowerShell SDK, the process is much simpler. Once connect to using Connect-MgGraph we can then run:
Get-MgUser -All
PS C:\> (Get-MgUser).Count
100
PS C:\> (Get-MgUser -All).Count
1329
As you notice, the first command without the -All parameter applies the default limit and the output show only 100 items.
Another example for get Signin logs
Microsoft Graph seems to have a limit of 1000 results for signin logs, when the limit is reached graph will then start paging the result and adding them to @odata.nextLink property.
$LoginUrl = "https://graph.microsoft.com/v1.0/auditLogs/signIns"
$LoginResults = Invoke-RestMethod -Headers $headers -Uri $LoginUrl -Method Get
$Results = @()
$Results += $LoginResults.value
PS C:\> $Results.count
1000
$results = @()
$LoginUrl = "https://graph.microsoft.com/v1.0/auditLogs/signIns"
while ($null -ne $LoginUrl ) {
$LoginResults = Invoke-RestMethod -Method GET -headers $headers -Uri $LoginUrl
$results += $LoginResults.Value
$LoginUrl = $LoginResults.'@odata.nextLink'
}
PS C:\> $Results.Count
190615
Same with Graph PowerShell SDK.
PS C:\> (Get-MgAuditLogSignIn).Count
1000
PS C:\> (Get-MgAuditLogSignIn -All).Count
190719
Be aware of throttling
If the response to a request is a 429 error, this indicates that throttling has occurred. The response header will contain a value called Retry-After which indicates the number of seconds to wait before retrying the call. Wait the requested number of seconds, then try the call again, if it fails again on the 429 error, get the new value for Retry-After and continue to wait. More details can be found in the article https://docs.microsoft.com/en-us/graph/throttling.
Not a reader? Watch this related video tutorial: