Get started with PowerShell to run Graph API queries – Part 2

Get started with PowerShell to run Graph API queries

Welcome to post 4 of the series Learn How to Use Microsoft Graph API with Joy where we will continue our journey to learn how we can make Graph API calls from PowerShell.

The previous posts of this series is listed below for your convenience.

In the previous post of this series, we already covered

  • Azure AD App Registration, and
  • Implementation of two MSAL Auth flow methods in PowerShell to obtain an Access Token.

As such, today in this post, we will learn

  • how to construct Graph API calls in PowerShell, and
  • invoke different types of API calls to MS Graph (GET, POST, PATCH, DELETE)

Before we begin, we need to obtain an access token from the Microsoft Identity platform to access Microsoft Graph. As such, you need to ensure that you have the actions performed as discussed in my previous post.

Brief intro to the different HTTP Methods

  • HTTP method GET is used to fetch data of existing entity/entities from MS Graph.
  • HTTP method POST is used to create a new entity for the resource type in MS Graph, as specified in the query.
  • HTTP method PATCH is used to update parameters of an existing entity in MS Graph.
  • HTTP method DELETE is used to remove an entity/entities from MS Graph. (soft deletion)

For the purpose of this blog, I will show you examples for each of the HTTP methods mentioned above.

Construct MS Graph API GET call in PowerShell

Let us take the example of fetching data for a single user from the tenant and exporting the retrieved data to a CSV file.

<# Region Auth Start #>
$tenantId = "<Your Tenant ID here>"
$clientID = "<Your registered Application ID here>"
$clientSecret = (ConvertTo-SecureString "<Your Application Client Secret here>" -AsPlainText -Force )
$Scope = "https://graph.microsoft.com/.default"
$authToken = Get-MsalToken -ClientId $clientID -ClientSecret $clientSecret -TenantId $tenantId -Scopes $Scope
<# Region Auth End #>

<# Ask user to enter User UPN to get data for #>
Write-Host "Enter the user UPN"
$usr = Read-Host
Write-Host "Fetching data of user $usr"

<# Construct Graph API call #>
$Headers = @{
        "Authorization" = "Bearer $($authToken.AccessToken)"
        "Content-type"  = "application/json"
    }
$apiUri = "https://graph.microsoft.com/beta/users/$usr"
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method GET

<#Ask user to enter filepath and filename to save retrieved data #>
$Path = Read-Host "Enter filepath to save data"
$Filename = Read-Host "Enter filename to save data"

<# Export data to file #>
$response | Export-Csv -Path "$Path\$Filename"

When you run this, you would be prompted to enter the User UPN for whom you want to get the data, followed by filepath and filename to save the user data that it fetched from Microsoft Graph.

Get started with PowerShell to run MS Graph API queries - Fetch data from Microsoft Graph using API GET call.
Get started with PowerShell to run MS Graph API queries – Fetch data from Microsoft Graph using API GET call.

You will find the user data retrieved with the above at the location as specified.

Get started with PowerShell to run MS Graph API queries - Save fetch data from Microsoft Graph to a CSV file.
Get started with PowerShell to run MS Graph API queries – Save fetch data from Microsoft Graph to a CSV file.
Did you noticed how I used the MSAL.PS module for the Client Credential Client Secret Auth flow? 
  
You can use the Get-MsalToken cmdlet of the MSAL.PS module to do MSAL auth flows and obtain an access token. However, still you should know how you can implement the auth flows as shown in my previous post. 

However, you need to keep in mind that whenever you are fetching data from Microsoft Graph, especially which deals with a large data set, like all users or all groups, the response may not give you all the items for the resource you are trying to retrieve at once. This is because of paging. Check what is paging with Microsoft Graph.

As such, if you are trying to fetch data from Microsoft Graph and the expected items is large, the response to the Graph API query returns the default page size number of items and includes a field called @odata.nextLink which contains the URL to the query that will return the next page of data.

Different APIs (Graph resources) have different default and maximum page size.

While making a Graph API call, you may also define the page-size like this

https://graph.microsoft.com/{version}/{resource}?$top=<page_value>

So if I have to construct a Graph API call to retrieve all users from the tenant but want 100 users to be returned per page, then my query would like this

https://graph.microsoft.com/beta/users?$top=100
Get started with PowerShell to run MS Graph API queries - Understand Graph API paging behavior.
Get started with PowerShell to run MS Graph API queries – Understand Graph API paging behavior.
So if I have more than 100 users in the tenant, the initial response will contain first 100 users with the @odata.nextLink pointing to the url of the query that will return the next set of users, till all the users are retrieved and there is no @odata.nextLink provided.

You have to note that if you define a page size that tops the max page-size for the resource, you will get an error running the query. For example, for Users resource,  the default page size is 100 and max page size is 999, so if you define custom page size as $top=1000 in your query, you will get an error.

As such, your app/script should be equipped to check if the response received contains the field @odata.nextLink and if yes, use it recursively till the response will have no @odata.nextLink field, meaning all the items have been retrieved.

This is how you would do that in PowerShell

<# Region Auth Start #>
Your Auth Code Goes Here
<# Region Auth End #>

<# Construct Graph API call #>

$Headers = @{
    "Authorization" = "Bearer $($authToken.AccessToken)"
    "Content-type"  = "application/json"
}

$uri = "https://graph.microsoft.com/beta/users"
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get

$store = @()
$store += $response.value 

while ($response.'@odata.nextLink' -ne $null) {
	$response += Invoke-RestMethod -Uri $next -Headers $headers -Method Get
}

You also need to be aware of throttling in MS Graph API when you are trying to work with a very large set of data. You get to know that your request is being throttled when Microsoft Graph returns HTTP status code 429.

Construct MS Graph API POST call with PowerShell

Let us consider the case of creating a user with PowerShell and Microsoft Graph.

HTTP Method POST call requires you to provide the data/values for parameters of the entity to be created for the defined resource type, as a JSON payload in the HTTP call Request Body.

To construct the JSON body, you need to read the documentation for the Microsoft Graph resource type.

For example, to create a resource type User, I only need to provide the required values (minimum parameters) that are required to create an entity of the specified resource type (user).

Body:
{
  "accountEnabled": true,
  "displayName": "Test User",
  "mailNickname": "TestU",
  "userPrincipalName": "testuser@intunewithjoy.in",
  "passwordProfile" : {
    "forceChangePasswordNextSignIn": true,
    "password": "password"
  }
}
In the GET example above, we saw how we can use Get-MsalToken to do a Client Credential Auth flow to make the app run with Application permissions. This time let's see how we can do an Interactive auth so that the app runs with Delegated permissions.
<# Region Auth Start #>
$tenantId = "<Your Tenant ID here>"
$clientID = "<Your Application ID here>"
$Scope = "https://graph.microsoft.com/.default"
$redirectUri = "https://localhost"
$authToken = Get-MsalToken -ClientId $clientID -TenantId $tenantId -Interactive -RedirectUri $redirectUri -Scopes $Scope
<# Region Auth End #>

<# Construct Graph API call #>

$Headers = @{
        "Authorization" = "Bearer $($authToken.AccessToken)"
    }

$body = @'
{
    "accountEnabled": true,
    "displayName": "Graph User",
    "mailNickname": "GUser",
    "userPrincipalName": "graph.user@intunewithjoy.in",
    "passwordProfile" : {
            "forceChangePasswordNextSignIn": true,
            "password": "xWwvJ]6NMw+bWH-d"
    }
}
'@

$apiUri = "https://graph.microsoft.com/beta/users/"
Invoke-RestMethod -Headers $Headers -Uri $apiUri -Body $body -Method POST -ContentType 'application/json'

If successful, the response to the above call will return the created user object.

Note how the Body is constructed using the @' '@. If you want to pass variables as values within the JSON body, you would need to use @" "@ instead.
Did you noticed how I used the MSAL.PS module for an Interactive Auth flow?

Here I used Get-MsalToken cmdlet of the MSAL.PS module to do an Interactive Auth requiring user sign-in to make the app work with Delegated permissions and obtain an access token.
Using MSAL.PS module Get-MsalToken function to perform AUTH and obtain Access Token.
Using MSAL.PS module Get-MsalToken function to perform AUTH and obtain Access Token.

Construct MS Graph API PATCH call with PowerShell

Let us consider the case of updating the details of the user we just created above with PowerShell and Microsoft Graph.

Same as POST call, HTTP Method PATCH call also requires you to provide the data/values for parameters of the entity to be updated as JSON payload in the HTTP Request Body.

<# Region Auth Start #>

Your Auth Code Here

<# Region Auth End #>

<# Construct Graph API call #>

$Headers = @{
        "Authorization" = "Bearer $($authToken.AccessToken)"
    }

$body = @'
{
    "displayName": "Graph Updated",
    "country": "India"
}
'@

$apiUri = "https://graph.microsoft.com/beta/users/"
Invoke-RestMethod -Headers $Headers -Uri $apiUri -Body $body -Method POST -ContentType 'application/json'

Note that there is no content returned as a response. If successful, you will get an HTTP status code 204 that you can check for, or use a successive GET call to check if the User parameters have been updated.

Construct MS Graph API DELETE call with PowerShell

Finally, we get to the HTTP method DELETE.

Let us delete the user we just created above with PowerShell and Microsoft Graph.

<# Region Auth Start #>

Your Auth Code Here

<# Region Auth End #>

<# Construct Graph API call #>

$Headers = @{
        "Authorization" = "Bearer $($authToken.AccessToken)"
    }

$user = "graph.user@intunewithjoy.in"
$apiUri = "https://graph.microsoft.com/beta/users/$user"

Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method DELETE

Similar to above, there is no content returned as a response. If successful, you will get an HTTP status code 204 that you can check for, or use a successive GET call to check for the user which should return an error.

Wrap UpLearn How to Use Microsoft Graph API with Joy

While working with Microsoft Graph API, if you see that your API call in PowerShell is failing or returning an error, as a best practice, use the Microsoft Graph Explorer to run your query to have a better idea of why the query is failing and you would be easily able to make the necessary corrections.

I hope this series of blog posts will help you to get started with using PowerShell to make Microsoft Graph API calls.

If you build upon the skills as learned and couple that with PowerShell’s scripting capabilities, you can go about automating various workflows in your M365 environment.

If you would want to make Graph API calls from a custom app that you are building using any popular programming languages out there, check out for the availability for MS Graph SDKs as the MS Graph team has done some great work in launching SDKs for different platforms. Check out Microsoft's official documentation to learn more about Graph SDK.