If you are looking to run automations against a single Microsoft tenant, its likely you would want to do so in a headless fashion. Headless, meaning you do not need to sit in front of a terminal window to authenticate. In this case, you want some service to run that is automatically authenticating and running Microsoft graph APIs to perform your automations. In other cases, you may want to create PowerShell scripts that tap into the Graph API directly vs using graph cmdlets as there are many API calls today that do not have subsequent cmdlets created.

If you are an MSP looking to run automations across the tenants you manage in Partner Cetner, check out my other article here: My Automations Break with GDAP: The Fix! – (tminus365.com)

In this article, I am going to show you how to leverage a service principal (aka App Registration) in Entra ID in order to be able to call Microsoft graph APIs and run automations in your tenant.

Creating a New Service Principal (App Registration)

The first step is creating a new service principal in Entra ID so that we have an identity that is going to be mapped to the API calls we will make in the future.

If you want to create the service principal with PowerShell, check out this script: Secure-App-Model/Create-App-Registration at main ยท msp4msps/Secure-App-Model (github.com)

*Note that if you leverage this script, you will still need to grant the permissions to the tenant as outlined in step 8 below.

  1. In the Entra ID Admin Center, go to Applications>App Registrations>+New Registration

2. Provide a display name (I recommend that this name have no spaces). Add A redirect URI of https://localhost:8400 and click Register

3. On the next screen, record the Application ID and Directory ID

4. Click into Certificates and Secrets > + New Secret

5. Add a Description and Expiration date

6. Record your Secret Value (Note: You will not be able to access it again after leaving this page)

7. Next Click on API Permissions. Here we can add the necessary API permissions we will want to leverage against this tenant. You can leverage the Microsoft Graph API documentation to see which permissions are required for each API Call

Ex. Permissions:

8. After permissions are added, you will need to grant consent for your tenant (Note: you need to be a Global Admin in order to perform this action)

Getting a Refresh Token

A refresh token can be leveraged to get access tokens for authentication headers we want to make to call Microsoft Graph APIs. The refresh token is good for 90 days when generated and should be stored in a very safe location.

Use the following PowerShell Script to generate an Access Token and RefreshToken. Replace the variables at the top with the Application registration variables you recorded earlier.

# Define your application (client) ID, secret, redirect URI, and tenant ID
$clientID = "YOUR_CLIENT_ID"
$clientSecret = "YOUR_CLIENT_SECRET"
$tenantID = "YOUR_TENANT_ID"
$redirectUri = "http://localhost:8400"
$scope = "https://graph.microsoft.com/.default"

# Step 1: Open the browser for user to authenticate and grant permissions
$authUrl = "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/authorize?client_id=$clientID&response_type=code&redirect_uri=$redirectUri&response_mode=query&scope=https://graph.microsoft.com/.default"
Start-Process $authUrl

# Manual step: After the user authenticates, they will be redirected to the provided redirectUri with the authorization code in the query string. 
# Capture this code manually.
$authorizationCode = Read-Host "Please enter the authorization code from the URL"

# Step 3: Exchange the authorization code for tokens
$body = "grant_type=authorization_code&client_id=$clientID&client_secret=$clientSecret&code=$authorizationCode&redirect_uri=$redirectUri&scope=$scope"
$headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' }
$tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/token"
$response = Invoke-RestMethod -Method POST -Uri $tokenEndpoint -Body $body -Headers $headers


$accessToken = $response.access_token
$refreshToken = $response.refresh_token

# Output the tokens
Write-Host -ForegroundColor Green "Access token is" $accessToken
Write-Host -ForegroundColor Blue "Refresh token is"$refreshToken

When you get prompted to enter an authorization code, grab the parameter from the URL:

As a best practice, copy the entire string and paste it in notepad. Only grab the code parameter and not the session state

The output will provide you an Access Token and RefreshToken. Store the RefreshToken in a safe location

Using the RefreshToken to get a new AccessToken

Your AccessToken is only going to be good for an hour so you will need to leverage another script to get a new AccessToken

function Get-AccessToken {
    param (
        [Parameter(Mandatory=$true)]
        [string]$clientID,

        [Parameter(Mandatory=$true)]
        [string]$clientSecret,

        [Parameter(Mandatory=$true)]
        [string]$tenantID = "common", # Your tenantID

        [Parameter(Mandatory=$true)]
        [string]$refreshToken, # Your refreshToken

        [string]$scope = "https://graph.microsoft.com/.default" # Default scope for Microsoft Graph
    )

    # Token endpoint
    $tokenUrl = "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token"

    # Prepare the request body
    $body = @{
        client_id     = $clientID
        scope         = $scope
        client_secret = $clientSecret
        grant_type    = "refresh_token"
        refresh_token = $refreshToken
    }

    # Request the token
    $response = Invoke-RestMethod -Method Post -Uri $tokenUrl -ContentType "application/x-www-form-urlencoded" -Body $body

    # Return the access token
    return $response.access_token
}

Leveraging the Access Token to call Graph APIs

The AccessToken is what we can pass into our Graph API call request headers in order to properly authenticate. Here is a sample script that requires an AccessToken to run. It leverages two Graph API endpoints to generate a License Report for the tenant:

Conclusion

Now that you have the fundamentals, the only thing you need to do now is making sure that your scripts/automations are able to get an AccessToken by having the correct permissions set on the App Registration for the API calls you are trying to make.

Share with the Community