Table of Contents
There are two ways to connect to Microsoft Graph using PowerShell:
- Using Invoke-RestMethod to make direct API calls to Microsoft Graph.
- Use the Microsoft Graph PowerShell SDK. It’s a collection of modules designed for interacting with Graph directly through PowerShell.
Graph PowerShell SDK: What is it, and why should I use it?
The Microsoft Graph PowerShell SDK consists of a main module and 38 sub-modules. The SDK is essentially a wrapper that allows you to use PowerShell cmdlets to make calls to Microsoft Graph. It targets Systems Administrators who are familiar with PowerShell and may not have as much experience working with various APIs.
You can use the -Debug parameter to see what happens after you run a command.
PS C:\> Get-MgUser -Debug
DEBUG: ============================ HTTP REQUEST ============================
HTTP Method:
GET
Absolute Uri:
https://graph.microsoft.com/v1.0/users
Headers:
FeatureFlag : 00000043
Cache-Control : no-store, no-cache
User-Agent : Mozilla/5.0,(Windows NT 10.0; Microsoft Windows 10.0.22621;
en-US),PowerShell/5.1.22621.1778
Accept-Encoding : gzip
SdkVersion : graph-powershell/2.6.1
client-request-id : 70c639d8-36c1-4575-b73d-946393d7c4a2
Body:
DEBUG: ============================ HTTP RESPONSE ============================
Status Code:
OK
Headers:
Transfer-Encoding : chunked
Vary : Accept-Encoding
Strict-Transport-Security : max-age=31536000
request-id : 62376f70-2d89-4b1b-ab8c-3cb072c65fd0
client-request-id : 70c639d8-36c1-4575-b73d-946393d7c4a2
x-ms-resource-unit : 2
OData-Version : 4.0
Cache-Control : no-cache
Date : Mon, 16 Oct 2023 13:06:29 GMT
PS C:\> Get-MgGroup -Debug
DEBUG: ============================ HTTP REQUEST ============================
HTTP Method:
GET
Absolute Uri:
https://graph.microsoft.com/v1.0/groups
PS C:\> Get-MgDevice -Debug
DEBUG: ============================ HTTP REQUEST ============================
HTTP Method:
GET
Absolute Uri:
https://graph.microsoft.com/v1.0/devices
It is a great option. It is fairly well documented, even though it’s not always easy to find the cmdlets you need in the documentation. It does have built in tools to allow you to discover the cmdlets you need in your scripts, and that feature makes up for a lot of the shortcomings in the documentation.
Microsoft has made a lot of entry points available to discover PowerShell cmdlets. For example, if we look at the Microsoft Graph docs page for returning a user from Microsoft Graph, there is an option to view the PowerShell cmdlets. It even shows which SDK module we need to import in the sample.
Microsoft Graph Explorer also includes PowerShell on the code snippets tab in the response. Both options make finding the required PowerShell cmdlets and finding examples extremely easy.
Finally, the SDK documentation has a wealth of knowledge on the individual cmdlets, but because the cmdlet names can be overwhelming it can be difficult to navigate.
If you are new to Microsoft Graph and REST APIs the Graph PowerShell SDK is a great jumping-off point. It is well-documented and has a very familiar look and feel. There’s a low barrier to entry and it doesn’t require learning a new skill set. You can build automation workflows or perform one-off tasks with relative ease.
Microsoft Graph REST API
Microsoft Graph is a RESTful web API that enables you to access Microsoft Cloud service resources. After you register your app and get authentication tokens for a user or service, you can make requests to the Microsoft Graph API.
To read from or write to a resource such as a user or an email message, you construct a request that looks like the following:
{HTTP method} https://graph.microsoft.com/{version}/{resource}?{query-parameters}
The components of a request include:
- {HTTP method} – The HTTP method used on the request to Microsoft Graph.
- {version} – The version of the Microsoft Graph API your application is using.
- {resource} – The resource in Microsoft Graph that you’re referencing.
- {query-parameters} – Optional OData query options or REST method parameters that customize the response.
- {headers} – Request headers that customize the request. Can be optional or required depending on the API.
After you make a request, a response is returned that includes:
- Status code – An HTTP status code that indicates success or failure. For details about HTTP error codes, see Errors.
- Response message – The data that you requested or the result of the operation. The response message can be empty for some operations.
- @odata.nextLink – If your request returns a lot of data, you need to page through it by using the URL returned in @odata.nextLink. For details, see Paging.
- Response headers – Additional information about the response, such as the type of content returned and the request-id that you can use to correlate the response to the request.
Using direct REST API calls to Microsoft Graph isn’t about replacing the SDK. It is about adding another tool to your toolbox. You should use whatever tool works best for you and your scenario.
Here’s an example of how to work with Microsoft Graph API. In this example, I am connecting to Microsoft Graph to get the list of users in a tenant. All of the parameters needed for the API call are splatted in a hashtable before being passed to Invoke-RestMethod.
At the start of the script, we gather information needed to connect to Microsoft Graph and return an access token. I use a version of this in every script that I use to interact with Graph.
# Get access token
$clientId = "843d9b89-943c-4dd0-a55c-4e115825dd9d"
$tenantId = "2621ff9b-03ee-47e2-8011-1e6280df5ca7"
$clientSecret = "CNv8Q~oTflh~e3o9Zka.bMxaAmecHJOUDOi~Zcp4"
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$body = @{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
client_Id = $clientId
Client_Secret = $clientSecret
}
$tokenRequest = Invoke-RestMethod -Uri $uri -Method POST -Body $body
$token = $tokenRequest.access_token
$headers = @{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
Next, we pass our access token, URI, REST method, and a body (if required) into the script. These parameters are added to a splat hashtable that is used to pass parameters into Invoke-RestMethod. If the method being used requires a body, the body is then added to the hashtable. Finally, we make our API call and return the response to the script.
# Get the list of users in the tenant
$uri = "https://graph.microsoft.com/v1.0/users?`$select=displayName,userPrincipalName,userType,accountEnabled&"
# Perform pagination if next page link (odata.nextlink) returned
$result = @()
while ($null -ne $uri) {
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method GET
$result += $response.value
$uri =$response.'@odata.nextlink'
}
# Output options to console, graphical grid view or export to CSV file.
$result | Format-Table
displayName userPrincipalName userType accountEnabled
----------- ----------------- -------- --------------
Bon Ben [email protected] Member True
Chris [email protected] Member True
DEM [email protected] Member True
DEM01 [email protected] Member True
Helen [email protected] Member True
Info [email protected] Member True
Leo [email protected] Member True
User1 [email protected] Member True
User2 [email protected] Member True
The section was essentially a manifesto in favor of using direct API calls instead of using the SDK. In my opinion, making direct API calls with Invoke-RestMethod is a better option because it offers more simplicity, stability, flexibility, and portability.
Simplicity
- The example script shared above use the Invoke-RestMethod cmdlet and it works without installing any extra modules and can be used to make API calls to any REST API that you have access to.
- The PowerShell SDK consists of a primary module and 80 sub modules (40 beta modules). You don’t need to install all of them, but you do need to know which ones need to be installed for the tasks you are trying to complete. If you choose only to install needed modules you may have to install additional modules later to makes calls to other areas of the service.
- Direct calls with Invoke-RestMethod only require you to know the URI, which is something you probably need to know to find the right cmdlet in the SDK. So, the script can be run on any computer with PowerShell installed including Mac and Linux computers.
Stability
- Technology is constantly evolving, and this includes Microsoft Graph, whether you’re making direct calls to the API or utilizing the SDK. Nonetheless, API endpoints tend to be more stable. They are integral to the suite of tools Microsoft has integrated into Azure, and deprecating an endpoint could have significant service implications.
- The PowerShell SDK, provided and regularly updated by Microsoft, frequently undergoes changes with cmdlets being added or removed. Modifications made to Graph may not be mirrored in the SDK until after several development cycles.
By making direct API calls to Microsoft Graph you can take advantage of new endpoints sooner, and they are going to be less likely to change.
Flexibility
Do you want to authenticate to Microsoft Graph with a client secret stored in Azure Key Vault? Do you want to make calls to Microsoft Graph from your favorite low-code tool? Does your workload require you to hit other APIs?
Most low-code tools don’t have an option to integrate PowerShell directly. If you want to use Power Automate or Logic Apps, you will have to call Azure Automation or an Azure Function. The Microsoft Graph SDK support Microsoft Graph. If you want to make calls to other APIs, you’re going to have to make a direct API call to those services.
Portability
- Portability and flexibility are closely related. Both the skills and the actual REST API calls that are used when making direct calls to Graph are portable.
- The SDK is self-contained. Knowing how to use Get-MgUser isn’t going to help you when running a flow in Power Automate. If you want to return all users, you’re going to make an HTTP request to GET https://graph.microsoft.com/v1.0/users
Working with third party APIs in PowerShell is identical to calling Graph with Invoke-RestMethod. The skillset itself is portable, which can’t be said for the PowerShell SDK.