<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Marius Wodtke</title>
        <link>https://www.marius-wodtke.de/post/managed-identity/</link>
        <description>Recent content on Marius Wodtke</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Sun, 16 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.marius-wodtke.de/post/managed-identity/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Revisited: Managed Identities for Plugin Packages</title>
        <link>https://www.marius-wodtke.de/post/managed-identity/revisited/</link>
        <pubDate>Sun, 21 Jun 2026 00:00:00 +0000</pubDate>
        
        <guid>https://www.marius-wodtke.de/post/managed-identity/revisited/</guid>
        <description>&lt;img src="https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/cover.jpg" alt="Featured image of post Revisited: Managed Identities for Plugin Packages" /&gt;&lt;p&gt;We already had a first shot at Managed Identities for Plugins 2 years ago, but since then, the feature has left the preview stage and is now generally available. This also brought a new version of the subject identifier to be used for new managed identity records in Dynamics. And I already teased in the previous article that we will need a proper ALM to use this feature in a production environment. So today we kick off a gain with a &amp;ldquo;Getting Started&amp;rdquo; to then expand with automation for developers and CI/CD pipelines in the next articles.&lt;/p&gt;
&lt;p&gt;There is also &lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/power-platform/admin/set-up-managed-identity&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;an official Microsoft documentation&lt;/a&gt;, but I will diverge from that a bit because I&amp;rsquo;m already thinking about my next steps with multiple developers and pipelines.&lt;/p&gt;
&lt;h2 id=&#34;the-code&#34;&gt;The Code&lt;/h2&gt;
&lt;p&gt;Because this is a new project, we want to use plugin packages of course!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pac plugin init -o ManagedIdentitySample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the code I really only ran a sample that lets me debug this well, so the Plugin1.cs got some additions, the 3 highlighted lines are what is important for your own adventure. The rest of my code will make sure that the returned JWT will be persisted to logs and the description field of the Account I am triggering on to easily copy it out and for example analyze it further with a tool like &lt;a class=&#34;link&#34; href=&#34;https://jwt.ms&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;jwt.ms&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;protected&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;override&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; ExecuteDataversePlugin(ILocalPluginContext localPluginContext)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (localPluginContext == &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;throw&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ArgumentNullException(nameof(localPluginContext));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; context = localPluginContext.PluginExecutionContext;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; managedIdentity = localPluginContext.ServiceProvider.Get&amp;lt;IManagedIdentityService&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Acquiring Token&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; scope = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; List&amp;lt;&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;&amp;gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://vault.azure.net/.default&amp;#34;&lt;/span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; token = managedIdentity.AcquireToken(scope);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#if&lt;/span&gt; DEBUG
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.Trace(&lt;span style=&#34;color:#e6db74&#34;&gt;$&amp;#34;Token: {token}&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#endif&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; updateAccount = &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Entity(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;account&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Id = context.PrimaryEntityId,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;        [&amp;#34;description&amp;#34;]&lt;/span&gt; = token
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    localPluginContext.PluginUserService.Update(updateAccount);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We retrieve the &lt;code&gt;IManagedIdentityService&lt;/code&gt;, then build a scope and then request a token for this scope. If everything is correct, &lt;code&gt;AcquireToken&lt;/code&gt; will return a JWT, a JSON Web Token, that can be used for authentication/authorization. Of course this only works for services that accept this kind of auth, for example all Azure services. Azure Key Vault is chosen here on purpose because it can securely store other credentials like username and password for services that do not accept JWT. For these, the Managed Identity in combination with the Key Vault still provide a bootstrap for secure communication.&lt;/p&gt;
&lt;h2 id=&#34;generating-the-certificate&#34;&gt;Generating the Certificate&lt;/h2&gt;
&lt;p&gt;For Managed Identity to work, our plugin package needs to be signed. And it&amp;rsquo;s not enough to use the Signing Key we are used to from Plugin Assemblies deployed to Dataverse, no the NuGet package needs to be signed after packaging. For that matter we will need a Certificate with a private key.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve put a script below that will automate this mostly, please notice that you should&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;replace the variables at the top of the file with your own ones&lt;/li&gt;
&lt;li&gt;choose a good CommonName as we will need this later for retrieval, it should be unique on everyones computer, so simply your companies name is not enough. But it should also be the same for everyone and the build server, so maybe something like the Assembly name which may already have your Company in there like a &amp;ldquo;Microsoft.CRM.Plugins.ERPSynchronization&amp;rdquo; might work well&lt;/li&gt;
&lt;li&gt;receive a warning when the script tries to trust the generated certificate, this is optional, but if you don&amp;rsquo;t you will receive annoying warnings every time you save about the certificate not being trusted.&lt;/li&gt;
&lt;li&gt;save the &amp;ldquo;App Registration Values&amp;rdquo; that are printed in the end, we will need it later.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: The official docs use the Extension &amp;lsquo;2.5.29.37={text}1.3.6.1.5.5.7.3.4&amp;rsquo;, which in my testing was never deemed valid for signing a package by the NuGet command line tool, instead &amp;lsquo;2.5.29.37={text}1.3.6.1.5.5.7.3.3&amp;rsquo; (.3 in the end) is used as the valid extension for code signing.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;If you are unsure about the environmentId, you can find it in PPAC on your environment. tenantId is found in the Session Details under the setting cog icon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/EnvironmentId.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;
&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/TenantId.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$email = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MariusWodtke@MariusWodtkeMVP.onmicrosoft.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$commonName = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MariusWodtkeMVP&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantId = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;738016bf-2f59-46ca-9660-357d1de3b865&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$environmentId = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;7efcc31a-c410-e20e-9b95-dd448cedf653&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Generate Cert&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$params = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Type = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Custom&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Subject = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;E=&lt;/span&gt;$email&lt;span style=&#34;color:#e6db74&#34;&gt;,CN=&lt;/span&gt;$commonName&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TextExtension = @(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;2.5.29.37={text}1.3.6.1.5.5.7.3.3&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.5.29.17={text}email=&lt;/span&gt;$email&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    KeyAlgorithm = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;RSA&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    KeyLength = &lt;span style=&#34;color:#ae81ff&#34;&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    SmimeCapabilities = $true
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    CertStoreLocation = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Cert:\CurrentUser\My&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$cert = New-SelfSignedCertificate @params
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;New Certificate generated&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# (Optional) Add to trusted certs to suppress warnings while signing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# This will open a dialog if you are sure you want to trust it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Root&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CurrentUser&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store.Open(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ReadWrite&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store.Add($cert)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$store.Close()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Certificate installed to Trusted Root store&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Extract hash of cert for the Subject we need for the Federated Credential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$cerFilePath = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$PSScriptRoot&lt;span style=&#34;color:#e6db74&#34;&gt;\&lt;/span&gt;$commonName&lt;span style=&#34;color:#e6db74&#34;&gt;.cer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Export-Certificate -Cert $cert -FilePath $cerFilePath
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Certificate exported to: &lt;/span&gt;$cerFilePath&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$certUtilOutput = CertUtil -hashfile $cerFilePath SHA256
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$hash = ($certUtilOutput | Select-Object -Index &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).Trim()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;SHA256 Hash: &lt;/span&gt;$hash&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Encode the TenantId for the Subject we need for the Federated Credential&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantGuid = [&lt;span style=&#34;color:#66d9ef&#34;&gt;System.Guid&lt;/span&gt;]::Parse($tenantId)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantHex = $tenantGuid.ToByteArray()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$tenantBase64 = [&lt;span style=&#34;color:#66d9ef&#34;&gt;System.Convert&lt;/span&gt;]::ToBase64String($tenantHex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$encodedTenantId = $tenantBase64.Replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;+&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;).Replace(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;_&amp;#39;&lt;/span&gt;).TrimEnd(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Encoded Tenant ID: &lt;/span&gt;$encodedTenantId&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Print out the final subject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$subject = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/eid1/c/pub/t/&lt;/span&gt;$encodedTenantId&lt;span style=&#34;color:#e6db74&#34;&gt;/a/qzXoWDkuqUa3l6zM5mM0Rw/n/plugin/e/&lt;/span&gt;$environmentId&lt;span style=&#34;color:#e6db74&#34;&gt;/h/&lt;/span&gt;$hash&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$issuer = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://login.microsoftonline.com/&lt;/span&gt;$tenantId&lt;span style=&#34;color:#e6db74&#34;&gt;/v2.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;App Registration Values:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;`Issuer:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host $issuer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Subject Value:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host $subject
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;`n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Name:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Write-Host $email
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is a sample output&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PS C:\Repos\Kunter-Bunt\...\ManagedIdentitySample&amp;gt; .\GenerateCert.ps1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;New Certificate generated
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Certificate installed to Trusted Root store
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Directory&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; C:\Repos\Kunter-Bunt\...\ManagedIdentitySample
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Mode                 LastWriteTime         Length Name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;----                 -------------         ------ ----
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-a----         &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;2026&lt;/span&gt;     &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;54&lt;/span&gt;           &lt;span style=&#34;color:#ae81ff&#34;&gt;1110&lt;/span&gt; MariusWodtkeMVP.cer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Certificate exported to&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; C:\Repos\Kunter-Bunt\...\ManagedIdentitySample\MariusWodtkeMVP.cer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SHA256 Hash&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; b7d2145e1e8f76faa41561e47b70eb3bc7ca5b2f337ded7480c21a343c8ac6c3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Encoded Tenant ID&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; vxaAc1kvykaWYDV9HeO4ZQ
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;App Registration Values&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Issuer&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;//login.microsoftonline.com/738016bf-2f59-46ca-&lt;span style=&#34;color:#ae81ff&#34;&gt;9660&lt;/span&gt;-357d1de3b865/v2.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Subject Value&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/eid1/c/pub/t/vxaAc1kvykaWYDV9HeO4ZQ/a/qzXoWDkuqUa3l6zM5mM0Rw/n/plugin/e/7efcc31a-c410-e20e-9b95-dd448cedf653/h/b7d2145e1e8f76faa41561e47b70eb3bc7ca5b2f337ded7480c21a343c8ac6c3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MariusWodtke@MariusWodtkeMVP.onmicrosoft.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;signing-the-package&#34;&gt;Signing the Package&lt;/h2&gt;
&lt;p&gt;Now we start do deviate from the official documentation. They use an exported .pfx file with a password to sign their packages, but I want to automate signing as a post build step and don&amp;rsquo;t want to a password in the project file nor do I want to rely on unprotected .pfx files. Therefore we will search the Certificate by CN (CommonName) from the Certificate Store of Windows. Because it&amp;rsquo;s already loaded to the store, no password is required. And here you see the reason why I wanted to have a unique but common among developer and build machines name for CN as &lt;code&gt;-CertificateSubjectName &#39;WhateverYourCNIs&#39;&lt;/code&gt; will be hardcoded.&lt;/p&gt;
&lt;p&gt;For now I will use the nuget sign command manually, but the plan is to deliver an automation in another article.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PS C:\Repos\Kunter-Bunt\...\ManagedIdentitySample\bin\Debug&amp;gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex; background-color:#3c3d38&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt; nuget sign .\ManagedIdentitySample.1.0.0.nupkg -CertificateStoreLocation CurrentUser -CertificateStoreName My -CertificateSubjectName &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;MariusWodtkeMVP&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WARNING&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; NU3002&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; The &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-Timestamper&amp;#39;&lt;/span&gt; option was not provided. The signed package will not be timestamped. To learn more about this option, please visit https&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;//docs.nuget.org/docs/reference/command-line-reference
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Signing package(s) with certificate&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Subject Name&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; E=MariusWodtke@MariusWodtkeMVP.onmicrosoft.com, CN=MariusWodtkeMVP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  SHA1 hash&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; 313D5BD2A5E36DB2B7211DC1B9D99C9836825636
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  SHA256 hash&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; B7D2145E1E8F76FAA41561E47B70EB3BC7CA5B2F337DED7480C21A343C8AC6C3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Issued by&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; E=MariusWodtke@MariusWodtkeMVP.onmicrosoft.com, CN=MariusWodtkeMVP
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Valid from&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;2026&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;44&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt; to &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;13&lt;/span&gt;/&lt;span style=&#34;color:#ae81ff&#34;&gt;2027&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;04&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Package(s) signed successfully.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can register the package in Dataverse and for testing I also registered an asynchronous step on updating telephone1 of Account.&lt;/p&gt;
&lt;h2 id=&#34;creating-the-app-registration&#34;&gt;Creating the App Registration&lt;/h2&gt;
&lt;p&gt;Now we have a signed package in Dataverse and code inside that tries to authenticate with a Managed Identity, but what identity? We will create an AppRegistration here, no special setting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/AppReg.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Then we create a &lt;em&gt;Federated Credential&lt;/em&gt; under Certificates &amp;amp; Secrets.
Here you will need the issuer and subject that you saved after executing the script to generate the secret. Subject Value is the &lt;em&gt;Value&lt;/em&gt; needed here.&lt;/p&gt;
&lt;p&gt;For name I still have the certificates thumbprint in the screenshot, however, developer name (I used email in the script) is a better choice here. It&amp;rsquo;s easier to associate entries with people when needing to invalidate a leaving member or needing to update your certificate after it reached its validity date.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/Credential.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;creating-the-managed-identity&#34;&gt;Creating the Managed Identity&lt;/h2&gt;
&lt;p&gt;And finally we need to tell Dynamics what App Registration matches our signed Plugin Package. Fortunately David Rivard wrote an XrmToolbox tool for this matter. Really simple, select your Assembly, &lt;em&gt;Link to a New Identity&lt;/em&gt; and use the TenantId and ApplicationId (marked that one for you in the screenshot of the AppRegistration overview).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/XrmToolbox.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-result&#34;&gt;The Result&lt;/h2&gt;
&lt;p&gt;Now we have everything in place and the &amp;ldquo;If everything is correct&amp;rdquo; should be fulfilled now, so if I update my Accounts Main Phone, I can see the token in the Description field. Still, that is not a use case and you don&amp;rsquo;t want to hand out the tokens to anyone except the receiving application but it clearly shows the feature working.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://mariuswodtkeblog.blob.core.windows.net/images/post/managed-identity/revisited/Result.jpg&#34;
	width=&#34;800&#34;
	height=&#34;600&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
	
		data-blob-image=&#34;true&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Managed Identities provide the bootstrap for external authentication that we were waiting for a long time. These are the first steps towards Plugin Packages that will leverage this feature to enable communication without exposing credentials somewhere.&lt;/p&gt;
&lt;p&gt;For this we generate a Certificate with the provided script. This Certificate is used for signing our Plugin Package before registering in Dataverse. Then we create an App Registration &amp;amp; Federated Credential with the values returned from the script. Finally, we associate the Assembly with the App Registration using a Managed Identity record in Dataverse. And now we can use the &lt;code&gt;AcquireToken&lt;/code&gt; method of the &lt;code&gt;IManagedIdentityService&lt;/code&gt; to obtain a JWT for a given service.&lt;/p&gt;
&lt;p&gt;The solution presented here is functional, however, there are ALM parts still missing, check &lt;a class=&#34;link&#34; href=&#34;https://www.marius-wodtke.de/post/managed-identity/&#34; &gt;the series&lt;/a&gt; with upcoming articles on how to automate signing for development workflows and how a pipeline could work for deployment to get a production ready feature.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
