In today’s world, businesses are driving cloud service adoption. At Lumagate, we find the adoption of cloud services impacts operational procedures, especially in the types of tasks traditional IT roles have performed (perhaps a blog for another day!). One area of particular significance is the transition from manual execution of tasks to automation. Microsoft has built a number of toolsets to accommodate this need, including Flow, Azure Logic Apps, Azure Functions, and Azure Automation. Automated tools need something to execute against however, and thus Microsoft is working to surface their cloud service APIs through the Microsoft Graph.
As a Microsoft Partner with Gold competencies in Cloud Productivity and Enterprise Mobility (among others), we see many use cases for Graph. However, Graph is delivered via a REST API, which many IT Pros are unfamiliar with. In addition, on our Professional Services side, we have a particular need to ensure our implementations are consistent, repeatable, and reliable. As such, PowerShell is a tool of choice for us during delivery. This blog series (which will be broken into a couple parts) describes how to leverage the Graph to configure Microsoft Intune via PowerShell.
Challenges to Solve
There are a couple of PowerShell wrapper modules for Graph available via GitHub and other repositories, in addition to a set of PowerShell samples published by the Intune team. However, all of the samples I’ve come across have deficiency in one of three areas (sometimes a combination):
- They require the Azure AD PowerShell module to be installed (as is the case with the Intune team’s PowerShell samples for Graph). This tends to be required only for the auth process, acquiring the OAuth token to talk to Graph. This is a heavy-handed approach (and arguably lazy) to acquire a token which is then used to talk to Graph.
- They require the ADAL DLLs to be installed. This is also used for the auth process, acquiring the OAuth token from Azure AD. Dropping DLLs into a customer’s environment is not always appreciated, and thus is something we like to avoid.
- They build their own OAuth connection, but require the admin to register an application with Azure AD, and then store the application ID and most egregiously, the client secret (as is the case with the PSMSGraph module). Storing a client secret should only be used for web applications where it can be protected in context of a service, and not for a portable PowerShell module, where untrusted personnel may be using the application.
So how do we solve these challenges? The answer lies in the Azure AD v2.0 authentication endpoint.
Modern Authentication with Azure AD v2.0
Azure AD v2.0 provides a number of features, but most interestingly is the ability to leverage OAuth 2.0 Authorization Code flow to authorize applications. This allows us to launch a native application (in this case PowerShell), and the following steps occur:
- The native application makes a request to the authorization endpoint for a code, supplying a string of permissions (also known as scopes) in the URL request, and the resource (i.e. service / API) which it wants to access.
- The authorization endpoint prompts the user to sign-in. After a successful authentication, the user is presented with a list of the permissions supplied in the URL request, and asked to authorize the application for the resource.
- The user authorizes the application, at which point the authorization endpoint returns an authorization code which is valid to redeem an access token with those permissions. The token expires in 10 minutes.
- The application presents the authorization code to the token endpoint. In the body of the request, it supplies the list of permission scopes (or a subset) that was used to obtain the authorization code, along with the resource to which it is requesting access.
- The token endpoint responds with an access token, valid for 1 hr. If the offline_access permission scope was requested, it also issues a refresh token, which can be valid for an extended time period (often 14 days, depending on context). The refresh token can be used to redeem new access tokens every hour without requiring the user to restart the whole authentication process.
- The native client makes a call to the resource (in this case Graph), bearing (i.e. supplying) the access token in the header to prove it is authorized to make the request.
A diagram from the Azure AD v2.0 Authorization Code flow documentation helps illustrate this concept.
In all the steps above, we never need to provide a client secret, allowing us to build a native application which can be used by anyone in any environment, provided they are authorized to perform the actions in their environment. This solves one of our challenges. In the next post, we will get into how we can implement this concept in PowerShell without DLLs or requiring the Azure AD module to be installed.