AppGov Score Blog

Check out our latest updates!

Connect to the Microsoft Graph API securely using a Function App and Azure Key Vault

October 24, 2024 Louis Mastelinck

Connect to the Microsoft Graph API securely using a Function App and Azure Key Vault

In a previous blog post, we demonstrated how to authenticate to the Microsoft Graph API using two different methods. The focus was on running a local script on your device, with the app secret securely stored in an encrypted format or using a self-signed certificate to authenticate as the app registration for communicating with the Microsoft Graph API. That post emphasized the importance of using app registration with more secure authentication methods rather than relying on user accounts.  

Based on popular demand, we’re now taking this automation to the next level. Instead of running the script locally or on a server, we’ll use an Azure Function App to handle the code execution. Azure Function Apps offer several benefits:

  • For simple scripts, it’s often very cost-efficient (no hardware or license costs) 
  • Can run fully isolated within your network 
  • No maintenance or patching required 
  • Seamless integration with Azure Key Vault for storing secrets and certificates 

By the end of this blog post, you’ll be able to set up the necessary infrastructure to securely store secrets, create an Azure Function App with basic code to interact with the Microsoft Graph API, and ensure that only the managed identity can access the secret. 

 

The Design 

Before we dive into the Azure portal to create the necessary resources, let’s start by outlining our design. 
We will start by creating an app registration with the necessary permissions to the Microsoft Graph API. The secret we generate during the app registration will be safely stored in an Azure Key Vault. Next, we will create a Function App. This Function App will run our basic script and can be triggered based on time or an HTTP request. 

Attached to this Function App, we will create a system-assigned managed identity. This system-assigned managed identity will allow us to specifically define that only this Function App can access the vault. Another advantage of using a system-assigned managed identity is that the authentication is managed by Microsoft. If the Function App were to be deleted, the system-assigned managed identity of the Function App would also be deleted, ensuring there are no standing permissions in the Key Vault. 

Inside our Function App, we will not place the app secret in clear text. Instead, we will use a reference to the Key Vault location where the app secret is stored. The Function App simplifies the process by fetching the secret at runtime, which is another advantage: We don't need to alter our code; our code contains a reference to the vault. 


Figure 1: Design Graphic 


As a recap, in our design we only allow a system-managed identity to access secrets
to ensure a higher level of security and minimize potential risks. Here’s why: 

  1. Limit Access to Sensitive Data: The secret (such as an app secret or certificate) is a critical piece of information used for authentication. By allowing only the managed identity to access it, you ensure that the secret is not exposed to other users, apps, or scripts. This reduces the risk of unauthorized access.
  2. Automatic Identity Management: System-assigned Managed identities are automatically managed by Entra ID, which means you don’t need to manually handle credentials or rotate them.
  3. Least Privilege Principle: Managed identities enforce the principle of least privilege, meaning that only the specific application or resource (e.g., an Azure Function) gets access to the secret. This helps avoid any unnecessary permissions being granted to other entities that don’t need them.
  4. Identity lifecycle: If the Function App, or any other the resource using a managed identity, is deleted. The system-assigned managed identity is also removed automatically. This ensures that there are no lingering permissions or access rights that could be exploited later.
  5. No Hard-Coded Credentials: By using a managed identity to access the secret in Azure Key Vault, you avoid hard-coding sensitive information like passwords or secrets into your code. Instead, the Function App dynamically fetches the secret at runtime, making the whole process more secure.

Overall, using a managed identity protects sensitive app registration secrets and simplifies secure access management for Entra ID Application Registration.

 

Create the App Registration 

If this is your first time working with an app registration, don’t worry—I’ve got you covered. First, navigate to Entra, then go to Applications > App registrations > New registration. 

Give your app registration a relevant name and set it as a single-tenant app. Once the registration is created, head over to the API permissions tab to assign the required permissions. Since we’ll be running our script in an Azure Function without any user interaction, we’ll assign application permissions. In this case, I'll grant the app registration permission to read all users in the tenant. 

A screenshot of a computer

Description automatically generated
Figure 2: Giving the app registration permissions to the Microsoft Graph API 

 

As a final step, let’s create an app secret. Navigate to Certificates & secrets > New client secret and generate a new secret

for your app registration.

A close-up of a screen

Description automatically generated
                                  Figure 3: Creating an app secret. 

 

Hint - Azure Key Vault   

Create an Azure Key Vault 

 Now it’s time to create our Key Vault. Azure Key Vault is a cloud service provided by Microsoft Azure that securely stores and manages sensitive information such as secrets and certificates. It helps ensure that critical data is protected and accessible only to authorized applications and users. We are going to use a Key Vault to safely store the app secret of our app registration and control who or what can read secrets inside of the vault. 

Head over to Azure Portal, search for Key Vaults, and select Create to set up a new Key Vault. 

Warning - Azure Key Vault

To add a secret to the Key Vault, you'll need to have the Key Vault Administrator role. This permission can be granted by navigating to Access Control (IAM) > Add > Add role assignment, then assigning the Key Vault Administrator role to yourself or the appropriate user. 

Next, go to Objects > Secrets > Generate/Import and fill in the necessary information. Make sure to enter the secret you just created during your app registration. As a best practice, don’t forget to specify an expiration date for the secret to ensure security and compliance.  

App Secret in Azure Key Vault
Figure 4: Filling in the App Secret in the Key Vault 

Great! We've successfully added the secret to the Key Vault. We'll revisit the Key Vault later when we begin coding in our Function App to retrieve and use the secret.  

 

Create the Function App 

We will use a consumption-based function app that runs PowerShell Core. After the creation of your resource, we still need to do some basic configurations.  

System-assigned identity 

First, we need to enable a system-assigned identity for our Function App. This allows the Function App to act like a persona, meaning we can assign it permissions to access other Azure resources. In our scenario, we will grant the managed identity of our Function App the necessary permissions to read secrets from our Key Vault. By design, I only want the Function App to be capable of accessing the secret that is safely stored away in the key vault.  

Enable the System-assigned managed identity in the identity tab.  

A screenshot of a computer

Description automatically generated
Figure 5: Enabling a Managed Identity on a Function App.

Now go back to your key vault > Access Control (IAM) > add > assign the Key Vault Secrets Users > assign it the managed identity of your Function App. 

A computer screen shot of a computer

Description automatically generated
Figure 6: add the system assigned managed identity as Key vault secret user 

Now, your Function App has just enough permissions to read the secret we have stored there.  

Get the secret in your code 

In your Key Vault, open your secret entry and copy the Secret Identifier. A screenshot of a computer

Description automatically generated
Figure 7: Fetching the secret Identifier from a secret in the key vault 

Go back to your Function App > Environment variables > App settings > add > create a new secret. There are 2 ways you can add a value pointing to your Key Vault.  
 
You can place a reference to your Secret Identifier of your secret: @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) 

Or you can do it as I did below:  

@Microsoft.KeyVault(VaultName=myvault;SecretName=mysecret) 

A screenshot of a computer

Description automatically generated
Figure 8: creating an environment variable that contains a dynamic reference to the secret identifier 

Back in our Function App, it is time to start writing code. Go to the overview of your function app > create function > choose a trigger that fits your use case, for example, an HTTP trigger.   

 The following code is a simple script that will fetch all users in our tenant. In the code, you will see that we use “$env:KEYVAULT” to fill in our $clientSecret dynamically. This variable points to the location we just defined in our environment variables.   

 
using namespace System.Net 

# Input bindings are passed in via param block. 
param($Request, $TriggerMetadata) 

# Write to the Azure Functions log stream. 
Write-Host "PowerShell HTTP trigger function processed a request." 

# Define the necessary variables 
$tenantId = " " #COMPLETE THIS  
$clientId = "" #COMPLETE THIS 

$clientSecret = $env:KEYVAULT 
$scope = https://graph.microsoft.com/.default 
$graphApiUrl = "https://graph.microsoft.com/v1.0/users" 

# Get an access token 
$body = @{ 
grant_type    = "client_credentials" 
scope         = $scope 
client_id     = $clientId 
client_secret = $clientSecret 
} 

$tokenResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -ContentType "application/x-www-form-urlencoded" -Body $body 

$accessToken = $tokenResponse.access_token 

# Call the Graph API to get all users 
$headers = @{

Authorization = "Bearer $accessToken"

} 

$response = Invoke-RestMethod -Method Get -Uri $graphApiUrl -Headers $headers 

# Output the users 
$users = $response.value 

# Write the users to the console 
Write-Host "Fetched Users:" 
$users | ForEach-Object { Write-Host $_ } 

# Interact with query parameters or the body of the request. 
$name = $Request.Query.Name 

if (-not $name) { 
    $name = $Request.Body.Name 
} 

$body = "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response." 

if ($name) {

$body = "Hello, $name. This HTTP triggered function executed successfully."

} 

# Include the fetched users in the response 
$body += "`n`nFetched Users:`n" 
$body += ($users | Out-String) 

# Associate values to output bindings by calling 'Push-OutputBinding'. 

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ 

    StatusCode = [HttpStatusCode]::OK 

    Body = $body 

}) 

 

Conclusion 

This blog post explains how to securely set up automation using an app registration to authenticate with the Microsoft Graph API. It covers how to safely store the app secret in Azure Key Vault and ensure that only the managed identity of the Function App can access it. Additionally, it highlights that rotating the secret has no impact, as the code dynamically fetches the latest secret from the Key Vault.  

It sounds more complicated than it is and hopefully this blog post has given you the steps to confidently set up this automation to authenticate with the Microsoft Graph API. Using App registration and managed identities is considered to be the next recommended way of managing authentication and access to resources.  

 

Many organizations we talk to are just starting their Application Security & Governance journey. It can be hard to get a handle on App Registrations, Secrets, Certificates, Permissions, etc... ENow's AppGov Score gives you a starting point.   

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! 


Check out our Community Forum and engage with experts like Louis about Entra ID! 

The AppGov Community Forum is moderated by Microsoft Security & Identity MVPs and subject-matter experts to answer your questions around Entra ID, managing Enterprise Applications, Application Registrations, and the impact of Tenant Settings on an application's lifecycle. 

 

Leave a Comment:

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.