Sometimes you need to programmatically perform tasks in your Microsoft tenant using Microsoft Graph PowerShell, but you may not have the option to or want to install the SDK. Not only is the SDK somewhat cumbersome to install, but it is also increasingly frustrating to maintain.
Thankfully, as the Graph API is easy to access, you can utilise built-in PowerShell functions to obtain access tokens and make queries. This traditional way of doing this however is to utilise an unattended method of authentication, such as a secret key with a custom app registration. These secret keys are often stored within your code, but can also be securely accessed through other means, like an Azure Key Vault.
Another way of authenticating to Microsoft Graph is by doing so interactively using modern authentication. Unfortunately, the integrated browser experience is not available without installing the SDK, so to work around this and get a similar experience, the device authorization grant flow can be used to obtain an access token.
In this tutorial, I am going to show you how you can use PowerShell, without installing the Microsoft Graph PowerShell SDK, to obtain an access token using Device Code authentication.
Starting the device authorisation request
The first step is to start the authorisation process by requesting a user code and device code from the authentication server. The below code will start the process. There are two parameters you must include in the body of your request, these are client_id and resource.
- The client_id is the service principal (or application) in Microsoft Entra you want to connect to, below I have defined the built-in Microsoft Graph Command Line Tools application.
- The resource defines what the access token will be valid for. In this case Microsoft Graph.
$ClientID = '14d82eec-204b-4c2f-b7e8-296a70dab67e'
$uri = "https://login.microsoftonline.com/common/oauth2/devicecode"
$body = @{
client_id = $ClientId
resource = "https://graph.microsoft.com/"
}
$Request = Invoke-RestMethod -Uri $uri -Method POST -Body $body
Completing the interactive login
The authorisation request response will contain information pertinent to completing the authentication. Specifically, it will contain the verification URL which you must navigate to and the user code which you will need to enter into the web browser before signing in. You can view this information by entering the $Request variable into your PowerShell session.
Open your preferred web browser and navigate to: https://microsoft.com/devicelogin. Then when prompted for the code, enter the user code stored in the $request variable and click Next.
Complete the usual sign-in experience and you will then be asked to close the window. Once this is done, return to your PowerShell session and proceed to the next step.
Obtaining the access token
Now that the user has been authorised, any standard application would normally be configured to poll the /token endpoint by making POST requests using the device code to obtain an access token. Instead, we must do this manually to obtain the access token.
In the below request, we are making a POST request to the /token endpoint using the required grant_type, device_code and client_id as defined in the Microsoft documentation. The response (include the access token) is then stored in the $token variable.
$TokenReq = @{
Method = 'POST'
Uri = "https://login.microsoftonline.com/common/oauth2/token"
Body = @{
grant_type = "urn:ietf:params:oauth:grant-type:device_code"
code = $Request.device_code
client_id = $ClientId
}
}
$Token = Invoke-RestMethod @TokenReq
You can check the response was successfully stored by entering $token into your PowerShell session.
Using the access token
Once the access token is obtained, it will have a short lifetime unless refreshed. Although generally speaking it will be long enough for most tasks and I will cover that in a separate post.
The access token can be defined at the start of your script and stored in a variable, allowing it to be re-used for multiple requests. The simplest way of doing this is with the following code.
$header = @{
'Authorization' = "Bearer $($token.access_token)"
}
Using the Invoke-RestMethod cmdlet, the $header variable can be included with the -Header parameter for any request. Here is an example where I invoke a GET request against the /v1.0/groups endpoint.
$header = @{
'Authorization' = "Bearer $($token.access_token)"
}
$URI = "https://graph.microsoft.com/v1.0/groups"
Invoke-RestMethod -Uri "$URI" -Headers $Header
You should then receive a successful response.
Full script to obtain an access token for Microsoft Graph using a device code
To simplify the process, here is a small script which will initiate the device authorisation grant flow, then it will launch your default web browser with the verification URL and save the user code to your clipboard. When the browser launches the script will pause until you return to it. Lastly, it will obtain the access token ready to be used.
#Define the ID of the application you wish to connect to.
#This has been prefilled with the Microsoft Graph Command Line Tools application.
$ClientID = '14d82eec-204b-4c2f-b7e8-296a70dab67e'
#Request a device code
$DeviceCodeRequestParams = @{
Method = 'POST'
Uri = "https://login.microsoftonline.com/common/oauth2/devicecode"
Body = @{
client_id = $ClientId
resource = "https://graph.microsoft.com/"
}
}
$Request = Invoke-RestMethod @DeviceCodeRequestParams
#Copy the device code to your clipboard
Set-Clipboard -Value $Request.user_code
#Launch default web browser at https://microsoft.com/devicelogin
Start-Process $Request.verification_url
#Notice and pause processing, press ENTER to continue
Write-host "`n User code $($Request.user_code) copied to clipboard!... It expires in 15 minutes `n" -ForegroundColor Yellow
Write-host "LAUNCHING WEB BROWSER, please complete the login and click ENTER when complete `n" -ForegroundColor Yellow
pause
#Obtain access token once authenticated
$TokenReq = @{
Method = 'POST'
Uri = "https://login.microsoftonline.com/common/oauth2/token"
Body = @{
grant_type = "urn:ietf:params:oauth:grant-type:device_code"
code = $Request.device_code
client_id = $ClientId
}
}
$Token = Invoke-RestMethod @TokenReq
#The token accessible at "$Token.access_token"
Hi Daniel,
I get the following error while running the code.
Invoke-RestMethod : {“error”:”invalid_request”,”error_description”:”AADSTS50059: No tenant-identifying information found in either the request or implied by any provided credentials. Trace ID: 966f6dd2-4346-49cf-8f2f-cc11c590e600
Correlation ID: 7a8a3fc1-bab5-4826-b9eb-c822aacb1e56 Timestamp: 2024-01-07 23:37:30Z”,”error_codes”:[50059],”timestamp”:”2024-01-07
23:37:30Z”,”trace_id”:”966f6dd2-4346-49cf-8f2f-cc11c590e600″,”correlation_id”:”7a8a3fc1-bab5-4826-b9eb-c822aacb1e56″}
At line:14 char:12
+ $Request = Invoke-RestMethod @DeviceCodeRequestParams
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Hi Dinesh. Does the Microsoft Graph Command Line Tools application exist in your tenant?