AppGov Score Blog

Check out our latest updates!

Authenticating to the Microsoft Graph API with PowerShell

August 6, 2024 Louis Mastelinck

Authenticating to the Microsoft Graph API with PowerShell

I still often encounter the use of user accounts to run automation, scripts, and applications to connect with Microsoft services. By user account, I mean a user object created specifically for running certain tasks. These accounts typically have complex passwords. Over time, the passwords for these service accounts are often forgotten, and none of the IT team members dare to touch them. If we change the password, we hope it doesn't break anything production-critical. It's clear that relying on a single password-protected account for a highly important service is a bad idea.

Even Microsoft is gradually pushing us to move away from these user-based service accounts and for good reasons. For example, Microsoft has announced that “starting in early 2025, MFA (Multifactor Authentication) for sign-in to Azure Command Line Interface (CLI), Azure PowerShell, and Infrastructure as Code (IaC) tools will gradually roll out to all tenants.” This means that if you have a regular user account (username and password) to interact with the Azure interfaces, you will have to use MFA. And as we all know, automations cannot deal with MFA prompts. I predict that in the future, Microsoft will not allow any interaction via user accounts to one of their APIs without MFA.

How Should You Authenticate Your Script or App to Your Cloud Tenant?

What is the right way to authenticate your script or application to your cloud tenant? Service principals are the way to go! 

If you look into the Microsoft docs, you will notice that "service principals" is a broad term, and discussing all the options would be a long journey. So in this blog post, I’m going to focus on a specific scenario, although this scenario will most likely cover the majority of the use cases IT professionals will encounter.

The Scenario: Interact with the Graph API via PowerShell

In this blog post, I am going to demonstrate how to authenticate to the Graph API using PowerShell.  

We will use an app registration to interact with the Graph API via code. When you register your application with Microsoft Entra ID, you create an identity for your application that allows it to integrate with Entra ID.  

Creating an Entra ID App Registration

Creating an app registration is simple. Go to entra.microsoft.com > Applications > App Registrations > New App Registration. Give your app registration a fitting name and register it as a single-tenant app. 

An app registration provides two methods of authentication. Each method has advantages and disadvantages, and you should uphold some general best practices.  

Creating a Client Secret

App secrets are very easy to use. They are straightforward to create and compatible with various PowerShell modules or directly in your code. However, they come with a significant disadvantage: in reality, an app secret is just a very long string. Therefore, you need to be very careful about how you use and store this app secret in your code. 

You want to avoid having the app secret in clear text in your code. There have been many cases of breaches where applications get compromised due to clear text secrets in their code. To help mitigate this risk, Microsoft has even created secret scanning for detecting clear text secrets in DevOps. 

Additionally, the secret’s lifetime should not be too long. While the maximum lifetime of a secret is 2 years, Microsoft recommends setting a secret lifetime of 6 months.  

It’s important to note that before April 2021, there was an option to set the Client Secret Expiration to never, which essentially gave the secret a 99-year lifetime. You can learn more about why Microsoft made this change and the additional recommendations they have for Client Secrets. 

Creating a Client Secret in the Microsoft Entra Portal

Figure 1: Creating a Client Secret in the Microsoft Entra Portal

Tip: If you are going to use secrets, make sure you give them a clear description. During the renewal of the app secret, it is often unclear what the app secret is used for. Proper documentation and naming conventions can do wonders! 

When creating a secret, you will only see the secret once. Make sure to note it down in your password manager.  

Viewing the Secret of my App Registration in the Microsoft Entra Portal
Figure 2: Viewing the Secret of my App Registration in the Microsoft Entra Portal.

Creating a Self-Signed Certificate to Authenticate to the Microsoft Graph API

You can use a certificate to authenticate to the Graph API by uploading it to your app registration. These certificates can be self-signed. 

With the following code, you can create your own self-signed certificate.  

$certName = "CN=LouSecApp" 
$certDirectory = "C:\temp" 
$certPassword = ConvertTo-SecureString -String "ThePasswordOfMyCertificate" -Force -AsPlainText 

# Create the directory if it does not exist 
New-Item -ItemType Directory -Path $certDirectory -Force 

 # Define file paths 
$cerFilePath = Join-Path -Path $certDirectory -ChildPath "LouSec.cer" 
$pfxFilePath = Join-Path -Path $certDirectory -ChildPath "LouSec.pfx" 

# Create a self-signed certificate 
$cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Subject $certName -KeyExportPolicy Exportable -KeySpec Signature -NotAfter (Get-Date).AddMonths(6

 # Export the public key to a .cer file 
Export-Certificate -Cert $cert -FilePath $cerFilePath 

 # Export the certificate to a .pfx file 
Export-PfxCertificate -Cert $cert -FilePath $pfxFilePath -Password $certPassword

Write-Output "Certificate created and exported to $certDirectory"

 

The script will generate two file types: 

  • The .cer file contains the public key and the digital certificate but does not include the private key. 
  • The .pfx file contains both the public and private keys along with the digital certificate. This file allows for importing/exporting the certificate and its keys. 

For example, if another system needs to use the same app registration and you want to reuse the same certificate, you can install it using the .pfx file. 

After installing the certificate, you can verify the Thumbprint in its properties. Depending on where you installed your certificate, you can find it in CertMgr under Personal > Certificates.

Validating the Thumbprint of a certificate in the Certificate Properties

Figure 3: Validating the Thumbprint of a certificate in the Certificate Properties.

Now that you have created your certificate, you can upload the .cer file in your app registration: 

Uploading the Certificate in the Microsoft Entra Portal
Figure 4: Uploading the Certificate in the Microsoft Entra Portal.

You will now find your certificate and its thumbprint saved under your app registration. We will need this thumbprint later in our code to authenticate to the Graph API.

Identifying the Certificate in the Microsoft Entra Portal by its Thumbprint
Figure 5: Identifying the Certificate in the Microsoft Entra Portal by its Thumbprint.

Authenticating to the Microsoft Graph API

Based on my experience, the Microsoft Graph API is essential for automating nearly every action in your cloud tenant. It is under active development and serves as the single point of entry for all automation tasks. It is crucial to assign the least privileged permissions to your app registration to ensure security. In this blog post, I will demonstrate how you can use an app secret or a certificate to authenticate to the Graph API. 

Authentication with a Client Secret 

In this example I don’t want to use any PowerShell module and just rely on pure API calls using web requests. I also want to avoid storing my secret in plain text within my code. Since I will be running this code on my personal device, I intend to save my secret as a secure string in a file for added security. 

$client_secret = "<your secret here>" 
$ClientSecretPath = "C:\ClientSecret.txt" 
$client_secret | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File "C:\ClientSecret.txt"  


This is what your ClientSecret.txt should look like:

Picture8-1Figure 6: Content of an Encrypted Secret.

Once you have ClientSecret.txt you can use it in the authentication flow in the script below.

# Parameters 
$tenantId = "<Tenant id> 
$client_id = "<client id>" 
$ClientSecretPath = “C:\ClientSecret.txt” 
$resource = "https://graph.microsoft.com" 

$client_secret = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR((Get-Content $ClientSecretPath | ConvertTo-SecureString)))) 

 # Get Access Token 
$tokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/token" 
$body = @{
     'resource'     = $resource 
     'client_id'      = $client_id 
     'client_secret' = $client_secret 
     'grant_type' = 'client_credentials' 
}

$response = Invoke-RestMethod -Method Post -Uri $tokenUrl -ContentType "application/x-www-form-urlencoded" -Body $body 
$access_token = $response.access_token 

 # Headers 
$headers = @{ 
     "Authorization" = "Bearer $access_token

 # User ID or User Principal Name (UPN) 
$userId = "louismastelinck@mastelinck.onmicrosoft.com" 

# API Endpoint 
$uri = "https://graph.microsoft.com/v1.0/users/$userId

 # Make the API call 
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get 

# Output the response 
$response

 

When analyzing the code above, you'll notice that we use the secret that was previously encrypted and stored in our file. In the "# Get Access Token" section of the script, we create a body containing all the information from our app registration. We then perform an authentication request and receive valid access tokens in return. These tokens are placed in the headers of each request we make to the Graph API.

Authentication with a Certificate

Implementing certificate-based authentication without any modules can be quite challenging. Fortunately, we have the Microsoft Graph SDK at our disposal, which conveniently assists us in authenticating using the certificate we just created. 

If the Graph SDK isn’t already installed and you wish to execute some demo code, you can proceed with its installation by following these steps:

Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser 
Install-Module -Name Microsoft.Graph.Users -Scope CurrentUser


The code below will use the thumbprint of our certificate to authenticate.
 

# Load the certificate from the certificate store 
$CertThumbprint = "9ce6763a1412633fc842e58ff13e8b93fe5855d4" # Thumbprint of the certificate used for authentication 
$Cert = Get-ChildItem Cert:CurrentUser\My\$CertThumbprint 

# Define the necessary parameters 
$TenantId = "f1eb716f-3738-4f1d-a41b-06cbf378a8a4" # Your Entra ID Tenant ID 
$ClientId = "3ec2a898-4795-4629-9b61-27431fa6671b" # Your Application (Client) ID 

# Connect to Microsoft Graph 
$connection = Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -Certificate $Cert 

$userUPN = "louismastelinck@mastelinck.onmicrosoft.com" 

# API Endpoint 
$uri = "https://graph.microsoft.com/v1.0/users/$userUPN

# Make the API call 
$response = Invoke-MgGraphRequest -Method GET -Uri $uri -Headers $connection.Headers 

# Output the response 
$response

 

Once you have the certificate installed, you can easily use it in your code. The Microsoft Graph SDK does all the heavy lifting for you. 

Conclusion  

If you're new to creating automation with app registrations, these samples are an excellent starting point! We've demonstrated how to use either an app client secret or a certificate to authenticate with the Graph API. The code is flexible, so you can modify it as needed. Remember to manage your certificates and secrets carefully, keeping track of their expiration dates to avoid any issues. 

Are you curious how your Client Secrets and Certificates are configured in your Application Registrations?  

ENow has a no-cost utility, AppGov Score, that will scan your Entra ID Application Registrations and identify how many registrations have been configured with Client Secrets, as well as how many Client Secrets are about to expire, how many have already expired, and those Clients Secrets with non-standard expirations (past the 2-year lifetime recommendation). The results of this free assessment will give you a quick look at whether your application registrations are configured in line with Microsoft’s recommended practices. 

Get Your Score!

Share This:

Louis Mastelinck

Written by Louis Mastelinck

Louis Mastelinck is a Belgian Soc analyst and security consultant with a passion for keeping the digital world secure. Specializing in incident response and the Microsoft Security stack (MDE, MDO, MDI, MDCA, Sentinel, ...), he excels at neutralizing threats and protecting organizations. As a Microsoft MVP and GCFA-certified professional, Louis brings a wealth of expertise to the table.