This is a post about using PowerShell and Microsoft Graph to access data in Azure AD, Intune and Office 365. The GUI management of these Microsoft 365 technologies is constantly evolving, but there will always be things that can’t be done that way. Microsoft Graph approaches the problem from the other direction. It provides an endpoint and API to access the entire dataset. You can then write your own scripts or applications, using the object model of the whole of the Microsoft 365 suite of products.
You will find a few blogs describing how to use PowerShell and Graph, and a collection of sample scripts for Intune. These are outstanding bits of work, but it is hard from these to understand what is really going on. If you just want to run a script to perform one of the functions in the samples, then you don’t need to know any more. But if you want to create your own functions, then this post is about the basic ideas behind the scripts.
If you have not used Microsoft Graph, then it is a little difficult to get over the first threshold of what it is about. The best place to start is Graph Explorer. This provides access to the data; then you can figure out how to use it.
The first thing you notice is that there is a v1.0 and a beta version. Beta is the more interesting. To use Graph Explorer, you need to know what you are exploring for: a bit of a contradiction. If you enter: https://graph.microsoft.com/beta/#metadata you will see the data model. Drill down one, for example: https://graph.microsoft.com/beta/users/ and you see the response to a request for Users. You also see the metadata of the request. Enter the metadata request: https://graph.microsoft.com/beta/$metadata#users and you will see the vast data model of the user entity. You will also see that it is based on the Open Data Protocol (OData). I really don’t know how you would find your way around the data model, except by starting with the Microsoft Graph REST API Beta reference. This is the Reference index:
Next up is why you would use Microsoft Graph. The ambition behind Graph is much larger than the ability to make obscure queries. Graph is described as a “programmability model”, with SDK’s for .NET, Node.js, PHP, Python, Ruby etc. The intention is to enable developers and third party vendors to build applications that have access to data from Teams, Exchange, SharePoint, Dynamics. You can use connectors to bring other data into Graph, and to export Graph data into other databases.
The potential is extraordinary. You could use it to post a message into Teams from a transaction in ServiceNow. Or you could build a new kind of collaboration app with Teams and SharePoint as the underlying data models but an entirely different user interface
This may explain why there is very little focus on using Graph with PowerShell. You can use PowerShell to execute queries, or perform obscure functions not handled with cmdlets, but that is not the central purpose of Graph. Nevertheless, Graph IS useful for obscure queries, and arguably is the best way to access the raw data. I recently needed a query to find all devices where the logged on user was not the assigned user in Intune. I don’t think it is possible to extract this in any other way.
The basic steps for using PowerShell with Graph are these:
- Use a pre-existing Enterprise Application, or register a new one
- GET or POST the entities and properties using the Invoke-RestMethod cmdlet
- Manipulate the data in PowerShell.
An Azure AD “application” is a collection of attributes for authenticating with the Microsoft Identity platform i.e. with http://login.microsoftonline.com/common/oauth2. An “Enterprise Application” is an instance of an existing Microsoft or third party application, with a service principal created for the specific tenant. So the instance of Microsoft Teams in your tenant is an Enterprise Application.
Applications have several important attributes:
- Consent of the administrators to run in the tenant
- Method of authenticating
- Scope of permissions.
You can see, in the Enterprise Applications blade, an instance of many applications has already been created in the tenant.
The Enterprise Application already contains the method of authenticating, and the scope of permissions. For example, the pre-existing Microsoft Intune PowerShell application allows you to connect to Graph for Intune devices and directory data. The permissions are delegated i.e. based on the permissions of the signing in user. Consent will be obtained from an administrator the first time it is used.
If you want a different method of authenticating, or different permissions, then you need to register a new “application”. For example, if you wanted to use PowerShell to connect to Microsoft Graph and post a message in Teams about Intune devices, you would create a custom App Registration. If you were building a new application with one of the Graph SDK’s, then you would register that application.
This is why, in some blog posts, you will see instructions for registering an application, and in others, not. If you use one of the powershell-intune-samples, then the application is the pre-existing Microsoft Intune PowerShell with an Application ID of d1ddf0e4-d672-4dae-b554-9d5bdfd93547. This is hard-coded into the scripts as:
$clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
$redirectUri = "urn:ietf:wg:oauth:2.0:oob"
$resourceAppIdURI = "https://graph.microsoft.com"
$authority = https://login.microsoftonline.com/$Tenant
Next, in order to be able to use Microsoft Graph, you need to authenticate and obtain a token. To do that, you use the Active Directory Authentication Library (ADAL) or the Microsoft Authentication Library (MSAL). The easiest way to do this in PowerShell is with the AzureAD module. You almost certainly have this already if you are working with Azure AD and PowerShell. To obtain a token, you need to submit a request containing the details of the application, and of the user requesting it.
The PowerShell Intune samples mentioned above use the same authentication function at the top of every sample. In fact, one of the samples is just the authentication process: GA_AdminConsent_Set.ps1. If you run this in the PowerShell ISE, then the session is authenticated and you can run whatever Graph requests you like.
If you were to just run the script as it is then, when the token expires, you are forced to authenticate again. However, if you turn the Region Authentication part of the script into a function, this renews a token if it has expired. You can then run this function before every request.
After getting a token, you use the native PowerShell cmdlet Invoke-RestMethod to interact with Microsoft Graph. A typical GET request is
Invoke-RestMethod –Uri [the resource URI] –Headers [the token obtained by authenticating] –Method Get. The explanation of how to construct the URI is here: Use the Microsoft Graph API. You can also test the URI in Graph Explorer. A typical POST request also requires the body, in JSON format, with values for the properties of the resource
A GET request returns either a complex data type, or an entity. The complex data type is the collection of resources e.g. users, devices, messages, groups, applications. The entity is a specific instance, always with an ID. You then need to manipulate this object in PowerShell to get what you want: a report; a list; a spreadsheet.
The GET request also returns only the first page of the response, if it is a collection, with a link to the next page. So a standard part of a request has to be a get-next-page loop. The snippet here: Paging; shows how to do it.
Working with the objects returned in the response is not easy, because Microsoft Graph exposes a complex data model. The best way (unless you already know what you are looking for) is to use Get-Member to find the properties of the returned object. Then you can obtain the property with the dot method. It is beyond the scope of this blog to go into it further.
There is a PowerShell SDK on the way, here: msgraph-sdk-powershell. When that arrives, it will mask what is going on with Application ID, token, URI, and the returned objects. But I think it is still useful to understand how it works underneath. The Intune sample scripts are here: powershell-intune-samples. And these samples have been developed into a PowerShell module for Intune here: Microsoft.Graph.Intune module. Credit to Dave Falkus for these. But bear in mind that these are only for Intune, and that is a very small part of the capability of Microsoft Graph.