A Basic Graph API Powershell Module
- Matt Zaske
- April 21, 2025
- 7 minutes
During a session planning conversation for MMS Flamingo last fall I had the idea to create a basic Powershell module as an illustration/starting point to using direct HTTP requests with Invoke-RestMethod
against Graph API. The concept was simple: here's a couple of lines of code to Do Something in Graph, with direct HTTP requests, and be a launch point for quickly exploring and troubleshooting calls to other Graph endpoints without a lot of overhead or abstraction.
One thing it was not intended to be: the module isn't for production scenarios — just a learning and discovery platform. To maintain simplicity, there's no error handling involved, it's only configured for Application permissions, and it doesn't handle things like pagination, bearer token management, or other things that are often managed for you with proper tooling such as the official SDKs and modules.
The Module Basics
I've had the module publicly sitting over on GitHub since MMS Flamingo, and it has a full README explaining the few bits necessary to configure in the application setup. For the purposes of running the example script it includes hard-coding a few IDs for Group, User, and Device to obtain, or in one case update, information. It's implied that the user is already familiar with configuring the app registration in the Entra portal, the basics of which I covered in the previous post.
What Does The Module Provide?
Ultimately, the basic module provides three key functions:
- Obtaining a bearer token from provided credentials: a client ID and client secret;
- Using a small number of direct HTTP read-based requests with
Invoke-RestMethod
to illustrate default Graph requests, some with a$select
query parameter, some with a$filter
parameter, and some with both parameters to obtain data for devices, device groups, device users, and bitlocker keys; and - A basic example of updating a JSON payload to update a device's extension attributes.
That's it. That's the module! Between those functions many of the common things folks need to know or fiddle with are illustrated in a basic manner. Because again the intent was to illustrate and simplify using Graph API, not be a fully-baked "take it and roll in production" piece of software.
Module Functions
This basic module exposes the following functions that I hope are easy to understand and follow:
Get-BearerToken
Get-BearerToken
is the Invoke-RestMethod
version of what I wrote several months ago: a basic OAuth request. That post used cURL for the HTTP calls, but with a little modification Invoke-RestMethod
does the same thing. Intended to be used in code/example.
Module Example:
$bearerToken = Get-BearerToken -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret
This function has been provided as part of the module since you'll need a $bearerToken
for all of the other calls within.
Important to note: Rolling your own authentication comes with risk, so while this method works great for testing or in your lab environment, be aware that there are other ways to more thoroughly handle authentication for your production stuff so you don't wind up causing a Resume-Generating Event (RGE).
Get-AllDevicesAllData
Get-AllDevicesAllData
does just that: It will obtain and output all default data points for all devices in your tenant. There's no filtering or selection; just everything. Useful to quickly see a list of devices.
Module Example:
Get-AllDevicesAllData -bearerToken $bearerToken
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/devices?$select=id,deviceId,displayName,manufacturer,model
Module Output (truncated):
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#devices",
"value": [
{
"id": "id-guid-is-here",
"deletedDateTime": null,
"accountEnabled": true,
"approximateLastSignInDateTime": null,
"complianceExpirationDateTime": null,
"displayName": "GIT-DEV-IT-02",
...
"alternativeSecurityIds": ""
},
...
]
}
Get-AllDevicesSelectData
Get-AllDevicesSelectData
obtains a selected list of data points for all devices in your tenant using the $select
query parameter to limit the number of fields returned. Useful to obtain a predefined set of data points for all devices.
Module Example:
Get-AllDevicesSelectData -bearerToken $bearerToken
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/devices?$select=id,deviceId,displayName,manufacturer,model
Module Output (truncated):
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#devices(id,deviceId,displayName,manufacturer,model)",
"value": [
{
"id": "id-guid-is-here",
"deviceId": "deviceId-guid-is-here",
"displayName": "GIT-DEV-IT-02",
"manufacturer": "Microsoft Corporation",
"model": "Virtual Machine"
},
...
]
}
Get-AllSecurityGroups
Get-AllSecurityGroups
obtains a list of all "security" groups in your tenant using the $select
query parameter to limit the number of fields returned and the $filter
query parameter to only return securityEnabled
groups. Useful to quickly see a list of groups.
Module Example:
Get-AllSecurityGroups -bearerToken $bearerToken
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/groups?$select=id,displayName,securityEnabled,groupTypes,createdDateTime&$filter=(securityEnabled eq true)
Module Output (truncated):
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups(id,displayName,securityEnabled,groupTypes,createdDateTime)",
"value": [
{
"id": "group-id-guid-is-here",
"displayName": "BrainStorm Demo Devices",
"securityEnabled": true,
"groupTypes": "",
"createdDateTime": "2025-03-07T01:48:00Z"
},
...
]
}
Get-GroupMembers
Get-GroupMembers
obtains a list of the members of the provided $groupId
in your tenant and uses the $select
query parameter to limit the fields returned.
Module Example:
Get-GroupMembers -bearerToken $bearerToken -groupId $groupId
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/groups/$groupId/transitiveMembers?`$select=id,displayName,deviceId
Module Output (truncated):
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects(id,displayName,deviceId)",
"value": [
{
"@odata.type": "#microsoft.graph.device",
"id": "id-guid-is-here",
"displayName": "GIT-DEV-IT-01",
"deviceId": "deviceId-guid-is-here"
},
...
]
}
Get-AllUsersSelectData
Get-AllUsersSelectData
obtains a selected list of data points for all users in your tenant using the $select
query parameter to limit the number of fields returned. Useful to obtain a predefined set of data points for all users.
Module Example:
Get-AllUsersSelectData -bearerToken $bearerToken
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/users?$select=id,displayName,mail,userPrincipalName
Module Output (truncated):
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users(id,displayName,mail,userPrincipalName)",
"value": [
{
"id": "id-guid-is-here",
"displayName": "Adele Vance",
"mail": "AdeleV@domain-name-here.com",
"userPrincipalName": "AdeleV@domain-name-here.com"
},
...
]
}
Get-DeviceGroupMemberships
Get-DeviceGroupMemberships
obtains and displays a list of group names for which a given $deviceId
is a member by using the $select
query parameter. Useful to quickly see a list of group memberships for a device.
Module Example:
Get-DeviceGroupMemberships -bearerToken $bearerToken -deviceId $deviceId
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/devices/$deviceId/memberOf/$/microsoft.graph.group?`$select=displayName
Module Output:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups(displayName)",
"value": [
{
"displayName": "MMS Demo Devices"
},
{
"displayName": "BrainStorm Demo Devices"
}
]
}
Get-DevicesOwnedByUser
Get-DevicesOwnedByUser
obtains and displays a list of device names for which a given $userId
is a owner by using the $select
query parameter. Useful to quickly see a list of devices owned by a given user.
Module Example:
Get-DevicesOwnedByUser -bearerToken $bearerToken -userId $userId
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/users/$userId/ownedDevices?`$select=displayName
Module Output:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#devices",
"value": [
{
"@odata.type": "#microsoft.graph.device",
"displayName": "GIT-DEV-IT-01"
},
{
"@odata.type": "#microsoft.graph.device",
"displayName": "GIT-DEV-IT-02"
}
]
}
Get-DeviceDetails
Get-DeviceDetails
obtains all default data points for a given $deviceId
. There's no filtering or selection; just everything. Useful to quickly see a list of all data points of a given device.
Module Example:
Get-DeviceDetails -bearerToken $bearerToken -deviceId $deviceId
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/devices/$deviceId
Module Output (truncated):
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#devices/$entity",
"id": "id-guid-is-here",
"deletedDateTime": null,
"accountEnabled": true,
"approximateLastSignInDateTime": null,
"complianceExpirationDateTime": null,
"displayName": "GIT-DEV-IT-02",
...
}
Update-DeviceDetails
Update-DeviceDetails
will update the extensionAttribute1
field for the given $deviceId
. It uses a simple JSON payload and the HTTP PATCH
method:
$deviceUpdateData = '{
"extensionAttributes": {
"extensionAttribute1": "This is an awesome object now"
}
}'
Module Example:
Update-DeviceDetails -bearerToken $bearerToken -deviceId $deviceId -updateJson $deviceUpdateData
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/devices/$deviceId
The module output will be empty if this was successful. To see your changes, you would re-run the Get-DeviceDetails
cmdlet with the same $deviceId
to see the change in extension attributes.
Get-DeviceBitlockerRecoveryKey
Get-DeviceBitlockerRecoveryKey
obtain and display all BitLocker recovery keys for a given $deviceId
. Under the hood this is a multi-step process (two functions in the module are used, but only one function is exposed outside the module) due to how keys are managed and stored in Entra/Intune. Useful to see how commands can be stacked and combined in the moment to do something more complex.
Module Example:
Get-DeviceBitlockerRecoveryKey -bearerToken $bearerToken -deviceId $deviceId
Underlying REST Endpoint:
https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys?`$filter=deviceId eq '$deviceId'
The exposed function uses of a second REST endpoint to obtain the recovery key in full for each $rKeyId
(escrowed key):
https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys/$rKeyId/?`$select=key
The module output at the command line will be a proper BitLocker recovery key (or one per line) if one exists.
Module Release Outcomes
I've had a number of folks reach out and express how useful this little module has been to help them wrap their heads around using Graph calls, both directly with HTTP requests, but also as a way they were able to clarify what they're looking to do with the official modules (pivoting to the equivalent cmdlets). When combined with awesome tools like Graph X-Ray, Graph Explorer, Postman, and Merill's Graph Permissions Explorer, cutting to the basics with a module like mine has proven useful in folks' distillations as they work through developing their own automations or mechanizations using Graph API. And that's outstanding — exactly the sort of outcome I was hoping for!
So go forward, give Graph API a try, and see if little tooling bits like this help you out! Good luck!