Table of Contents
How to use the Graph API for PowerShellers
Before you start, I want to tell you that this is my way of working with the Microsoft Graph API & PowerShell.
It may be that you don’t like this & that you prefer another way and that’s fine too.
It would be nice if you share this in the comments.
I’ve noted the steps for a quick look and I explain the steps below in detail in a separate chapter.
I’ll use ‘List User’ as an example in the blog post.
A short explanation about how I work with a new Microsoft Graph API call in PowerShell.
- Start by searching on Google for ‘List User Microsoft Graph site:microsoft.com‘
- I almost always scroll to the examples first to see how the requests are structured and what the result is what you get back.
I often use this as an example for my own requests.
- Add the least permissions to my Registered Application by the principle of least privileges.
- The value in the HTTP request is the relative path for the API.
You need to add the correct endpoint: https://graph.microsoft.com/XXXX.
Where XXXX stands for v1.0 or beta.
When I don’t get the output I want I switch from v1.0 to beta.
Looking at the Method I know which cmdlet I am using from my module. Get-Mga, Put-, Post-, ~
- What exactly do I need and how many objects are involved?
When it comes to 100+ objects, I use the$top
query as much as possible.
Is this just the userPrincipalName? Then I use the query$select
.
Can I filter out the users via a property?
Via the query parameters I can reduce the calls to the API & I don’t exceed the throttling limits.
- The header contains additional information necessary for the request.
You must provide at least the mandatory headers.
This is almost non-existent with the Graph API. When it does occur I use the CustomHeader parameter.
- Which properties should I add to my HashTable for the RequestBody and which types are expected?
Are these strings, arrays, bool, int, ~?
And after that press the magic F5 or F8 and wait for the results.
Wondering if you’re adopting best practices for the Microsoft Graph API? Then look at this blog post:
Best practices, tips and tricks working with Microsoft Graph API in PowerShell
So, where do I start when I need something from the Microsoft Graph API?
The Microsoft Graph API has its own search function. Personally, I am not a fan of the search function because what you search for must match the title of the page.
Instead, I search on Google with the site:microsoft.com query.
When I use ‘Get User’ as an example, the first thing I start with is ‘Googling’ for:
List User Microsoft Graph API site:microsoft.com
The ‘List Users‘ is the first result.
We’ve got the Microsoft Graph API, what now?
The Table of Contents is shown on the right of the screen.
In general it is the same for all Graph references.
Permissions
The permissions in this table are important.
The permissions you see below are listed from least to most privileged. Which means that your registered application needs one of these permissions, but with the leftmost permission it can execute this API call.
Permission type | Permissions (from least to most privileged) |
---|---|
Delegated (work or school account) | User.ReadBasic.All, User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All, Directory.AccessAsUser.All |
Delegated (personal Microsoft account) | Not supported. |
Application | User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All |
It is wise not to give an application more permissions than necessary.
With the idea that when the application is compromised, the hacker does not get more permissions than was necessary within your environment.
This is called the ‘principle of least privilege’.
To add permissions to you registered application you can follow the steps here.
I don’t recommend reusing a Registered application and instead always create a new application and give it the fewest permissions possible.
And always have the Authorization done with a certificate.
For what you do with PowerShell and my Optimized.Mga module you always need an registered application.
The idea behind this application is that you can run scripts without logging in as a person. That is why we need to use the Application permission.
Microsoft Graph has two types of permissions:
- Delegated permissions are used by apps that have a signed-in user present. For these apps, either the user or an administrator consents to the permissions that the app requests and the app can act as the signed-in user when making calls to Microsoft Graph. Some delegated permissions can be consented by non-administrative users, but some higher-privileged permissions require administrator consent.
- Application permissions are used by apps that run without a signed-in user present. For example, apps that run as background services or daemons. Application permissions can only be consented by an administrator.
Source: Microsoft.com
HTTP request
the HTTP request is the URL you use for Invoke-RestMethod
or Get-Mga
.
The HTTP request shows the method (Get) & relative path to the correct API call.
You have to add the endpoint yourself.
There are 2 different ‘endpoints‘:
- v1.0
EndPoint: https://graph.microsoft.com/v1.0
Uri for ‘List Users‘: https://graph.microsoft.com/v1.0/users
For Reference: Microsoft Graph REST API v1.0 reference – Microsoft Graph v1.0 | Microsoft Docs - beta
EndPoint: https://graph.microsoft.com/beta
Uri for ‘List Users‘: https://graph.microsoft.com/beta/users
For Reference: Microsoft Graph beta endpoint reference – Microsoft Graph beta | Microsoft Docs
The v1.0 will remain as is & the beta may change in the future.
I always try the v1.0 reference first and when I make the call and don’t get the values back I expect, I try the beta.
On the left side of the page you can see which reference is used by the docs.
When you switch reference versions, different properties may be requested for the body, header, content-type, or queries.
Depending on the method you use a cmdlet in my module.
The cmdlets contain the Method in the name:
Get-Mga
Post-Mga
Put-Mga
Delete-Mga
Patch Mga
With Invoke-RestMethod
you have to use the method parameter.
For more about the Methods I’d like to refer your to the official Microsoft docs.
Optional query parameters
How important the query parameters are depends on what you want to do.
Suppose you only need to request one user and you want to request as many properties as possible, then it is not necessary to use a query,
Get-Mga -URL https://graph.microsoft.com/v1.0/users/[email protected]
but this concerns ‘List Users’ which means that we will request a certain number of users.
Suppose you need the users including all retrievable properties, then it is still useful to use a query parameter, why?
Because by default the Graph API returns 100 objects and with the $top
query you can adjust this to a maximum of 999.
(Get-Mga -URL 'https://graph.microsoft.com/v1.0/users' -Once).count
100
(Get-Mga -URL 'https://graph.microsoft.com/v1.0/users?$top=999' -Once).Count
999
That means you make 9 calls less to the Graph API.
The chance of hitting the throttling limits is significantly reduced that way.
Suppose you only need the userPrincipalName?
Then you can also use the $select
query.
Get-Mga -URL 'https://graph.microsoft.com/v1.0/users?$top=999&$select=userPrincipalName' -Once
userPrincipalName
-----------------
[email protected]
[email protected]
[email protected]
[email protected]
This ensures that you have even less chance of exceeding the throttling limits.
look as much as possible at the query parameters that you can use for the relevant Microsoft Graph API call and how you can use them.
For more about the query parameters check this page:
Use query parameters to customize responses – Microsoft Graph | Microsoft Docs
Request headers
The request Header is mainly important for people who don’t use my module.
If you do use my module, it won’t happen often unless you’re uploading files or doing other specific tasks.
The header contains additional information necessary for the request.
For example, the authorization is your token to validate that you are allowed to make the call.
When you use the Connect-Mga
cmdlet the authorization token it is saved in your global scope and automatically refreshed when it expires (the token expires after one hour).
$ConnectMgaSplatting = @{
ClientSecret = 'XXXXXX'
ApplicationID = 'XXXXXX'
Tenant = 'cab50820-6873-42d9-8d79-f1bcad910f35'
}
Connect-Mga @ConnectMgaSplatting
$global:MgaheaderParameters
AuthKey : Authorization
Value : Bearer eyJ0eXAiO~
For the users who work with Invoke-RestMethod, the Header is the parameter Headers.
Invoke-RestMethod -Method Get 'https://graph.microsoft.com/v1.0/users?$top=999&$select=userPrincipalName' -Headers $MgaheaderParameters
@odata.context : https://graph.microsoft.com/v1.0/$metadata#users(userPrincipalName)
value : {@{[email protected]}
Unfortunately, ‘List Users‘ doesn’t have that many headers which is generally not the case with the Graph API.
If you do want to add an extra header to one of the cmdlets of the Optimized.Mga module, you can use the CustomHeader parameter.
It works the same as the Headers parameter of Invoke-RestMethod
and after the call the header is automatically reverted to the original header.
For example, this is what I’m using in my Upload-MgaSharePointFiles
cmdlet in this blog post & this API call.
$Header = @{}
$Header.Add('Content-Length', $LocalFileBytes.Length)
$Header.Add('Content-Range', $contentRange)
$Header.Add('Content-Type', 'octet/stream')
$UploadResult = Put-Mga -URL $uploadUrlResponse.uploadUrl -InputObject $LocalFileBytes -CustomHeader $Header
Request body
Because List Users uses the Get method, which means you only get information, you don’t have a request body.
You use a request body when, for example, you are going to create a user and have to provide the properties of the user with your request.
You do this by using the InputObject parameter in my module under the appropriate cmdlets and with Invoke-RestMethod
you can use the Body parameter.
To elaborate on this, I switch to the Create User API call for now:
Create User – Microsoft Graph v1.0 | Microsoft Docs
Always look carefully at the request body to see which properties are mandatory. These are often listed separately in a table:
‘The following table lists the properties that are required when you create a user:.’
Parameter | Type | Description |
---|---|---|
accountEnabled | boolean | true if the account is enabled; otherwise, false. |
displayName | string | The name to display in the address book for the user. |
onPremisesImmutableId | string | Only needs to be specified when creating a new user account if you are using a federated domain for the user’s userPrincipalName (UPN) property. |
mailNickname | string | The mail alias for the user. |
passwordProfile | PasswordProfile | The password profile for the user. For Azure B2C tenants, the forceChangePasswordNextSignIn property should be set to false and instead use custom policies to force password reset at first sign in. |
userPrincipalName | string | The user principal name ([email protected]). |
The parameter names are case sensitive. So keep this in mind!
Read the description carefully because it often contains valuable information. As you can see we don’t need to add the property onPremisesImmutableId because we don’t use federated services.
Look closely at the type they expect and carefully build your HashTable.
If we only grab the mandatory properties, your HashTable should look like this:
$User = [PSCustomObject]@{
userPrincipalName = $UserPrincipalName
displayName = $Username
accountEnabled = 'true'
mailNickname = $Username
passwordProfile = [PSCustomObject]@{
password = 'H78302ehpib'
forceChangePasswordNextSignIn = 'true'
}
}
Post-Mga -URL 'https://graph.microsoft.com/v1.0/users' -InputObject $User
id : e65aba63-a8b0-4f19-b479-e8f6798e8829
businessPhones : {}
displayName : test.test.test
userPrincipalName : [email protected]
With the Optimized.Mga module you don’t have to worry about the ObjectType.
The cmdlets automatically convert it to Json-format and use the correct content-type for the request.
With Invoke-RestMethod
you need to make sure that your HashTable is converted to Json with the ConvertTo-Json
cmdlet. The Invoke-RestMethod
also expects you to specify what type of content it is with the ContentType parameter. For json this is ‘application/json‘.
Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users' -Body ( $User | ConvertTo-Json ) -Method Post -Headers $MgaheaderParameters -ContentType 'application/json'
Response
This states what kind of response you can expect in return.
I find it superfluous to look at the response when I already get this information from the examples.
Example
The examples are the most valuable to me.
In the example you can often see at glance how the call is made.
There is no tab for PowerShell in the examples yet, but the HTTP example is enough to understand what to use.
Here you can see how an existing call is made:
- This shows how the URL is formatted
- Which method you use
- The minimum request necessary
- Whether you have to specify the content-type
and, not in the screenshot, the answer you can expect in return.