Cross Tenant Microsoft Graph Access Using Federated Identity Credential 😍
With Microsoft Entra Workload Identity Federation, Azure now supports a fully secretless authentication flow between managed identities and multitenant applications. This enables Azure Container Apps, Functions, Automation Accounts, and other workloads to securely access resources in another tenant without storing credentials at all. In this blog, we will build a native cross tenant authentication setup.
The following diagram illustrates the complete cross tenant authentication flow using a User Assigned Managed Identity (UAMI), a Federated Identity Credential (FIC), and a multitenant Microsoft Entra application.
- The automation workload, in this example an Azure Automation Account, is configured to use a User Assigned Managed Identity. This managed identity serves as the workload identity and eliminates the need for client secrets or certificates.
- A Federated Identity Credential is configured on the multitenant application registration in Microsoft Entra ID. The credential establishes a trust relationship between the managed identity and the application registration, allowing the managed identity to authenticate as the application through workload identity federation.
- In the customer tenant, administrator consent is granted to the multitenant application. This creates an Enterprise Application (service principal) and enables the required Microsoft Graph application permissions within the target tenant.
- When the automation workload requests access to Microsoft Graph, the managed identity first obtains a token that is used as a client assertion. Through the Federated Identity Credential, Microsoft Entra ID validates the trust relationship and issues a new access token on behalf of the multitenant application.
- The resulting token contains the application permissions granted in the customer tenant and can be used to securely access Microsoft Graph resources across tenant boundaries without storing or managing credentials.
The result is a modern authentication pattern based entirely on token exchange and workload identity federation. No certificates. No client secrets. No custom token broker modules.
Although every component can be configured through the Azure portal, I prefer using Infrastructure as Code whenever possible. Besides being repeatable and version controlled, it also makes it much easier to share and reproduce the solution. That’s why I created the following Bicep deployment, which provisions the complete cross tenant authentication setup from end to end.
metadata name = 'Graph API Application with Federated Identity Credential'
metadata description = 'Deploys a Graph API Application with a Federated Identity Credential that trusts a User Assigned Managed Identity to impersonate the application.'
metadata owner = 'Brian Veldman'
metadata version = '1.0.0'
/* TARGET SCOPE */
targetScope = 'subscription'
/* EXTENSIONS */
extension 'br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.8-preview'
@description('Defining our input parameters')
param parLocation string = 'westeurope'
@description('Defining our variables')
var resourceGroupName = 'rg-fic-demo'
var managedIdentityName = 'mi-fic-demo'
var applicationDisplayName = 'fic-demo-app'
var applicationName = 'fic-demo-app'
module rg 'br/public:avm/res/resources/resource-group:0.4.3' = {
params: {
name: resourceGroupName
location: parLocation
}
}
module uami 'br/public:avm/res/managed-identity/user-assigned-identity:0.5.1' = {
scope: resourceGroup(resourceGroupName)
params: {
name: managedIdentityName
}
}
resource app 'Microsoft.Graph/applications@v1.0' = {
displayName: applicationDisplayName
uniqueName: applicationName
signInAudience: 'AzureADMultipleOrgs'
defaultRedirectUri: 'https://admin.microsoft.com'
resource msiFic 'federatedIdentityCredentials@v1.0' = {
name: '${app.uniqueName}/msiAsFic'
description: 'Trust the workloads UAMI to impersonate the App'
audiences: [
'api://AzureADTokenExchange'
]
issuer: '${environment().authentication.loginEndpoint}${tenant().tenantId}/v2.0'
subject: uami.outputs.principalId
}
dependsOn: [
uami
]
}When checking the Azure portal, we can see the deployed resource group, including the user assigned managed identity.
To test cross tenant authentication, we need the Client ID of the user assigned managed identity. Please securely save it, as we will need it later.
When checking Microsoft Entra ID, we can see that the app registration has been created, including the Federated Identity Credential, which is linked to the User Assigned Managed Identity.
If we check the Federated Identity Credential itself, we can see the selected managed identity, which is the User Assigned Managed Identity.
To test cross tenant authentication, we need the Client ID of the multi-tenant app registration. Please store it securely, as it will be required later.
We need to add API permissions to the fic-demo-app so that, when we grant consent in the target tenant, the application has the required permissions.
We can now grant consent in the target tenant using the following URL. Afterward, the fic-demo-app will be listed under Enterprise Applications in the target tenant. We need the Tenant ID of the target tenant. Please securely save it, as we will need it later.🔥
https://login.microsoftonline.com/{targetTenantId}/adminconsent?client_id={clientId}Now that the configuration is complete, it’s time to test the cross tenant authentication. You could do this from an Azure VM with the User Assigned Managed Identity (UAMI) assigned and the Azure PowerShell modules installed. However, for automation scenarios I prefer a serverless approach using Azure Container Apps Jobs, Azure Functions, or Azure Automation.
Get Brian Veldman’s stories in your inbox
Join Medium for free to get updates from this writer.
Before running the script, update the Target Tenant ID, the Client ID of the multitenant application, and the Client ID of the UAMI. Once configured, the script will validate the federated authentication flow and allow you to access Microsoft Graph in the target tenant without using secrets or certificates.
# =====================================================================
# Configuration
# =====================================================================
$targetTenantId = "<TARGET-TENANT-ID>"
$appClientId = "<MULTITENANT-APP-CLIENT-ID>"
$uamiClientId = "<UAMI-CLIENT-ID>"
# =====================================================================
# Login with User Assigned Managed Identity
# =====================================================================
Connect-AzAccount -Identity -AccountId $uamiClientId | Out-Null
# =====================================================================
# Get Managed Identity token for Azure AD Token Exchange
# =====================================================================
$rawToken = (
Get-AzAccessToken `
-ResourceUrl "api://AzureADTokenExchange"
).Token
# Handle both SecureString and String - This is needed for support both IaaS and PaaS on Azure
if ($rawToken -is [System.Security.SecureString]) {
$miToken = [System.Net.NetworkCredential]::new(
"",
$rawToken
).Password
}
else {
$miToken = $rawToken
}
Write-Host "Managed Identity token acquired"
# =====================================================================
# Exchange MI token for App Registration token
# =====================================================================
$body = @{
client_id = $appClientId
scope = "https://graph.microsoft.com/.default"
grant_type = "client_credentials"
client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
client_assertion = $miToken
}
$tokenResponse = Invoke-RestMethod `
-Method POST `
-Uri "https://login.microsoftonline.com/$targetTenantId/oauth2/v2.0/token" `
-Body $body `
-ContentType "application/x-www-form-urlencoded"
$graphToken = $tokenResponse.access_token
Write-Host "Graph token acquired"
# =====================================================================
# Test Microsoft Graph
# =====================================================================
$headers = @{
Authorization = "Bearer $graphToken"
}
$users = Invoke-RestMethod `
-Uri "https://graph.microsoft.com/v1.0/users?`$top=5" `
-Headers $headers `
-Method GET
$users.value | Select-Object displayName, userPrincipalName, idFor this example I used an Azure Automation Account where the User Assigned Managed Identity is connected to.
I’ve created an Azure Runbook within the Azure Automation Account to run the script and filter for users with Admin within their name. As you can see we successfully authenticated to the target tenant and fetched the user. We can now do every automation based on the API consents.😍
That’s all for today. As you have seen, Microsoft Entra Workload Identity Federation enables secure cross tenant access without relying on client secrets or certificates. By combining a User Assigned Managed Identity with a Federated Identity Credential and a multitenant application, you can build modern and secure automation solutions across tenants using native Azure capabilities. The Azure Bicep code is available in my GitHub repository: GitHub — brianveldman/azure-bicep. Feel free to explore it, use it in your own projects, and leave a star if you found it useful. I hope this article helped you better understand how to implement secretless cross tenant authentication in Azure. If you have any feedback, questions, or ideas for future articles, please let me know in the comments. Thank you for reading, and I look forward to sharing more Azure and Microsoft Entra content with you soon. Until next time, take care! ❤️







