← Back to list

Passwordless Cross-Tenant Azure SQL Access with User-Assigned Managed Identity

Introduction

Davit Paytyan · 2026-02-16 02:08 · 4 claps · 4.6 min read
#devops #microsoft-azure #sql-azure #identity-management #cloud-computing
Open on Medium ↗
Wiki topics: BIZ · Business Strategy ☁️ · DevOps & Cloud

Passwordless Cross-Tenant Azure SQL Access with User-Assigned Managed Identity

Introduction

While working on a real Azure project, I encountered a problem that seemed simple at first but turned out to be surprisingly confusing.

My application was running in Azure Tenant A, but it needed secure access to Azure SQL Database in Tenant B. The main requirement was clear:

  • no passwords
  • no secrets
  • no certificates
  • private network access only

Everything had to be fully passwordless.

The solution uses Managed Identity and Microsoft Entra ID to securely trust and exchange identities across tenants. This works with many Azure services (VMs, App Services, AKS, Azure Container Apps, Functions, and more).

In this article, I focus on the identity setup and validation, not application development. A Virtual Machine is used only for testing, because it’s easy to see what’s happening step by step.

This article assumes the infrastructure is already in place and explains how cross-tenant authentication works in practice.

And since this is my first article, I hope it saves you some of the time I lost figuring this out — happy reading! 😊

Prerequisites

The following are assumed to already exist:

  • Azure SQL Server and database in Tenant B
  • Two Azure virtual networks (one per tenant)
  • Non-overlapping address spaces
  • VNet peering between virtual networks
  • Azure SQL exposed via Private Endpoint
  • Private DNS is correctly configured and resolvable from both networks
  • A workload (VM / App) in Tenant A ( For testing purposes, we will use Windows VM from Tenant A)
  • Required permissions in both tenants to manage Entra ID, apps, and SQL users

This article does not cover:

  • SQL or database creation
  • Network or DNS setup

Architecture Overview

  • Tenant A owns the workload with a managed identity
  • Tenant B owns the data
  • Microsoft Entra ID performs federation and token issuance
  • Network communication occurs over a private network

Phase 1: Tenant A — User-Assigned Managed Identity

Create a user-assigned managed identity for the workload.

az login --tenant <TENANT_A_ID>
az account set --subscription <SUBSCRIPTION_A_ID>
az identity create \
  --name uami-cross-tenant \
  --resource-group <IDENTITY_RG> \
  --location <IDENTITY_LOCATION>

Values to Save

UAMI_CLIENT_ID This value comes from the clientId field in the command output. It is used by the workload at runtime when authenticating with Managed. Identity.

UAMI_PRINCIPAL_ID This value comes from the principalId field in the command output. It represents the managed identity object in Microsoft Entra ID and is. required for workload identity federation.

Important: Do not use clientId for federation. Federation only works with principalId.

Attach this managed identity to your workload (VM, App Service, AKS, etc.). For testing configuration, we will attach it to the VM at the end of the main solutions ( to be changed)

Phase 2: Tenant A — Multi-Tenant Application and Federation

Step 1: Create the Multi-Tenant Application

az ad app create \
  --display-name cross-tenant-app \
  --sign-in-audience AzureADMultipleOrgs

Save the following values for use in later steps : APP_CLIENT_ID: This value comes from the appId field. It is the Application (Client) ID and must be reused in both Tenant A and Tenant B.

APP_OBJECT_ID: This value comes from the id field. It is the Application Object ID, which exists only in Tenant A, and is required to configure federation.

Step 2: Create the Service Principal in Tenant A

az ad sp create --id <APP_CLIENT_ID>

Replace APP_CLIENT_ID with the value saved from the previous execution. This creates a tenant-local service principal for the same application created above

Step 3: Configure the Federated Identity Credential

az ad app federated-credential create \
  --id <APP_OBJECT_ID> \
  --parameters '{
    "name": "uami-federation",
    "issuer": "https://login.microsoftonline.com/<TENANT_A_ID>/v2.0",
    "subject": "<UAMI_PRINCIPAL_ID>",
    "audiences": ["api://AzureADTokenExchange"]
  }'

Use the following values:

  • APP_OBJECT_ID is the Application Object ID from the app registration (saved after Phase 2, step 1).
  • UAMI_PRINCIPAL_ID is the managed identity principalId (saved after Phase 1).
  • TENANT_A_ID is the tenant ID for the hosted application.

Phase 3: Tenant B — Service Principal and SQL Authorization

At this stage, Tenant A is fully prepared:

  • The managed identity exists
  • The multi-tenant application exists
  • Federation is configured correctly

Now we switch context to Tenant B, which owns the data and enforces authorization.

Step 1: Register the Application in Tenant B

The multi-tenant application created in Tenant A must have a corresponding service principal in Tenant B.

Important You must use the exact same Application (Client) ID that was created in Tenant A. Do not create a new app registration.

az login --tenant <TENANT_B_ID>
az ad sp create --id <APP_CLIENT_ID>

This command creates an Enterprise Application in Tenant B that represents the same application identity.

Step 2: Provision Azure SQL Access

Ensure the Azure SQL Server is configured for Microsoft Entra ID authentication.

  • An Entra ID administrator must already be assigned to the SQL Server
  • Log in to the database as an Entra ID admin

Create a database user for the federated application:

CREATE USER [cross-tenant-app] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [inter-tenant-app];
ALTER ROLE db_datawriter ADD MEMBER [inter-tenant-app];

Important Note About Testing Tools

Before validating the setup, one important limitation must be understood.

Interactive tools such as:

  • sqlcmd
  • SSMS
  • Azure Data Studio

does not support workload identity federation or token exchange.

These tools always request tokens directly for the managed identity and cannot use api://AzureADTokenExchange.

This means:

  • They will fail even if the configuration is correct
  • This is a tooling limitation, not a misconfiguration

Therefore, validation must be performed programmatically, using Microsoft-supported SDKs.

Validation: Running the Test from a VM

To verify that cross-tenant authentication works, we use a very small test program.

The VM is used only for testing. The same approach works from any Azure service that supports Managed Identity.

Step 1: Install .NET (one-time)

On the VM, open PowerShell and run:

winget install Microsoft.DotNet.SDK.8

Verify installation:

dotnet --version

Step 2: Create a Test Folder

mkdir token-test
cd token-test

Step 3: Create a Small Test App

dotnet new console

Add required packages:

dotnet add package Azure.Identity
dotnet add package Microsoft.Data.SqlClient

Step 4: Replace the Test Code

Open the file:

notepad Program.cs

Replace everything with the code below and save.

Test Code

using Azure.Core;
using Azure.Identity;
using Microsoft.Data.SqlClient;
Console.WriteLine("Starting authentication test...");
string managedIdentityClientId = "<UAMI_CLIENT_ID>";
string appClientId             = "<APP_CLIENT_ID>";
string tenantBId               = "<TENANT_B_ID>";
string sqlServer               = "<sqlserver>.database.windows.net";
string database                = "<database-name>";
string exchangeAudience = "api://AzureADTokenExchange";
// Get exchange token using Managed Identity
var miCredential = new ManagedIdentityCredential(
    ManagedIdentityId.FromUserAssignedClientId(managedIdentityClientId));
var exchangeContext =
    new TokenRequestContext(new[] { $"{exchangeAudience}/.default" });
// Exchange token for Azure SQL access token
var appCredential = new ClientAssertionCredential(
    tenantBId,
    appClientId,
    async _ => (await miCredential.GetTokenAsync(exchangeContext)).Token);
var sqlToken = await appCredential.GetTokenAsync(
    new TokenRequestContext(new[] { "https://database.windows.net/.default" }));
Console.WriteLine("Access token acquired");
// Connect to Azure SQL using the token
using var connection =
    new SqlConnection($"Server={sqlServer};Database={database};");
connection.AccessToken = sqlToken.Token;
await connection.OpenAsync();
var command = connection.CreateCommand();
command.CommandText = "SELECT SUSER_SNAME()";
var result = await command.ExecuteScalarAsync();
Console.WriteLine("CONNECTED SUCCESSFULLY");
Console.WriteLine($"SQL identity: {result}");

Step 5: Run the Test

dotnet run

Successful Output

Starting authentication test...
Access token acquired
CONNECTED SUCCESSFULLY
SQL identity: <APP_CLIENT_ID>@<TENANT_B_ID>

메타데이터
post_id
819e80c8e70e
slug
passwordless-cross-tenant-azure-sql-access-with-user-assigned-managed-identity-819e80c8e70e
url
https://medium.com/@dvpaytyan/passwordless-cross-tenant-azure-sql-access-with-user-assigned-managed-identity-819e80c8e70e
canonical_url
https://medium.com/@dvpaytyan/passwordless-cross-tenant-azure-sql-access-with-user-assigned-managed-identity-819e80c8e70e
author_url
https://medium.com/@dvpaytyan
status
ok
fetched_at
2026-08-19 12:13:33