Best practices, tips and tricks working with Microsoft Graph API in PowerShell

Microsoft Graph API is a RESTful API that allows you to create, read, update, and delete data within your Microsoft account. This tutorial will cover how to use the PowerShell cmdlet New-AzureADUser with parameters to create new users in Azure AD using PowerShell scripts.

The microsoft graph api guid is a Microsoft Graph API that provides access to various Microsoft services. It can be used in PowerShell scripts and it allows you to use the same syntax as other REST APIs.


Microsoft has also launched a blog dedicated to best practices. My best practices sometimes align with Microsoft’s, although they are based on my own thoughts. You don’t agree with one of the best practices or tips? Please leave your comments at the bottom of the page.

Best practices for dealing with Microsoft Graph – Microsoft Graph | Microsoft Docs is a Microsoft-owned best practices blog.

For the Best practices, tips, and techniques for Microsoft Graph Rest API, I’m using my own Optimized.Mga module as an example.

Guidelines for Best Practices

Obtain all data that can be obtained in a single request first.

This is, in my opinion, one of the most critical issues. This results in a considerable performance difference in queries made to the Microsoft Graph API.

Assume you wish to combine two data items into a single report. For example, a client asked whether I could disclose the sizes of the Mailbox and OneDrive folders.

My initial idea is to obtain the Mailbox data, then go through the people to retrieve the OneDrive sizes and put them to a List.

A Bad Case in Point

As a result, the PS script would be as follows:

Connect-Mga -ApplicationID X -Tenant X -ClientSecret X -ApplicationName X -ApplicationName X -ApplicationName X -ApplicationName X -Application $MBXURL = “’D7′)” $MBXURL = “’D7′)” Get-Mga -URL $MBXSizes $MBXurl $FusedDataSources = [System.Collections.Generic.List[System.Object]]::new() foreach ($MBX in $MBXSizes) $MBX in $MBXSizes $MBX in $MBXSizes $MBX in $MBXSizes $MBX in $MBXSizes $MBX in $MBXSizes $null$CurrentSP = $null$CurrentSP = $null$CurrentSP = $ -f $SPURL = “” $SPURL = “” $MBX. Try ‘User Principal Name’. $CurrentSP = Get-Mga -URL $CurrentSP = Get-Mga -URL $CurrentSP = Get-Mga if ($null -ne $CurrentSP) $SPURL $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; @ UserPrincipalName = $MBX.’User Principal Name’ MBSize = $MBX.’Storage Used (Byte)’ ODSize = $CurrentSP.quota.used MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX.’MBX. otherwise $FusedDataSources.Add($Object) $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; @ UserPrincipalName = $MBX.’User Principal Name’ MBSize = $MBX.’Storage Used (Byte)’ ODSize=”No OneDrive found” @ UserPrincipalName = $MBX.’User Principal Name’ MBSize = $MBX.’Storage Used (Byte)’ $FusedDataSources.Add($Object) if $FusedDataSources.Add($Object) if $FusedDataSources.Add($Object) if $FusedDataSources.Add($Object)

Fiddler is what I use to keep track of the requests. Let me make a list of everything:

  • 1 Open Graph and log in.
  • One request for all mailboxes, regardless of size.
  • The size of OneDrive per user was obtained after 999 inquiries.
  • 3 minutes and 8 seconds (measure-command).
  • There are also a lot of problem warnings since not everyone has OneDrive.

Fiddler’s 404 errors are OneDrives that can’t be located. To continue despite the mistake, I had to utilize a try and catch in my script.

This translates to:

  • When you have more users, the queries to Microsoft Graph grow rapidly.
  • This is more slower since each user receives a fresh request.
  • You also reached the limit of the throttle considerably sooner.

Fortunately, my module handles throttling, but you should touch it as rarely as possible.

As an example,

Rather of asking the size of each user’s OneDrive, we first get the sizes of all mailboxes and OneDrives and then use a hashtable to connect them.

This is how the script would go:

Connect-Mga -ApplicationID X -Tenant X -ClientSecret X -ApplicationName X -ApplicationName X -ApplicationName X -ApplicationName X -Application $MBXURL = “’D7′)” $MBXURL = “’D7′)” $SPURL = “’D7′)” $SPURL = “’D7′)” Get-Mga -URL $MBXSize $MBXurl Get-Mga -URL $SPSize $SPURL @ foreach ($user in $SPSize) $SPHash = @ foreach ($user in $SPSize) $SPHash = @ foreach ($user in $S $SPHash.Add($user.’Owner Principal Name’, $user) $SPHash.Add($user.’Owner Principal Name’, $user) $SPHash.Add($user.’Owner Principal $FusedDataSources = [System.Collections.Generic.List[System.Collections.Generic.List[System.Collections.Generic.List[System.Collections .Object]] foreach ($MBX in $MBXSize) ::new() $null$CurrentSP = $null$CurrentSP = $null$CurrentSP = $ $SPHash[$MBX.’User Principal Name’] $CurrentSP = $SPHash[$MBX.’User Principal Name’] $CurrentSP = $SPHash[$MB $CurrentSP if ($null -ne $null -ne $null -ne $null -ne $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; @ MBSize = $MBX.’Storage Used (Byte)’ ODSize = $CurrentSP.’Storage Used (Byte)’ UserPrincipalName = $MBX.’User Principal Name’ $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $FusedDataSources.Add($Object else $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; @ MBSize = $MBX.’Storage Used (Byte)’ ODSize=”No OneDrive found” UserPrincipalName = $MBX.’User Principal Name’ MBSize = $MBX.’Storage Used (Byte)’ $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $Fused

To demonstrate the difference, I ran this script on a client environment where we installed it, and it completed in 28 seconds for 24883 users. I’m not even going to attempt it because of the poor example.

  • 1 Open Graph and log in.
  • 1 request for all mailboxes, regardless of size
  • There have been 1 requests for the OneDrive sizes.

For 24883 users, the runtime was 28 seconds!


  • This implies that there are 24878 fewer requests for 24883 users. The larger these percentages grow, the more users you have in your tenant. This has a significant impact.
  • The script’s runtime is substantially reduced.
  • It’s far less likely that you’ll exceed the throttle limit.

Uri, be as precise as possible with your request.

To avoid throttling, Microsoft has published a table that shows how much each request contributes to the throttling limit. This is where you’ll find the table.

According to the chart, you should be as precise as possible with your request.

1633384783_442_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIOther variables that influence the cost of a request include: Using $select reduces the cost by one.

They imply that you should only ask for the qualities you need.

Take, for example, HR’s request for all users in the tenancy. The List Users request may be used.

You will receive the following attributes when we fetch one user:

1633384783_6_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIProperty values were returned by Microsoft Graph List Users.

There are certain characteristics that are useless to HR. DisplayName, givenName, surname, and email should be enough.

You may filter the properties with the query $select.

With cmdlet, the basic URL would be like follows:

‘’ Get-Mga

After the basic URL, queries are added. Always begin with a ‘?’ and then write “$select=’Property’, ‘Property2′” when creating the first query. (Instead of ‘?’, you use & when adding a second query like ‘$filter.’)

So, for this request, it should be like follows:

Get-Mga -URL ‘$select=displayName,givenName,surname,mail&$select=displayName,givenName,surname,mail&$select=displayName,givenName,surname,mail&$select=displayName,givenName,sur

Only these attributes are returned when we run Get-Mga with the new URL:

1633384784_366_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIGet-Mga -URL ‘$select=displayName,givenName,surname,mail’

This reduces the throttle limit by one count. This will make a big impact if we run it for 24883 people.

When you have a lot of requests, use batch requests.

If you need to add many users or modify a property for a group of users, you may use the Post or Put methods, or a Batch request.

A Batch request allows you to submit many requests at once, but it only counts as one request. This has the following advantages:

  1. It is much quicker than a standard put- or Post-Mga.
  2. You’re less likely to exceed the speed limit.

You may still use a throttle, and the batch request can only handle up to 20 requests at once.

Batch-Mga is here to help.

I used Batch-Mga to process everything. There are no limits to what you can do with this cmdlet, and you could theoretically create 10,000 users with it.

This is something I’ve done previously as an example for Github. For additional information, visit Optimized on Github. Module mga.

In a nutshell, I generated 10,000 users in just ten minutes.

Batching may be used in the backend for both Patch- and Delete-Mga.

Instead of using Where-Object in your foreach loop, utilize hashtables to match your data.

I previously hinted at this in the first best practice advice, but hashtables make a significant difference in performance when matching data. I’ll use the same illustration as previously. This advice is applicable outside of the Microsoft Graph API.

I’ve condensed the scripts below; the poor example only served 1000 people, whereas the excellent example served 24883.

  • The Mailbox sizes are stored in $MBXSize.
  • The OneDrive sizes are stored in $SPSize.

A Bad Case in Point

$MBX in $MBXSize foreach $null$CurrentSP = $null$CurrentSP = $null$CurrentSP = $ $SPsize = $CurrentSP | $MBX.’User Principal Name’ -eq $MBX.’Owner Principal Name’ Where-Object $_.’Owner Principal Name’ -eq $MBX.’User Principal Name’ $CurrentSP if ($null -ne $null -ne $null -ne $null -ne $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; @ $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX OneDriveSizeInGb = $CurrentSP.’Storage Used (Byte)’ ‘User Principal Name’ MailboxSizeInGb = $MBX.’Storage Used (Byte)’ $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $FusedDataSources.Add($Object $Object = [PSCustomObject] if else @ $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MB ‘User Principal Name’ MailboxSizeInGb = $MBX.’Storage Used (Byte)’ OneDriveSizeInGb = ‘No OneDrive found’ ‘User Principal Name’ MailboxSizeInGb = $MBX.’Storage Used (Byte)’ $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $Fused

Unfortunately, for 24883 people, it took much too long to complete. As a result, I had to cut it down to 1000 users.

It took 13 minutes for the data to match.

1633384785_648_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-API13 minutes and 45 seconds (measure-command)

As an example,

@ foreach ($user in $SPSize) $SPHash = @ foreach ($user in $SPSize) $SPHash = @ foreach ($user in $S $SPHash $SPHash $SPHash $SPHash $SPHash $ $user.’Owner Principal Name’, $user) Add($user.’Owner Principal Name’, $user) $FusedDataSources = [System.Collections.Generic.List[System.Object]]::new() foreach ($MBX in $MBXSize) $MBX in $MBXSize) $MBX in $MBXSize) $MBX in $MBXSize) $MBX in $MBXSize) $MBX in $MBXSize $null$CurrentSP = $null$CurrentSP = $null$CurrentSP = $ $SPHash[$MBX.’User Principal Name’] $CurrentSP = $SPHash[$MBX.’User Principal Name’] $CurrentSP = $SPHash[$MB $CurrentSP if ($null -ne $null -ne $null -ne $null -ne $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; @ $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MBX $MB MBSize = $MBX ‘User Principal Name’ ODSize = $CurrentSP.’Storage Used (Byte)’ ‘Storage Used (Byte)’ ‘Storage Used (Byte)’ ‘Storage Used (Byte)’ ‘Storage Used (Byte)’ ‘Storage Used ( $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $FusedDataSources.Add($Object else $Object = [PSCustomObject]; $Object = [PSCustomObject]; $Object = [PSCustomObject]; MBSize = $MBX.’User Principal Name’ @ UserPrincipalName = $MBX.’User Principal Name’ ‘Byte Storage Used’ No OneDrive could be found” ODSize=”No OneDrive could be found” $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $FusedDataSources.Add($Object) $Fused

In less than 2 seconds, the data matched.

1633384786_962_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-API1 second and 794 milliseconds Measure-Command



Microsoft Graph Rest API hints and tricks

Explorer of Graphs

The Graph Explorer is one of the simplest methods to see whether your URL works without requiring the necessary App registration permissions.

Without needing to log in to an environment, you may enter a URL here and see what the outcome is. The Graph Explorer will then show a demonstration setting.

1633384786_224_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APITest your URLs using the Graph Explorer to check whether they work.

I like to test my URLs in a sample Office 365 tenancy where I can log in to the graph explorer. You’ll get the most out of it this way.

For instance, when obtaining a user account with certain characteristics. When you’re signed in, you may also utilize the Put, Post, Patch, and Delete methods. However, keep in mind that these Methods will be executed on your renter.

1633384787_119_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIYour tenant will execute the Put, Post, Patch, and Delete procedures.

Fiddler on the Roof

Fiddler is a web debugging proxy for monitoring network activity on your device.

Fiddler also displays the Microsoft Graph API queries you make. A request to obtain an Oauth token is seen in the image below.

  1. Is the request valid?
  2. Is the heading correct?
  3. This is the outcome. The Oauth token is shown here, along with its validity (1 hour)

1633384788_906_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIIn Fiddler, use Connect-Mga to get an oauth token for the Microsoft Graph API.

Use case for Fiddler on the Roof

What is the significance of Fiddler on the Roof? Well, I’ve got a good reason for you.

To add users to a group, I used the Patch-Mga cmdlet. This was a script that had been working well for a while until I received the following PowerShell error.

(400) Unacceptable Request was returned by the remote server.

According to the HTTP status codes, they are as follows:

400 Bad Request The request cannot be processed because it is faulty or wrong.

(400) Bad Request was returned by the remote server.

With such an error message, there isn’t much you can do. How is it possible that it functioned for a time and then stopped working?

To record traffic, I opened Fiddler and hit F12. After that, I ran the script again, and Fiddler reported the following error:

1633384789_969_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIWorking with Microsoft Graph API in PowerShell: best practices, tips, and techniques

I typically use a process filter to view just events from my PowerShell console. Right-click the event, then choose Filter Now > Show Only process=123456.

1633384790_587_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIWorking with Microsoft Graph API in PowerShell: best practices, tips, and techniques

Open the Raw tab by double-clicking the event. Here’s the whole error message:

“message”: “A resource can only have a maximum of ’20’ link modifications.”

The HTTPS status code is returned by PowerShell, while the error message is shown by Fiddler. (This is doable with PowerShell, but it varies depending on the error message, making filtering tricky.)

Due to the above problem notice, I’ve changed Patch- and Batch-Mga to group requests by 20 when there are more than 20.

To authenticate, use a certificate.

An Oauth token for Microsoft Graph may be obtained in a variety of ways. A certificate is the most secure method, and it is not difficult to get.

I’ve seen scripts with plain text passwords at customers’ servers where many providers have access, and the same is true with ClientSecrets. A ClientSecret works similarly to a password.

To protect your App registrations with a certificate and delete your ClientSecrets, follow these procedures.

The Oauth token may then be obtained using Connect-Mga -Certificate or -Thumbprint.

Always strive to go from the least to the greatest privileges.

To maintain the minimal permissions, utilize the minimum permissions needed for your script and don’t duplicate app registrations for multiple scripts.

The permissions tab is one of the initial headings on the List Users Graph information page. It displays the permissions in order of least to greatest rights.

1633384791_851_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIWorking with Microsoft Graph API in PowerShell: best practices, tips, and techniques

Unfortunately, Microsoft does not always update permissions properly. As a result, it’s possible that your request may still fail.

Then use the Graph Explorer to look up the URL. The Modify Permissions tab on the Graph Explorer page after you test a URL will also display the permissions in order of least to greatest power.

1633384791_575_Best-practices-tips-and-tricks-working-with-Microsoft-Graph-APIWorking with Microsoft Graph API in PowerShell: best practices, tips, and techniques

Maurice Lok-Hin deserves credit.

Maurice Lok-Hin, who taught me this over the past year, deserves credit. I was able to decrease scripts from 5 to 6 hours to 20 to 30 minutes thanks to him.


Microsoft Graph API is a RESTful service that allows users to access and manipulate data in the cloud. The microsoft graph api limit results is a PowerShell command-line tool that allows users to limit the number of records returned by the Microsoft Graph API.

Related Tags

  • microsoft graph explorer
  • microsoft graph api authentication
  • microsoft graph api error handling
  • microsoft graph api delay
  • microsoft graph api documentation

Leave a Comment

Your email address will not be published. Required fields are marked *