Skip to content

How to Secure a Dataverse Plug-in with Managed Identity using Plugin Identity Manager for XrmToolBox

The ability to assign managed identities to Dataverse plug-ins was recently introduced in the Power Platform and was received with waves of likes and thumbs up from the Power Platform community.

Rightfully so, this powerful feature enables Dataverse plug-ins to securely connect with Azure resources without the hassle of managing credentials.

However, due to technical constraints and limited documentation, setting up plug-in managed identities is not that straightforward and can prove to be a daunting process, even for seasoned Power Platform developers.

One aspect that I found unintuitive is the creation of managed identity records in Dataverse and their association to plug-in assemblies. This sparked the idea for my latest community tool the Plugin Identity Manager for the XrmToolBox.

Plugin Identity Manager screenshot

You can use the tool to:
šŸ”§ Create, Update and Delete Managed identity records in Dataverse
šŸ”— Link Managed Identity to plugin assemblies
šŸ‘€ Inspect existing Plugin/Identity configuration

In the following, I will show how to setup a managed identity for a Dataverse plug-in from scratch and use the Plugin Identity Manager tool for the final configuration step.

Important! at the time of writing, managed identities for Dataverse plug-ins is still in preview. Some features and implementation details might change in the future.

Managed Identities for Dataverse Plugins

In a nutshell, Azure Managed Identity enables secure, password-free access to Azure resources, simplifying both security and management in the application lifecycle. Any Azure resource that supports Azure Entra authentication can be accessed by a managed identity, opening up a wide range of scenarios.

I highly recommend these resources that goes deeper on the matter

šŸ”—Power Platformā€™s protection ā€” Managed Identity for Dataverse plug-ins by MVP RaphaĆ«l Pothin
šŸ”—Developer introduction and guidelines - Managed identities for Azure resources | Microsoft Learn

As a proof of concept, I will demonstrate how to access a secret stored in an Azure Key Vault from within a Dataverse plug-in using a managed identity, removing the burden of credential management to the Azure resource.

I’ll create a Custom APIā€”mainly for easier testingā€”that takes the name of a Key Vault and the name of a secret in the Key Vault as input and return its value as output. Iā€™ll also output the token for testing purposes. Screenshot from my Custom API Manager tool for XrmToolBox

The code looks like this and basically recreate the following Rest Api call to the key vault.

GET {vaultBaseUrl}/secrets/{secret-name}/{secret-version}?api-version=7.4

using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Net.Http;

namespace Dataverse.ManagedIdentity.Plugin
{
    public class GetSecretValue : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {

            // Get Services.
            var pluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var identityService = (IManagedIdentityService)serviceProvider.GetService(typeof(IManagedIdentityService));


            var inputparameters = pluginExecutionContext.InputParameters;
            var outputparameters = pluginExecutionContext.OutputParameters;



            //INPUT of the Custom API
            var keyvaultname = (string)inputparameters["KeyVaultName"];
            var secretname = (string)inputparameters["SecretName"];

            // Get Token
            var scopes = new List<string> { "https://vault.azure.net/.default" };
            var token = identityService.AcquireToken(scopes);
            outputparameters["Token"] = token;
            outputparameters["Message"] = string.Empty;

            var keyvaultsecretUrl = $"https://{keyvaultname}.vault.azure.net/secrets/{secretname}?api-version=7.4";

            try
            {
                using (HttpClient client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                    var request = new HttpRequestMessage(HttpMethod.Get, new Uri(keyvaultsecretUrl));
                    var response = client.SendAsync(request).Result;

                    string json = response.Content.ReadAsStringAsync().Result;

                    var keyVaultResponse = System.Text.Json.JsonSerializer.Deserialize<KeyVaultResponse>(json);

                    outputparameters["SecretValue"] = keyVaultResponse.value;
                    outputparameters["Success"] = true;

                }
            }
            catch (Exception ex)
            {
                outputparameters["Success"] = false;
                outputparameters["Message"] = ex.Message;
            }

        }
    }
}

public class KeyVaultResponse
{
    public string value { get; set; }
    public string id { get; set; }

}


All the magic lies in the AcquireToken method call of the IManagedIdentityService. Without proper configuration of the managed identity on both the Azure and Dataverse sides, accessing key vault secrets will be out of reach to the plug-in code.

I will follow the official documentation and try to fill the gaps where needed.

šŸ”—Set up managed identity for Power Platform (preview) - Power Platform | Microsoft Learn

Step #1 : Sign the plugin with a certificate

One of the first step in the process is to sign the plugin assembly with a certificate (.pfx signing) . This is essential otherwise an error will be thrown while trying to associate a Dataverse managed identity record to the plugin assembly.

Despite years of development on the platform, I never signed plug-in assemblies with a certificate (.pfx) and had always relied on the simpler strong name signing (.snk). As a result, this part proved to be a bit tedious for me as I am not a security expert.

The official doc is a bit scarce on this aspect but I followed the recipe described in this great post by Clive Oldridge. It shows how to create a self-signed certificate (not recommended for production) that will be enough for our experimentations.

I’m copying the powershell script here but go see the entire blog post for more context
šŸ”—Set up managed identity for Power Platform Plugins – Clive Oldridge on Power Platform Blog

$ku_codeSigning = "1.3.6.1.5.5.7.3.3";

  $codeSignCert = New-SelfSignedCertificate `
    -Type "CodeSigningCert" `
    -KeyExportPolicy "Exportable" `
    -Subject "ManagedIdentityPlugin" `
    -KeyUsageProperty @("Sign") `
    -KeyUsage @("DigitalSignature") `
    -TextExtension @("2.5.29.37={text}$($ku_codeSigning)", "2.5.29.19={text}false") `
    -CertStoreLocation cert:\CurrentUser\My `
    -KeyLength 2048 `
    -NotAfter ([DateTime]::Now.AddDays(90)) `
    -Provider "Microsoft Software Key Storage Provider";

Once created, you can inspect the certificate with certmgr. Grab the value of the thumbprint, it will be needed later.

Now, with the certificate at hand the plug-in assembly can be signed using this command.

signtool sign /n {{CERTIFICATENAME}} /fd SHA256 {{ASSEMBLYNAME}}.dll

Although I much prefer setting up a post-build command in Visual Studio to automatically sign the assembly upon every successful build of the plug-in project.

"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe" sign /n {{CERTIFICATENAME}} /fd SHA256 $(SolutionDir)bin\$(Configuration)\{{ASSEMBLYNAME}}.dll

Once the plug-in assembly is signed, you can assess the presence of the signature by inspecting the file properties.

Inspect dll properties to validate signature

Thatā€™s one big step down! Now, letā€™s head over to the Azure setup.

Step #2 : Configure the managed identity in Azure

Two types of managed identities can be configured: a user-assigned managed identity or an application registered in Microsoft Entra ID.

For the current scenario, I will create a user-assigned managed identity, easily available to create from the Azure Portal.

Assign the identity to a resource group and give it a name.

Once created, keep the Client ID at hand it will be needed later on.

Next, grant the newly created identity access to the required Azure resources via the Azure role assignment tab. Here the Key Vault Secrets User RBAC role is given on the Key Vault we want to expose to the plug-in.

Now on to the trickiestā€”and, in my opinion, under-documentedā€”part: configuring federated credentials for managed identity.

Before starting the configuration have these info at hand

  • Dataverse Environment Id
  • Thumbprint of the certificate

Navigate to the Federated credentials tab and click on Add Credential

Select Other as Federated credential scenario

And enter the following configurations.

Issuer Url :

Take the Dataverse Environment ID (GUID), remove the dashes, and format it by placing a period between the first 30 characters and the last 2 characters to construct the Issuer URL.

https://{ENVID_FIRST30}.{ENVID_LAST2}.environment.api.powerplatform.com/sts

ex. https://3608895ef8844a53a065b306837e60.96.environment.api.powerplatform.com/sts

2- Subject :

Important! I went through some trial and error here, but this configuration finally worked for me. The thumbprint must be in uppercase, and the environment Id should be in the usual GUID format (lowercase with dashes)

component:pluginassembly,thumbprint:{THUMBPRINT_UPPERCASE},environment:{ENVID}

ex. component:pluginassembly,thumbprint:6C79AFBC726410ECB5A821F7C4663EC0299CD748,environment:3608895e-f884-4a53-a065-b306837e6096

Audience :

By default the audience is set to api://AzureADTokenExchange. I found out that the audience needed to be set in lowercase api://azureadtokenexchange otherwise errors where thrown

āŒ WRONG
āœ… Good

Here is the error I got when using the default value, after I changed the audience to lowercase, it went through correctly.

An unexpected error occurred: Microsoft.Identity.Client.MsalServiceException: A configuration issue is preventing authentication – check the error message from the server for details. You can modify the configuration in the application registration portal. See https://aka.ms/msal-net-invalid-client for details. Original exception: AADSTS7002122: No matching federated identity record found for presented assertion audience ‘api://azureadtokenexchange‘. The audience matches with case-insensitive comparison, but not with case-sensitive comparison. Please check your federated identity credential Subject, Audience and Issuer against the presented assertion. https://learn.microsoft.com/entra/workload-id/workload-identity-federation

And now, the final piece of the puzzleā€”where I finally get to show off my tool! šŸ˜‰

Step #3 : Configure the managed identity in Dataverse

Now that all is set in Azure, its time to register the managed identity in Dataverse and associate the record to the plugin assembly.

Since there is no available UI for Managed Identity records in Dataverse, the official documentation instruct the user to make 2 platform WebApi requests.

POST https://<<orgURL>>/api/data/v9.0/managedidentities

{
    "applicationid":"<<appId>>",
    "managedidentityid":"<<anyGuid>>",
    "credentialsource":2,
    "subjectscope":1,
    "tenantid":"<<tenantId>>"
 }

PATCH https:// <<orgURL>>/api/data/v9.0/pluginassemblies(<<PluginAssemblyId>>)

{
     "managedidentityid@odata.bind": "/managedidentities(<<ManagedIdentityGuid>>)"
 }

While, there’s nothing wrong with making Web API calls if you’re comfortable with it, I think it falls short in terms of intuitive user experience. This is where the Plugin Identity Manager for the XrmToolBox comes in and brings value to the table.

You can download the tool from the Tool Library of the XrmToolBox

Download the tool

Select plugin assembly

The first thing to do after firing up the tool is to choose your plug-in assembly. You have the choice to list all the plugins assembly installed in the environment of choose a given solution. You can also filter out managed or unmanaged assemblies.

Select Plug-in assembly to link

Create and Assign a new Managed Identity Record

Once the plug-in selected, you can create a new Managed Identity record to associate with the assembly by clicking Link to New Identity

In the creation screen you are invited to set these parameters :

  • Name : I have defaulted to ‘{Plugin Name} Identity‘ but you can enter what you want.

The official docs doesn’t highlight the Name field in the Web API call example, but itā€™s good practice to assign a name to the record. This makes it easier to manage and include in a solution later on.

  • ApplicationId : This is the ClientId of of the managed identity created in Azure.
  • TenantId : the GUID of the tenant where the Azure resource is located
  • Credential Source : I am defaulting and forcing the ‘IsManaged (2)‘ value as the other available values seems to be reserved by Microsoft for internal scenarios. Drop me a line in the Github repo if you want me to open this.
  • Subject Scope : I default to ‘Environment Scope (1)‘ but there are 2 other values. Global Scope or DevOnly Scope.

Environment Scope limits the managed identity to the same tenant as the Dataverse environment. Global scope, on the other hand, would enable access to Azure resources located on another tenant. Note that I haven’t tested Global scope yet and am unsure if it’s available in the preview release of the feature.

By clicking on Create and Link, the managed identity record will be created and associated with the selected plug-in assembly.

At this point all the configuration is done and we can proceed to the testing phase. But here are some additional features of the tool.

Assign to an existing Managed Identity Record

If the desired Managed Identity record already exists, you also have the choice to Link to existing Identity

This will display a list of the Managed Identity records where credential source is ‘IsManaged (2)‘ allowing you to link them to the selected plug-in.

Update/Delete a Managed Identity Record

The tool allows modification of a Managed Identity record…

… And deletion as well

Without further ado, let’s proceed to the testing phase.

Step #4 : Test the plug-in

Now comes the moment of truthā€”testing whether the plugin code can successfully access the Azure Key Vault secret.

In the Key Vault (kv-isv-dev) that Iā€™ve granted access to the managed identity, I have created a secret named MySecret.

Using the Custom API Tester Tool from Jonas Rapp, I can execute the plug-in code.

With the GetSecretValue Custom API (described earlier) selected, simply provide the name of the Azure Key Vault, the name of the secret and Execute the API.

If all is well configured, the value of the secret is correctly retrieved by the plug-in code. All without any credentials, thanks to the managed identity and the federated credential.

That is all for now šŸ˜‰

Take Away

Overall, managed identities for Dataverse plug-ins is a great new feature that deserves to be on Power Platform developers radar. But, you’ll have to admit that there is a certain complexity associated with the process. Itā€™s important to weigh the pros and cons before pursuing this approach.

I hope that some of you will find the Plugin Identity Manager tool useful to make the setup experience more enjoyable and coherent. Please submit any comments or ideas to improve the tool in the github repo

Thereā€™s much more to discuss on the subject, such as current limitations and ALM considerations, but thatā€™s beyond the scope of this blog post. I will certainly continue my experimentations and share my findings.

Until then,

Links

Image by freepik

Published inBlogProjects and Tools

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *