Skip to main content

PL400_Azure_Managed_Identities_for_Dataverse_and_Power_Platform

To implement Azure Managed Identities for Dataverse and Power Platform, we analyze the architectural patterns of system-assigned and user-assigned managed identities, focusing on Federated Identity Credentials (FIC) version 2. We map out the step-by-step configuration of Dataverse application users, plugin registration, and token exchange using IManagedIdentityService. Finally, we establish robust error handling, ALM pipelines, and troubleshooting matrices to resolve common authentication failures like AADSTS700213.


Azure Managed Identities for Dataverse and Power Platform

1. Conceptual Foundation

Modern enterprise cloud architectures demand the elimination of static credentials, such as client secrets and certificates, from application code and configuration files. Hardcoded secrets represent a significant attack surface, introducing risks of accidental exposure in source control, administrative overhead during rotation cycles, and service disruptions when credentials expire.

Microsoft Power Platform and Azure address this vulnerability through Power Platform Managed Identities and Federated Identity Credentials (FIC). This framework allows Dataverse plug-ins, custom workflow activities, and other platform components to securely authenticate to Azure resources (such as Azure Key Vault, Azure SQL, and Azure Storage) without storing, managing, or rotating credentials.

The Core Mechanics of Workload Identity Federation

Power Platform Managed Identity is built on Workload Identity Federation, which leverages Microsoft Entra ID (formerly Azure Active Directory) to establish a trust relationship between the Power Platform execution environment and your Azure resources. Instead of using a client secret or certificate to request an access token directly from Entra ID, the platform uses a cryptographic token exchange protocol.

+-------------------------------------------------------------------------------------------------+
| Dataverse Sandbox Environment |
| |
| +------------------+ 1. Request Token +-------------------------------------------+ |
| | Plug-in Assembly | -------------------------> | IManagedIdentityService (Runtime Sidecar) | |
| +------------------+ +-------------------------------------------+ |
+-------------------------------------------------------- | --------------------------------------+
| 2. Request Assertion (Signed by PP)
v
+--------------+
| Microsoft |
| Entra ID |
+--------------+
| 3. Validate Assertion via FIC
| 4. Issue Scoped Access Token
v
+--------------+
| Target App |
| Registration |
+--------------+

The authentication flow operates through the following phases:

  1. Execution Context Initiation: A Dataverse plug-in executes within the isolated sandbox worker process. The developer requests an access token using the platform-provided IManagedIdentityService interface.
  2. Assertion Generation: The Dataverse runtime requests a federated assertion token from the internal Power Platform identity provider. This assertion is cryptographically signed by a Microsoft-managed certificate specific to the Power Platform region and environment.
  3. Token Exchange (OIDC Federation): The runtime sends this assertion to the Microsoft Entra ID token endpoint (https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token). The request specifies the target User-Assigned Managed Identity (UAMI) or App Registration client ID.
  4. Federated Credential Validation: Entra ID intercepts the request and evaluates the configured Federated Identity Credential (FIC) on the target UAMI or App Registration. It verifies that:
    • The Issuer matches the Power Platform identity provider URL.
    • The Subject Identifier matches the exact runtime context of the calling plug-in (including the environment ID, assembly/package ID, and certificate hashes).
  5. Token Issuance: Once validated, Entra ID issues an OAuth 2.0 access token scoped to the requested Azure resource (e.g., https://vault.azure.net/.default). The plug-in uses this token to perform authorized operations against the target resource.

System-Assigned vs. User-Assigned Managed Identities

Understanding the distinction between System-Assigned Managed Identities (SAMI) and User-Assigned Managed Identities (UAMI) is critical for designing secure integrations:

  • System-Assigned Managed Identities (SAMI): Tied directly to the lifecycle of a single Azure resource (e.g., an Azure Function or an Azure Synapse Link profile). When the resource is deleted, Azure automatically cleans up the identity and its associated role assignments in Entra ID. In Power Platform, SAMIs are utilized for platform-level integrations, such as Azure Synapse Link for Dataverse or Microsoft Purview scanning.
  • User-Assigned Managed Identities (UAMI): Created as standalone Azure resources in an Azure subscription. A UAMI can be shared across multiple resources and environments, and its lifecycle is managed independently. For custom Dataverse plug-in development, UAMIs (or custom App Registrations) are the standard because they allow developers to pre-provision identities, configure FICs, and bind them to specific plug-in assemblies or packages.

Federated Identity Credentials (FIC) Versions

Microsoft supports two versions of Federated Identity Credentials for Power Platform components:

  1. Version 1 (CN-Based): This legacy version constructs the subject identifier using the certificate's Common Name (CN). It is highly sensitive to formatting and fails if the CN contains non-ASCII characters (resulting in AADSTS70050) or commas (resulting in AADSTS700213).
  2. Version 2 (Hash-Based - Recommended): Version 2 computes a fixed-length, ASCII-only subject identifier by taking the SHA-256 hash of the certificate's full Distinguished Name (DN) strings (both Issuer and Subject) and encoding them as URL-safe Base64. This version is robust, supports certificates with special characters, and is the standard for all new deployments.

2. Architecture & Decision Matrix

When designing integrations between Dataverse and external Azure resources, architects must select the appropriate execution context. The table below compares the four primary patterns:

Architectural DimensionPower Platform Managed Identity (Plug-ins)Azure Functions (with Managed Identity)Power Automate (Cloud Flows)Power Apps Component Framework (PCF)
ComplexityMedium (Requires assembly signing and FIC configuration)High (Requires hosting, deployment pipelines, and API design)Low (Drag-and-drop connectors)High (Client-side TypeScript, requires backend proxy for security)
ScalabilityHigh (Executes within Dataverse sandbox scale limits)Extremely High (Serverless auto-scaling)Medium (Subject to API request limits and throttling)Low (Bound by client browser resources)
Execution ContextSynchronous or Asynchronous (Dataverse Pipeline)Asynchronous (Webhooks) or Synchronous (via Custom API)Asynchronous (Event-driven)Client-side (User browser context)
Offline SupportNoNoNoYes (via local caching, but authentication requires connectivity)
LicensingStandard Power Apps/Dynamics 365 licensesAzure consumption costs + Dataverse API call limitsPower Automate Premium / Pay-as-you-goBound by host app licensing
PL-400 Exam RelevanceExtremely High (Core focus on secure, credential-free integration)High (Integration patterns)Medium (Cloud flow extensibility)High (Client-side development)

Architectural Recommendation

  • Use Power Platform Managed Identity with Dataverse Plug-ins when you require synchronous, low-latency execution within the Dataverse transaction boundary (e.g., retrieving an encryption key from Key Vault to decrypt a field value during the Retrieve or Create stage).
  • Use Azure Functions when the integration requires long-running processes (>2 minutes), heavy computational workloads, or third-party libraries incompatible with the Dataverse sandbox.
  • Use Power Automate for asynchronous, orchestrational integrations where low-code maintainability is prioritized over execution speed.
  • Use PCF only for client-side UI enhancements, ensuring that no direct secrets are accessed from the client side.

3. Step-by-Step Implementation Guide

This guide walks through configuring a User-Assigned Managed Identity (UAMI) to allow a signed Dataverse plug-in to securely retrieve secrets from Azure Key Vault using FIC Version 2.

Step 1: Create the User-Assigned Managed Identity in Azure

First, provision the UAMI in your Azure subscription.

  1. Sign in to the Azure Portal (https://portal.azure.com).
  2. Search for Managed Identities and select Create.
  3. Configure the following settings:
    • Subscription: Select your target subscription.
    • Resource Group: Select or create a resource group (e.g., rg-powerplatform-prod).
    • Region: Select the region matching your Dataverse environment.
    • Name: Enter a descriptive name (e.g., uami-dataverse-plugins-prod).
  4. Select Review + create, then Create.
  5. Once deployed, navigate to the resource and copy the Subscription ID, Resource Group Name, Client ID, and Tenant ID from the Overview blade.

Step 2: Generate a Signing Certificate and Sign the Plug-in Assembly

Dataverse requires plug-in assemblies to be cryptographically signed with a strong name key file (.snk) or a certificate (.pfx) to establish identity. For production, use a certificate issued by a trusted Certificate Authority (CA). For development, generate a self-signed certificate.

Run the following PowerShell script to generate a self-signed certificate and export it:

# Generate a self-signed certificate for plug-in signing
$cert = New-SelfSignedCertificate -Subject "CN=DataversePluginSigning" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-HashAlgorithm SHA256

# Export the certificate as a PFX (with private key) for signing
$inputPassword = ConvertTo-SecureString -String "StrongPassword123!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert `
-FilePath "C:\Certs\DataversePluginSigning.pfx" `
-Password $inputPassword

# Export the public key (.cer) for hashing
Export-Certificate -Cert $cert -FilePath "C:\Certs\DataversePluginSigning.cer"

To sign your compiled assembly using SignTool.exe (included in the Windows SDK):

signtool sign /f "C:\Certs\DataversePluginSigning.pfx" /p "StrongPassword123!" /t http://timestamp.digicert.com /fd SHA256 "C:\Source\MyPlugin\bin\Release\MyPluginAssembly.dll"

Step 3: Register the Plug-in Assembly in Dataverse

Using the Plug-in Registration Tool (PRT):

  1. Open the PRT and connect to your target Dataverse environment.
  2. Select Register -> Register New Assembly.
  3. Browse to your signed assembly (MyPluginAssembly.dll).
  4. Ensure Sandbox isolation mode is selected.
  5. Select Register Selected Plugins.
  6. Once registered, select the assembly in the tree view and copy its Assembly ID (GUID) from the properties pane.

Step 4: Compute the Version 2 Issuer and Subject Hashes

To configure the Federated Identity Credential, you must compute the SHA-256 Base64URL hashes of the certificate's Issuer and Subject Distinguished Names (DN).

Run this PowerShell script to extract the DNs and compute the exact hashes:

function Compute-Sha256Base64Url([string]$inputString) {
$sha256 = [System.Security.Cryptography.SHA256]::Create()
$bytes = [System.Text.Encoding]::UTF8.GetBytes($inputString)
$hashBytes = $sha256.ComputeHash($bytes)
$base64 = [Convert]::ToBase64String($hashBytes)
# Convert to Base64URL format
$base64Url = $base64.Replace('+', '-').Replace('/', '_').TrimEnd('=')
return $base64Url
}

# Load the certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import("C:\Certs\DataversePluginSigning.pfx", "StrongPassword123!", [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)

# Extract exact DN strings as formatted by .NET
$issuerDN = $cert.Issuer
$subjectDN = $cert.Subject

$issuerHash = Compute-Sha256Base64Url -inputString $issuerDN
$subjectHash = Compute-Sha256Base64Url -inputString $subjectDN

Write-Host "Issuer DN: $issuerDN"
Write-Host "Issuer Hash: $issuerHash"
Write-Host "Subject DN: $subjectDN"
Write-Host "Subject Hash: $subjectHash"

Step 5: Configure the Federated Identity Credential (FIC) on the UAMI

Now, establish the trust relationship in the Azure Portal:

  1. Navigate to your User-Assigned Managed Identity in the Azure Portal.
  2. Under Settings, select Certificates & secrets.
  3. Select the Federated credentials tab, then select Add credential.
  4. In the Federated credential scenario dropdown, select Other issuer.
  5. Configure the following parameters:
    • Issuer: https://login.microsoftonline.com/{your-tenant-guid}/v2.0 (Replace with your actual Tenant ID).
    • Audience: api://AzureADTokenExchange (This is case-sensitive and must be exact).
    • Subject identifier: Construct the string using the following format: /eid1/c/pub/t/{encodedTenantId}/a/qzXoWDkuqUa3l6zM5mM0Rw/n/plugin/e/{environmentId}/i/{issuerHash}/s/{subjectHash} Note: To get the {encodedTenantId}, parse your Tenant GUID to bytes and encode as Base64URL (see Section 5 for the script).
    • Name: Enter a unique name (e.g., fic-dataverse-plugin-prod).
    • Description: Federated credential for Dataverse production plug-in assembly.
  6. Select Add.

Step 6: Create the Managed Identity Record in Dataverse

You must register the managed identity within Dataverse by creating a record in the managedidentity table. This can be done via the Dataverse Web API or the Power Platform CLI (PAC).

Using the Web API (via an HTTP POST request):

POST https://orgname.crm.dynamics.com/api/data/v9.2/managedidentities
Content-Type: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json

{
"applicationid": "00000000-0000-0000-0000-000000000000",
"managedidentityid": "11111111-2222-3333-4444-555555555555",
"credentialsource": 2,
"subjectscope": 1,
"tenantid": "22222222-3333-4444-5555-666666666666",
"version": 2
}
  • applicationid: The Client ID of your UAMI.
  • managedidentityid: A new, unique GUID for this Dataverse record.
  • credentialsource: Set to 2 (IsManaged).
  • subjectscope: Set to 1 (EnvironmentScope).
  • tenantid: Your Microsoft Entra Tenant ID.
  • version: Set to 2 (to enforce Version 2 hash-based validation).

Next, bind the registered plug-in assembly to this managed identity record using a PATCH request:

PATCH https://orgname.crm.dynamics.com/api/data/v9.2/pluginassemblies(33333333-4444-5555-6666-777777777777)
Content-Type: application/json

{
"managedidentityid@odata.bind": "/managedidentities(11111111-2222-3333-4444-555555555555)"
}

Step 7: Grant the UAMI Access to Azure Key Vault

To allow the plug-in to read secrets, assign the appropriate Azure Role-Based Access Control (RBAC) role to the UAMI:

  1. Navigate to your Azure Key Vault in the Azure Portal.
  2. Select Access control (IAM) -> Add role assignment.
  3. Select the Key Vault Secrets User role and select Next.
  4. Select Managed identity under "Assign access to", then select + Select members.
  5. Select your subscription, filter by User-assigned managed identity, select uami-dataverse-plugins-prod, and select Select.
  6. Select Review + assign.

4. Complete Code Reference

The following production-grade C# plug-in demonstrates how to acquire an access token using IManagedIdentityService and retrieve a secret from Azure Key Vault. It includes robust error handling, tracing, and structured execution.

//-----------------------------------------------------------------------
// <copyright file="KeyVaultSecretRetrieverPlugin.cs" company="Enterprise Solutions">
// Copyright (c) Enterprise Solutions. All rights reserved.
// </copyright>
// <summary>
// Dataverse plug-in demonstrating credential-free authentication
// to Azure Key Vault using Power Platform Managed Identities.
// </summary>
//-----------------------------------------------------------------------

namespace Microsoft.PowerPlatform.Samples
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.Xrm.Sdk;

/// <summary>
/// A Dataverse plug-in that retrieves a secret from Azure Key Vault using Managed Identity.
/// Registered on the Pre-Operation stage of the Create/Update message of target entities.
/// </summary>
public sealed class KeyVaultSecretRetrieverPlugin : IPlugin
{
/// <summary>
/// The target Azure Key Vault secret URI. In production, retrieve this from an Environment Variable.
/// </summary>
private const string KeyVaultSecretUri = "https://kv-enterprise-prod.vault.azure.net/secrets/ExternalApiKey/?api-version=7.4";

/// <summary>
/// The standard OAuth 2.0 scope required for Azure Key Vault data plane operations.
/// </summary>
private static readonly string[] KeyVaultScopes = new string[] { "https://vault.azure.net/.default" };

/// <summary>
/// Executes the plug-in logic.
/// </summary>
/// <param name="serviceProvider">The service provider containing platform services.</param>
public void Execute(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}

// Retrieve the execution context and tracing service
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

tracingService.Trace("KeyVaultSecretRetrieverPlugin: Execution started. Depth: {0}", context.Depth);

// Retrieve the Managed Identity Service
IManagedIdentityService managedIdentityService = (IManagedIdentityService)serviceProvider.GetService(typeof(IManagedIdentityService));
if (managedIdentityService == null)
{
tracingService.Trace("Error: IManagedIdentityService is not available. Ensure Managed Identity is configured for this assembly.");
throw new InvalidPluginExecutionException("Managed Identity Service is unavailable. Please contact your system administrator.");
}

string accessToken;
try
{
tracingService.Trace("Attempting to acquire access token for Key Vault scope.");
accessToken = managedIdentityService.AcquireToken(KeyVaultScopes);

if (string.IsNullOrEmpty(accessToken))
{
throw new InvalidPluginExecutionException("Acquired token was null or empty.");
}

tracingService.Trace("Access token successfully acquired.");
}
catch (Exception ex)
{
tracingService.Trace("Exception occurred during token acquisition: {0}", ex.ToString());
throw new InvalidPluginExecutionException("Failed to authenticate to Azure resources using Managed Identity.", ex);
}

// Retrieve the secret from Azure Key Vault using the acquired token
try
{
string secretValue = RetrieveSecretFromKeyVault(KeyVaultSecretUri, accessToken, tracingService);
tracingService.Trace("Successfully retrieved secret from Key Vault. Length: {0}", secretValue.Length);

// Perform business logic with the retrieved secret (e.g., set a field value or call an external API)
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity targetEntity)
{
targetEntity["new_decryptedapikey"] = secretValue;
}
}
catch (Exception ex)
{
tracingService.Trace("Exception occurred during Key Vault secret retrieval: {0}", ex.ToString());
throw new InvalidPluginExecutionException("Failed to retrieve configuration secrets from Azure Key Vault.", ex);
}
}

/// <summary>
/// Performs an authenticated HTTP GET request to Azure Key Vault to retrieve the secret value.
/// </summary>
private static string RetrieveSecretFromKeyVault(string secretUri, string token, ITracingService tracingService)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

tracingService.Trace("Sending GET request to Key Vault URI: {0}", secretUri);

using (HttpResponseMessage response = httpClient.GetAsync(secretUri).GetAwaiter().GetResult())
{
string responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

if (!response.IsSuccessStatusCode)
{
tracingService.Trace("Key Vault returned error status code: {0}. Response: {1}", response.StatusCode, responseContent);
throw new HttpRequestException($"Key Vault request failed with status code {response.StatusCode}");
}

// Parse the JSON response to extract the secret value
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(responseContent)))
{
var serializer = new DataContractJsonSerializer(typeof(KeyVaultSecretResponse));
var secretResponse = (KeyVaultSecretResponse)serializer.ReadObject(memoryStream);
return secretResponse?.Value ?? throw new InvalidDataException("Secret value was missing from Key Vault response.");
}
}
}
}

/// <summary>
/// Data contract representing the Azure Key Vault secret response payload.
/// </summary>
[System.Runtime.Serialization.DataContract]
private class KeyVaultSecretResponse
{
[System.Runtime.Serialization.DataMember(Name = "value")]
public string Value { get; set; }

[System.Runtime.Serialization.DataMember(Name = "id")]
public string Id { get; set; }
}
}
}

5. Configuration & Environment Setup

Tenant ID Base64URL Encoding Script

To construct the Federated Identity Credential subject path, you must encode your Tenant GUID into a URL-safe Base64 string. Use this PowerShell script:

$tenantId = "22222222-3333-4444-5555-666666666666"
$tenantGuid = [System.Guid]::Parse($tenantId)
$tenantBytes = $tenantGuid.ToByteArray()
$base64 = [System.Convert]::ToBase64String($tenantBytes)
$encodedTenantId = $base64.Replace('+', '-').Replace('/', '_').TrimEnd('=')
Write-Host "Encoded Tenant ID: $encodedTenantId"

Bicep Template for Infrastructure Provisioning

The following Bicep template provisions the User-Assigned Managed Identity, the Azure Key Vault, configures the Federated Identity Credential (FIC) for a Dataverse environment, and assigns the Key Vault Secrets User role.

@description('The region where resources will be deployed.')
param location string = resourceGroup().location

@description('The Client ID of the User-Assigned Managed Identity.')
param uamiName string = 'uami-dataverse-plugins-prod'

@description('The name of the Azure Key Vault.')
param keyVaultName string = 'kv-enterprise-prod'

@description('The Dataverse Environment ID (GUID).')
param dataverseEnvironmentId string

@description('The Microsoft Entra Tenant ID.')
param tenantId string

@description('The computed SHA-256 Base64URL hash of the certificate Issuer DN.')
param certificateIssuerHash string

@description('The computed SHA-256 Base64URL hash of the certificate Subject DN.')
param certificateSubjectHash string

// 1. Provision the User-Assigned Managed Identity
resource uami 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: uamiName
location: location
}

// 2. Provision the Azure Key Vault with Azure RBAC enabled
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: keyVaultName
location: location
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: tenantId
enableRbacAuthorization: true
enabledForDeployment: false
enabledForDiskEncryption: false
enabledForTemplateDeployment: false
}
}

// 3. Configure the Federated Identity Credential (FIC) Version 2
// We must compute the Base64URL encoded Tenant ID inside Bicep or pass it.
// For simplicity, we construct the subject identifier using the pre-calculated hashes.
resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = {
parent: uami
name: 'fic-dataverse-plugin-prod'
properties: {
issuer: 'https://login.microsoftonline.com/`${tenantId}`/v2.0'
audiences: [
'api://AzureADTokenExchange'
]
// Note: Ensure the encoded tenant ID is passed correctly in real scenarios.
// This subject path uses the standard public cloud prefix.
subject: '/eid1/c/pub/t/I_g-IjNDREVEV_V_V_V_V_g/n/plugin/e/`${dataverseEnvironmentId}`/i/`${certificateIssuerHash}`/s/`${certificateSubjectHash}`'
}
}

// 4. Assign Key Vault Secrets User Role to the UAMI
var keyVaultSecretsUserRoleId = '4633e12f-d780-4111-af0d-530c49090af4' // Standard Built-in Role ID

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(keyVault.id, uami.id, keyVaultSecretsUserRoleId)
scope: keyVault
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', keyVaultSecretsUserRoleId)
principalId: uami.properties.principalId
principalType: 'ServicePrincipal'
}
}

output uamiClientId string = uami.properties.clientId
output keyVaultUri string = keyVault.properties.vaultUri

6. Security & Permission Matrix

To ensure least-privilege access control, configure permissions according to the following matrix:

PrincipalPermission / RoleScopeReason
User-Assigned Managed Identity (UAMI)Key Vault Secrets UserAzure Key Vault (Data Plane)Allows the plug-in to read secret values without administrative rights.
Dataverse Application UserCustom Security Role (e.g., "Integration Service")Dataverse EnvironmentRequired if the external Azure resource needs to call back into Dataverse.
Deployment Service Principal (ALM)Managed Identity ContributorAzure Subscription / Resource GroupRequired to create and update UAMIs and FICs during CI/CD pipelines.
Dataverse System AdministratorSystem AdministratorDataverse EnvironmentRequired to register plug-in assemblies and bind them to managed identities.
Power Platform Admin / Global AdminPower Platform AdministratorTenant / EnvironmentRequired to link enterprise policies and manage environment-level identities.

7. Pipeline Execution Internals

When a plug-in executes within the Dataverse event execution pipeline, the runtime manages the lifecycle of the IManagedIdentityService and token exchange.

[Client Request]
|
v
Stage 10: Pre-Validation (Sync)
|
v
Stage 20: Pre-Operation (Sync) <-- [Managed Identity Token Acquisition & Key Vault Call]
|
v
Stage 30: Main Operation (Dataverse Database Transaction Boundary)
|
v
Stage 40: Post-Operation (Sync)
|
v
[Database Commit]
|
v
Stage 40: Post-Operation (Async) <-- [Managed Identity Token Acquisition & External API Call]

Transaction Boundaries and Performance

  • Synchronous Stages (Pre-Operation / Post-Operation): If the plug-in executes within a synchronous stage, the call to IManagedIdentityService.AcquireToken and any subsequent external HTTP calls (e.g., to Azure Key Vault) occur inside the Dataverse database transaction.
    • Critical Impact: Any latency or timeout in the external call directly extends the SQL transaction lock duration. If the external call takes 5 seconds, the Dataverse database transaction remains open for an additional 5 seconds, increasing the risk of SQL blocking and timeouts for concurrent users.
    • Mitigation: Keep synchronous external calls extremely lightweight. Set aggressive timeouts (e.g., 2-5 seconds) on HTTP clients.
  • Asynchronous Stage (Post-Operation): For non-blocking integrations (e.g., sending data to an external API), register the plug-in step as Asynchronous. This executes the plug-in outside the main database transaction, eliminating transaction lock overhead.

Sandbox Isolation and Callout Restrictions

Dataverse plug-ins execute within a highly restricted Sandbox Isolation Mode.

  • Network Restrictions: Plug-ins can only communicate via standard HTTP/HTTPS protocols (ports 80 and 443). IP addresses must be publicly resolvable; calls to internal virtual networks or private endpoints are blocked unless Azure ExpressRoute or Power Platform VNet Integration is configured.
  • Execution Timeout: The sandbox worker process enforces a strict 2-minute (120-second) execution timeout. If the plug-in (including token acquisition and external API calls) exceeds this limit, the platform terminates the process, rolls back the transaction, and throws a System.TimeoutException.

8. Error Handling & Retry Patterns

Network calls and token exchanges are subject to transient failures. Plug-ins must implement structured exception handling and retry patterns to ensure resilience.

Common Failure Modes and Error Codes

Error Code / ExceptionRoot CauseRemediation
AADSTS700213No matching federated identity record found. The subject identifier computed at runtime does not match the FIC configured on the UAMI.Verify the subject path format. Ensure the certificate hashes, environment ID, and encoded Tenant ID are correct.
AADSTS70050The Federated Managed Identity path is not properly formatted. Typically caused by non-ASCII characters in the certificate CN under Version 1.Upgrade the managed identity record and FIC to Version 2 (hash-based).
System.TimeoutExceptionThe external call (Key Vault or token endpoint) exceeded the 120-second sandbox execution limit.Implement aggressive timeouts on the HTTP client and offload long-running processes to asynchronous steps.
HttpRequestException (403 Forbidden)The UAMI authenticated successfully, but lacks permissions on the target Azure resource.Verify Azure RBAC role assignments (e.g., ensure the UAMI has the "Key Vault Secrets User" role).

Resilient Retry Pattern Implementation

The following code snippet demonstrates how to implement an exponential back-off retry pattern for transient network errors when calling external resources:

using System;
using System.Net.Http;
using System.Threading;
using Microsoft.Xrm.Sdk;

public static class ResilientHttpCaller
{
private const int MaxRetries = 3;
private const int InitialDelayMilliseconds = 500;

public static string CallWithRetry(Func<string> httpCall, ITracingService tracingService)
{
int retryCount = 0;
int delay = InitialDelayMilliseconds;

while (true)
{
try
{
return httpCall();
}
catch (Exception ex) when (IsTransient(ex))
{
retryCount++;
if (retryCount > MaxRetries)
{
tracingService.Trace("Max retries ({0}) reached. Failing operation.", MaxRetries);
throw;
}

tracingService.Trace("Transient error encountered: {0}. Retrying in {1}ms (Attempt {2} of {3}).",
ex.Message, delay, retryCount, MaxRetries);

Thread.Sleep(delay);
delay *= 2; // Exponential back-off
}
}
}

private static bool IsTransient(Exception ex)
{
if (ex is HttpRequestException)
{
// Retry on network-level failures
return true;
}
if (ex is TimeoutException)
{
return true;
}
return false;
}
}

9. Performance Optimisation & Limits

To maintain platform stability and performance, developers must design integrations within the following limits:

Platform Limits

  • Sandbox Execution Limit: 120 seconds per plug-in execution.
  • Token Cache Duration: Access tokens acquired via IManagedIdentityService are cached internally by the Power Platform runtime. The cache respects the token's Time-To-Live (TTL), typically 24 hours. Do not implement custom token caching within the plug-in code; calling AcquireToken repeatedly is highly optimized and retrieves the cached token automatically.
  • API Throughput Caps: Dataverse API requests are subject to service protection limits. External calls from plug-ins do not count against Dataverse API limits, but they are subject to the throttling limits of the target Azure resource (e.g., Azure Key Vault enforces a limit of 2,000 transactions per 10 seconds for standard vaults).

Optimization Strategies

  1. Avoid Synchronous Key Vault Calls on High-Frequency Operations: If a plug-in runs on the Retrieve or RetrieveMultiple messages of a high-volume entity, do not call Key Vault synchronously. Instead, use Environment Variables with a secret store reference, which are natively cached by the Dataverse infrastructure.
  2. Minimize Payload Sizes: When sending data to external APIs, serialize only the required attributes. Avoid serializing the entire Entity object, which contains metadata overhead.
  3. Use Keep-Alive Connections: Reusing HTTP connections reduces TCP handshake latency. In Dataverse plug-ins, instantiate the HttpClient using a static or thread-safe pattern, or utilize HttpMessageHandler to manage connection lifecycles efficiently within the sandbox limits.

10. ALM & Deployment Checklist

Deploying components utilizing Managed Identities across environments (Dev -> Test -> Prod) requires careful coordination of Azure resources and Dataverse solutions.

[Development Environment]
- Register Plug-in Assembly
- Create Dev UAMI & FIC (Dev Cert)
- Bind Assembly to Dev UAMI
- Export Solution (Managed)
|
v
[Azure DevOps / GitHub Pipeline]
- Extract Solution
- Inject Target Environment Variables
- Deploy Managed Solution
|
v
[Production Environment]
- Import Managed Solution
- Create Prod UAMI & FIC (Prod Cert)
- Bind Assembly to Prod UAMI (via Deployment Script)

Step-by-Step ALM Checklist

  1. Source Control: Commit the plug-in source code and the strong-name key file or certificate public key (.cer) to your Git repository.
  2. Solution Packaging: Include the plug-in assembly and plug-in steps in your Dataverse solution. Export the solution as Managed from the development environment.
  3. Azure Resource Provisioning: Use Bicep or ARM templates within your CI/CD pipeline to provision the target environment's UAMI, Key Vault, and role assignments.
  4. FIC Configuration: Ensure the pipeline computes the correct certificate hashes for the target environment's signing certificate and configures the FIC on the target UAMI.
  5. Environment Variables: Use Dataverse Environment Variables to store the Key Vault secret URIs. Do not hardcode URIs in the C# code.
  6. Post-Deployment Binding: Since the managedidentity record GUID and the pluginassembly ID may differ across environments, execute a post-deployment PowerShell script (using PAC CLI) to create the managedidentity record in the target environment and bind it to the imported assembly.

Azure DevOps Pipeline YAML Snippet

The following YAML snippet demonstrates how to automate the deployment of a Dataverse solution and configure the managed identity binding using the Power Platform Build Tools:

trigger:
- main

pool:
vmImage: 'windows-latest'

variables:
SolutionName: 'EnterpriseIntegrationSolution'
TargetEnvUrl: 'https://org-prod.crm.dynamics.com'

steps:
- task: PowerPlatformToolInstaller@2
displayName: 'Install Power Platform Build Tools'

- task: PowerPlatformImportSolution@2
displayName: 'Import Managed Solution to Production'
inputs:
authenticationType: 'PowerPlatformSPN'
PowerPlatformSPN: 'ProductionServiceConnection'
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/$(SolutionName)_managed.zip'
OverwriteUnmanagedCustomizations: true
PublishCustomizations: true

- task: PowerShell@2
displayName: 'Configure Managed Identity Binding'
inputs:
targetType: 'inline'
script: |
# Install PAC CLI
$env:Path += ";C:\Program Files\Microsoft Power Platform CLI\bin"

# Authenticate to target environment
pac auth create --url $(TargetEnvUrl) --tenant $(TenantId) --applicationId $(ClientId) --clientSecret $(ClientSecret)

# Retrieve the imported Plugin Assembly ID
# (In production, query the Web API or use a pre-defined mapping)
$assemblyId = "33333333-4444-5555-6666-777777777777"
$uamiClientId = "00000000-0000-0000-0000-000000000000"
$tenantId = "22222222-3333-4444-5555-666666666666"

# Create and link the managed identity record
pac managed-identity create --application-id $uamiClientId --tenant-id $tenantId --component-id $assemblyId --component-type PluginAssembly

11. Common Pitfalls & Troubleshooting Guide

1. Error AADSTS700213: No matching federated identity record found

  • Symptom: The plug-in fails during execution with an InvalidPluginExecutionException containing the Entra ID error code AADSTS700213.
  • Root Cause: The subject identifier computed by the Dataverse runtime does not match any Federated Identity Credential configured on the target UAMI.
  • Diagnostic Steps:
    1. Check the plug-in trace log to inspect the runtime context.
    2. Run pac managed-identity show-fic to view the computed subject identifier.
    3. Compare the computed path character-by-character with the path configured in the Azure Portal.
  • Fix: Update the FIC in the Azure Portal to match the exact computed subject identifier. Ensure the Tenant ID, Environment ID, and certificate hashes are correct.

2. Error AADSTS70050: Path is not properly formatted

  • Symptom: Token acquisition fails with error AADSTS70050.
  • Root Cause: The certificate used to sign the assembly contains non-ASCII characters in its Distinguished Name (DN), and the managed identity record is configured to use Version 1 (CN-based) validation.
  • Diagnostic Steps: Inspect the certificate's Subject property. Look for accented characters or special symbols.
  • Fix: Upgrade the managed identity record to Version 2 by setting the version attribute to 2 in the managedidentity table, and update the FIC subject path to use the hash-based format.

3. Missing Assembly Signing

  • Symptom: The Plug-in Registration Tool allows registration, but token acquisition fails at runtime with a "Managed Identity is not configured" error.
  • Root Cause: The assembly was registered without being cryptographically signed, or the signature was stripped during a build step.
  • Diagnostic Steps: Run signtool verify /v MyAssembly.dll to verify the signature.
  • Fix: Ensure the assembly is signed with a valid .pfx certificate during the release build pipeline.

4. Mismatched Certificate Hashes

  • Symptom: The plug-in works in the development environment but fails immediately upon deployment to production.
  • Root Cause: The production assembly was signed with a different certificate than the development assembly, but the production FIC was configured using the development certificate's hashes.
  • Diagnostic Steps: Run the PowerShell hashing script (Section 4) against the production .pfx file and compare the output with the production FIC configuration.
  • Fix: Update the production FIC with the hashes computed from the production signing certificate.

5. Key Vault Firewall Blocking Access

  • Symptom: Token acquisition succeeds, but the HTTP call to Key Vault times out or returns a 403 Forbidden error.
  • Root Cause: The Key Vault firewall is enabled, and it is blocking requests from the Power Platform sandbox IP addresses.
  • Diagnostic Steps: Check the Key Vault firewall settings. Temporarily allow access from "All networks" to test.
  • Fix: Configure the Key Vault firewall to allow trusted Microsoft services, or configure Power Platform VNet Integration to route sandbox traffic through a secured subnet.

6. Sandbox Execution Timeout (120 seconds)

  • Symptom: The plug-in execution is terminated with a TimeoutException.
  • Root Cause: The external API call or Key Vault request is taking too long, exceeding the sandbox limit.
  • Diagnostic Steps: Review the plug-in trace logs to measure the duration of the token acquisition and HTTP calls.
  • Fix: Implement aggressive timeouts on the HttpClient (e.g., 5 seconds) and register the plug-in step as Asynchronous if synchronous execution is not strictly required.

7. Incorrect Audience in FIC

  • Symptom: Token acquisition fails with AADSTS700213.
  • Root Cause: The audience in the FIC is set to the Key Vault URI instead of the token exchange endpoint.
  • Diagnostic Steps: Inspect the FIC properties in the Azure Portal.
  • Fix: Ensure the audience is set to exactly api://AzureADTokenExchange (case-sensitive).

8. Expired Signing Certificate

  • Symptom: Token acquisition begins failing after months of successful operation.
  • Root Cause: The certificate used to sign the plug-in assembly has expired.
  • Diagnostic Steps: Check the validity period of the certificate in the local store or by inspecting the assembly properties.
  • Fix: Generate a new certificate, sign the assembly, update the FIC with the new hashes, and redeploy.

9. Missing Role Assignment on Target Resource

  • Symptom: Token acquisition succeeds, but the plug-in receives a 403 Unauthorized when calling the Azure resource.
  • Root Cause: The UAMI has not been granted the necessary RBAC permissions on the target resource.
  • Diagnostic Steps: Check the Access Control (IAM) blade of the target resource.
  • Fix: Assign the appropriate role (e.g., "Key Vault Secrets User" or "Storage Blob Data Contributor") to the UAMI.

10. Cross-Tenant Token Acquisition Failure

  • Symptom: Token acquisition fails when attempting to access resources in a different Entra ID tenant.
  • Root Cause: Workload Identity Federation does not support cross-tenant token exchange by default.
  • Diagnostic Steps: Verify that the UAMI and the Dataverse environment reside within the same tenant.
  • Fix: Ensure all resources, UAMIs, and Dataverse environments are provisioned within the same Microsoft Entra tenant boundary.

12. Exam Focus: Key Facts & Edge Cases

To prepare for the PL-400 (Microsoft Power Platform Developer) exam, memorize the following key facts, limits, and behaviors:

  • IManagedIdentityService: This is the exact interface used in C# plug-in code to acquire access tokens. It is retrieved from the serviceProvider using typeof(IManagedIdentityService).
  • AcquireToken Signature: The method signature is string AcquireToken(IEnumerable<string> scopes). It accepts a collection of scopes (e.g., https://vault.azure.net/.default) and returns the token string.
  • FIC Audience: The audience configured on the Federated Identity Credential must be exactly api://AzureADTokenExchange (case-sensitive). Any other value will cause authentication to fail.
  • FIC Issuer: The issuer URL must be https://login.microsoftonline.com/{tenantId}/v2.0.
  • Version 2 Subject Path: Understand the structure of the Version 2 subject path: /eid1/c/pub/t/{encodedTenantId}/a/qzXoWDkuqUa3l6zM5mM0Rw/n/plugin/e/{environmentId}/i/{issuerHash}/s/{subjectHash}
  • Strong Name Signing: Plug-in assemblies must be signed with a certificate or strong name key file to utilize Managed Identities. Unsigned assemblies cannot perform token exchange.
  • Sandbox Isolation: Managed Identity token exchange is fully supported within the Dataverse sandbox isolation mode. It does not require bypassing sandbox restrictions.
  • Caching: The Power Platform runtime automatically caches tokens acquired via IManagedIdentityService. Developers should not implement custom caching logic within the plug-in code.
  • Supported Components: Power Platform Managed Identity is generally available (GA) for Dataverse plug-ins and dependent assembly plug-ins. It is not supported for client-side JavaScript or legacy workflow activities.
  • National Clouds: When deploying to specialized cloud environments (e.g., GCC High, China), the audience and subject prefix must be explicitly modified (e.g., api://AzureADTokenExchangeUSGov for GCC High).