<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anton Staykov</title>
    <description>The latest articles on DEV Community by Anton Staykov (@astaykov).</description>
    <link>https://dev.to/astaykov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3827606%2Fbb3aa4b8-90b5-4db5-8ad5-f9bd05ab762b.jpg</url>
      <title>DEV Community: Anton Staykov</title>
      <link>https://dev.to/astaykov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/astaykov"/>
    <language>en</language>
    <item>
      <title>Every change to an Entra extension is a Control Plane event: the monitoring contract</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:47:00 +0000</pubDate>
      <link>https://dev.to/astaykov/every-change-to-an-entra-extension-is-a-control-plane-event-the-monitoring-contract-5621</link>
      <guid>https://dev.to/astaykov/every-change-to-an-entra-extension-is-a-control-plane-event-the-monitoring-contract-5621</guid>
      <description>&lt;p&gt;Parts 1 and 2 of this series (&lt;a href="https://dev.to/astaykov/microsoft-entra-extensibility-is-a-gift-it-is-also-control-plane-4egn"&gt;Microsoft Entra extensibility is a gift. It is also Control Plane.&lt;/a&gt; and &lt;a href="https://dev.to/astaykov/securing-the-code-that-decides-who-entra-trusts-a-function-app-and-a-logic-app-end-to-end-1bi0"&gt;Securing the code that decides who Entra trusts&lt;/a&gt;) made two static decisions. &lt;em&gt;Where&lt;/em&gt; the code lives: a dedicated Control Plane subscription, directly under the root management group or under a dedicated Control Plane management group, never in the platform identity subscription or an application landing zone. &lt;em&gt;What credential&lt;/em&gt; it uses to call out: a managed identity by default, &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;federated identity credentials&lt;/a&gt; when the call must leave Azure, certificates as a tolerated middle step, and static symmetric keys never.&lt;/p&gt;

&lt;p&gt;Both decisions are one-time. You make them, you walk away, you do not touch them for months.&lt;/p&gt;

&lt;p&gt;The third decision is not like that. It is continuous, and it is the one most teams quietly skip: how do you know the deployed code on that Function App is still the code your reviewers approved? How do you know the Logic App workflow definition has not been rewritten since last Tuesday? How do you know nobody added a federated identity credential to the managed identity at 3 a.m. on a Saturday?&lt;/p&gt;

&lt;p&gt;The answer is monitoring. Not "we have Log Analytics turned on." Monitoring with a specific operating contract attached.&lt;/p&gt;

&lt;h2&gt;
  
  
  The posture inversion
&lt;/h2&gt;

&lt;p&gt;For most Azure workloads, the default operating posture is reasonable trust. Engineers deploy. Pipelines run. Configuration drifts a little. The team reviews changes weekly. Anomalies are caught eventually.&lt;/p&gt;

&lt;p&gt;For a Microsoft Entra extension, that posture is wrong. The default has to be inverted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Once an Entra extension lands in production, every change to it is suspicious by default.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not "needs review." Not "let's check first." Suspicious. The default state of an alert firing on a Function App that hosts a &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;custom claims provider&lt;/a&gt; is "the SOC is investigating, prove this was approved." If you cannot prove the change was approved within the team's response SLA, the change is treated as an incident and rolled back. That posture is harsh on purpose. The same RBAC inheritance chain that lets a Contributor four management groups above your Function App quietly replace its code (the central argument of Part 1) is what forces the inverted default. You cannot rely on the deployment surface to be small. You have to rely on the alerting surface to be loud.&lt;/p&gt;

&lt;p&gt;That inversion is the monitoring contract this article is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three change surfaces
&lt;/h2&gt;

&lt;p&gt;A production Entra extension has three change surfaces, and each one needs its own forced-diagnostics control, its own alert, and its own row on the operating contract.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surface 1: the runtime itself
&lt;/h3&gt;

&lt;p&gt;This is the surface most teams think of first, and the one most teams under-instrument. The deployed code on the Function App. The workflow definition on the Logic App. The App Settings. The environment variables. The runtime version. The configured custom domain. Each of these is a knob that, if turned, changes the behavior of the extension the next time Entra calls it.&lt;/p&gt;

&lt;p&gt;For the custom claims provider Function App, the relevant Azure Resource Manager operations to alert on are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/extensions/write&lt;/code&gt; (a new deployment was pushed).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/config/appsettings/write&lt;/code&gt; (App Settings were changed, including any reference to a Key Vault secret).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/config/web/write&lt;/code&gt; (runtime version, startup command, or general configuration was changed).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Web/sites/publishxml/action&lt;/code&gt; (publish profile credentials were generated, which is one of the easiest ways to deploy code outside the normal pipeline).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the dynamic approval Logic App, the analogous operations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Logic/workflows/write&lt;/code&gt; (the workflow definition itself was modified).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Logic/workflows/connections/write&lt;/code&gt; and &lt;code&gt;Microsoft.Web/connections/write&lt;/code&gt; (an API connection was created or modified, including the authentication target it points at).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these should be backed by an &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/activity-log-alerts" rel="noopener noreferrer"&gt;Activity Log alert&lt;/a&gt; scoped to the Control Plane subscription, with an action group that routes to the SOC's primary intake. Not to the team's chat channel. Not to an email list. The SOC's intake.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surface 2: identity and RBAC
&lt;/h3&gt;

&lt;p&gt;This is the surface that does not look like the extension at all and is therefore the easiest to ignore. The extension is unchanged. The blast radius around it is what changed.&lt;/p&gt;

&lt;p&gt;Specific things to alert on, across the Control Plane subscription:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.Authorization/roleAssignments/write&lt;/code&gt; anywhere on the inheritance path, from the resource up to the subscription. A Contributor (or User Access Administrator, or Owner) just appeared on a tier in the chain. Walk the inheritance (&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC overview&lt;/a&gt;) and ask: does this new principal now have effective Contributor on the Function App? If yes, the SOC needs to know.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials/write&lt;/code&gt; on any user-assigned managed identity in scope. A new &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;federated identity credential&lt;/a&gt; is, by construction, a new way to act as that identity from outside Azure. Treat it as a credential issuance event, because that is what it is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Microsoft.KeyVault/vaults/accessPolicies/write&lt;/code&gt; and &lt;code&gt;Microsoft.Authorization/roleAssignments/write&lt;/code&gt; scoped to the Key Vault. Same logic as the RBAC inheritance, sharper consequence: Owner on the Key Vault is operationally Owner of every certificate in it.&lt;/li&gt;
&lt;li&gt;App registration credential events in the Entra audit log. The Function App and the Logic App that back the extension are both fronted by app registrations or managed identities in Entra. A new client secret, certificate, or federated credential added to that app registration is an alert-worthy event, full stop. The &lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-audit-logs" rel="noopener noreferrer"&gt;Entra audit logs&lt;/a&gt; capture these as &lt;code&gt;Update application – Certificates and secrets management&lt;/code&gt; events; pipe them into the same SOC intake.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Surface 3: the Entra-side binding
&lt;/h3&gt;

&lt;p&gt;This surface lives entirely in Entra, not in Azure. The custom claims provider is &lt;em&gt;bound&lt;/em&gt; to the application through a custom authentication extension configuration. The dynamic approval Logic App is &lt;em&gt;bound&lt;/em&gt; to an access package through an entitlement management custom extension. Those bindings are what make Entra actually call your code. Change the binding, and Entra now calls somebody else's code instead.&lt;/p&gt;

&lt;p&gt;Two specific events worth their own alerts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes to custom authentication extensions in the Entra audit log: the endpoint URL was repointed, the API authentication configuration was changed, the extension was disabled.&lt;/li&gt;
&lt;li&gt;Changes to entitlement management custom extensions: a new callback URL, a new authentication method, a change to the access package policy that consumes the extension.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same Entra audit log feed that the IGA team already monitors covers most of this. The extra step is making sure the SOC sees it alongside the Azure Activity Log events, because the correlation between "the Entra binding moved" and "a deployment happened on the new target one hour earlier" is exactly the story you want analysts to be able to tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forced diagnostics: making the signal undeniable
&lt;/h2&gt;

&lt;p&gt;Alerts only fire if the signal reaches the alert engine. Three controls turn diagnostics from "we enabled it on most resources" into a property of the subscription itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Policy with &lt;code&gt;deployIfNotExists&lt;/code&gt; for diagnostic settings.&lt;/strong&gt; Apply &lt;a href="https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effect-deploy-if-not-exists" rel="noopener noreferrer"&gt;built-in or custom policies&lt;/a&gt; at the Control Plane management group that force every Function App, every Logic App, every Key Vault, every Storage Account, and every Managed Identity created underneath it to ship diagnostic logs to a dedicated tenant-level &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/diagnostic-settings" rel="noopener noreferrer"&gt;Log Analytics workspace&lt;/a&gt;. The policy is &lt;code&gt;deployIfNotExists&lt;/code&gt;, not &lt;code&gt;audit&lt;/code&gt;, so a missing diagnostic setting is repaired by the platform, not just reported on. There are existing built-ins covering Logic Apps and many resource types; custom definitions cover the rest. The principle is: it is not optional, and it is not possible to deploy a resource into this management group without diagnostic settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Policy &lt;code&gt;deny&lt;/code&gt; for tampering with diagnostic settings.&lt;/strong&gt; A &lt;code&gt;deployIfNotExists&lt;/code&gt; policy creates the setting. A complementary policy denies its modification or removal by anyone except a tightly-scoped platform identity. If an engineer cannot turn the diagnostic setting off, an attacker who compromises that engineer cannot either.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Locks on the resource groups that host the extensions.&lt;/strong&gt; &lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources" rel="noopener noreferrer"&gt;Resource Locks&lt;/a&gt; at &lt;code&gt;CanNotDelete&lt;/code&gt; for everything, &lt;code&gt;ReadOnly&lt;/code&gt; for the resource groups that hold the runtime resources, applied at the resource-group level. ReadOnly does not prevent code deployments through the Function App's own publish endpoint, so it is not a substitute for the alert on &lt;code&gt;Microsoft.Web/sites/extensions/write&lt;/code&gt;, but it does block the entire class of "an attacker recreates the resource with the same name and a different definition" attack.&lt;/p&gt;

&lt;p&gt;Layered: policy forces diagnostics on, policy prevents diagnostics from being turned off, resource locks prevent the resources from being deleted out from under the diagnostics, alerts on the resulting log stream surface every change. Each control compensates for the failure modes of the others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correlation: the story the SIEM has to be able to tell
&lt;/h2&gt;

&lt;p&gt;Each individual alert is signal. The value lives in the correlation across feeds. The SOC should be able to ask, and answer, questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A token issued by Entra at 14:07 carried a claim asserting "department=Finance." The custom claims provider for that application is hosted on Function App &lt;code&gt;fa-claims-prod&lt;/code&gt;. Did the deployed code on &lt;code&gt;fa-claims-prod&lt;/code&gt; change in the last 24 hours? Did its App Settings? Did any role assignment land on it, its resource group, its subscription, or any management group above it?&lt;/li&gt;
&lt;li&gt;An access package request was auto-approved at 09:21 for a privileged group. The dynamic approval Logic App that decided is &lt;code&gt;la-dynapproval-prod&lt;/code&gt;. Did the workflow definition change in the last 7 days? Did any of its API connections get repointed? Did the Logic App's managed identity get a new federated credential?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both questions are answerable with &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/platform-logs-overview" rel="noopener noreferrer"&gt;Azure Activity Log&lt;/a&gt;, Entra audit log, Function App and Logic App resource logs, and the extension's own application logs, all sitting in one Log Analytics workspace, with &lt;a href="https://learn.microsoft.com/en-us/azure/sentinel/data-connectors/microsoft-entra-id" rel="noopener noreferrer"&gt;Microsoft Sentinel&lt;/a&gt; on top stitching them together. The Sentinel queries are the easy part. The hard part is making sure the three feeds are guaranteed to be there, which is exactly what the forced diagnostics controls deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  The operating contract: three states, not four
&lt;/h2&gt;

&lt;p&gt;Every alert from any of the three change surfaces lands in one of exactly three buckets. There is no fourth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approved change.&lt;/strong&gt; The deployment was pre-declared in the change management system. The change record carries the artifact hash, the pipeline run ID, the requesting engineer, and a declared start and end window. The alert correlates to the change record automatically (on pipeline run ID, on deployment principal, on time window) and is closed within minutes. The audit trail shows the alert, the correlation, and the closure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Informational platform change.&lt;/strong&gt; Microsoft platform-driven activity that you do not control: automatic certificate rotations on platform-managed components, Microsoft-side runtime version refreshes, Azure-managed identity key rotations, and similar. These still ship to the same workspace and still fire as Activity Log entries, but they route to a low-severity "platform" queue, not to the SOC's primary intake. Two rules govern this bucket: the events must be attributable to a known Microsoft service principal or platform operation (the actor identity on the Activity Log entry is non-human and matches an allow-listed Microsoft pattern), and the bucket must be reviewed at least weekly so it does not become a quiet hiding place. Anything that does not cleanly match a platform pattern falls through to the third bucket by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incident.&lt;/strong&gt; Anything that is neither pre-declared by a change record nor cleanly attributable to a Microsoft platform operation. SOC intake, paged on-call, automatic rollback if the runtime change can be reverted, manual investigation either way. The default reaction is to revert first and investigate after. The cost of a wrongly-reverted legitimate change is a Slack apology. The cost of a wrongly-allowed malicious change is a forged claim in a token your applications already trusted.&lt;/p&gt;

&lt;p&gt;That is the entire operating model. Approved, informational-platform, or incident. No "we will look at it later." No "probably the platform team." If it does not fit one of the first two buckets within a tight automatic correlation window, it is bucket three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three-legged stool
&lt;/h2&gt;

&lt;p&gt;Part 1 placed the resources correctly. Part 2 made sure they authenticate without leaking secrets. Part 3 makes sure that, once they are running, every change to them is either justified or visibly investigated. None of the three legs stands alone. A correctly placed resource without monitoring is one quiet deployment away from being someone else's resource. A perfectly monitored resource using a static API key is monitoring around an already-compromised credential. A managed identity in the wrong subscription is a credential model wasted on an over-permissive blast radius.&lt;/p&gt;

&lt;p&gt;Built together, they are how Entra extensibility stays the thing that makes Entra a great product, rather than the thing that quietly hollowed it out. Use the extensibility. Put the code where it belongs. Authenticate it with the right credentials. And treat every change to it, from the first day it is in production, as a Control Plane event worth proving.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;Custom authentication extensions overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/diagnostic-settings" rel="noopener noreferrer"&gt;Diagnostic settings in Azure Monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/platform-logs-overview" rel="noopener noreferrer"&gt;Azure Monitor platform logs overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effect-deploy-if-not-exists" rel="noopener noreferrer"&gt;Azure Policy &lt;code&gt;deployIfNotExists&lt;/code&gt; effect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/activity-log-alerts" rel="noopener noreferrer"&gt;Activity log alerts in Azure Monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-types" rel="noopener noreferrer"&gt;Azure Monitor alert types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources" rel="noopener noreferrer"&gt;Lock your resources to protect your infrastructure (Resource Locks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-audit-logs" rel="noopener noreferrer"&gt;Microsoft Entra audit logs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/sentinel/data-connectors/microsoft-entra-id" rel="noopener noreferrer"&gt;Microsoft Entra ID data connector for Microsoft Sentinel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Federated Identity Credentials overview (Microsoft Graph)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoftentra</category>
      <category>azuremonitor</category>
      <category>zerotrust</category>
      <category>secops</category>
    </item>
    <item>
      <title>Securing the code that decides who Entra trusts: a Function App and a Logic App, end to end</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 02 Jul 2026 10:37:00 +0000</pubDate>
      <link>https://dev.to/astaykov/securing-the-code-that-decides-who-entra-trusts-a-function-app-and-a-logic-app-end-to-end-1bi0</link>
      <guid>https://dev.to/astaykov/securing-the-code-that-decides-who-entra-trusts-a-function-app-and-a-logic-app-end-to-end-1bi0</guid>
      <description>&lt;p&gt;Part 1 of this series, [[Entra-Extensibility-Is-Control-Plane]], made the case that every Microsoft Entra extension point hands a piece of the Entra trust boundary to a system Entra does not own. The Azure resources that host that code sit on the Control Plane (in the modern Microsoft &lt;a href="https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model" rel="noopener noreferrer"&gt;Enterprise Access Model&lt;/a&gt;, what the legacy tiered model called Tier 0). The Azure RBAC inheritance chain makes them dangerously easy to compromise from far up the management-group tree, and they deserve a placement and a guardrail set normally reserved for domain controllers and Entra Connect.&lt;/p&gt;

&lt;p&gt;This article gets concrete. Two real extension scenarios, end to end, and then the question that decides the security of both: what credential does the extension use when it calls out?&lt;/p&gt;

&lt;h2&gt;
  
  
  Example A: a custom claims provider backed by an Azure Function
&lt;/h2&gt;

&lt;p&gt;The first scenario is the one most likely to land in a production tenant in the next six months. A team needs to inject custom claims into the access tokens issued to their applications. The supported path is a &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;custom claims provider&lt;/a&gt;, implemented as a REST endpoint that Entra calls during the token issuance event. The reference implementation is an Azure Function.&lt;/p&gt;

&lt;p&gt;The flow, in one paragraph. A user signs in to an application registered in Entra. Entra's token issuance pipeline reaches the configured custom claims provider extension. Entra calls the Function App's HTTPS endpoint with a server-to-server access token in the &lt;code&gt;Authorization&lt;/code&gt; header, exactly as documented in &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;the step that covers protecting your Azure Function&lt;/a&gt;. The Function validates the incoming token (issuer, audience, signature), inspects the request body, calls whatever upstream sources it needs to resolve the claims it cares about, and returns a JSON response describing which claims to add to the outgoing token. Entra builds the access token, including the returned claims, and hands it to the relying application. The application makes authorization decisions based on those claims.&lt;/p&gt;

&lt;p&gt;Read that one more time. &lt;em&gt;The application makes authorization decisions based on those claims.&lt;/em&gt; The Function App controls what your applications see. A compromised Function App is, in practical terms, a claim forger sitting inside your token issuance pipeline. Whatever an attacker can put in a returned claim, the downstream application will trust as if Entra had asserted it.&lt;/p&gt;

&lt;p&gt;The blast radius of that Function App is therefore the union of everything that can change what code runs in it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The source repository. Branch protection, code-owner approvals, the list of users with push rights to &lt;code&gt;main&lt;/code&gt;, the list of users with admin rights on the repository who can bypass protection, and every CI configuration file that ships with the repo.&lt;/li&gt;
&lt;li&gt;The CI/CD pipeline definition. Who can edit it, who can re-run it with arbitrary inputs, who can change which artifact gets deployed at the end of a run.&lt;/li&gt;
&lt;li&gt;The identity the pipeline uses to deploy to Azure. This is the credential that lets the pipeline rewrite the Function App's code at any time. If that credential is a client secret stored as a CI variable, the Function App's code is one leaked variable away from being replaced.&lt;/li&gt;
&lt;li&gt;The Function App resource itself, and every security principal that has Website Contributor, Contributor, or Owner on it directly. These principals can replace the deployed ZIP without ever touching the repository.&lt;/li&gt;
&lt;li&gt;The resource group, the subscription, every management group above the subscription, and the root. As &lt;a href="https://dev.to/astaykov/microsoft-entra-extensibility-is-a-gift-it-is-also-control-plane-4egn"&gt;Part 1&lt;/a&gt; walked, Contributor at any level inherits down. A Contributor (any role) on the root management group does not need to be a member of your identity team to replace your Function App's code.&lt;/li&gt;
&lt;li&gt;The Key Vault holding any secrets, certificates, or connection strings the Function uses to talk to upstream systems. Owner on that Key Vault is, for practical purposes, Owner on the Function's ability to authenticate outbound.&lt;/li&gt;
&lt;li&gt;The App Service Plan, because deployment slots, scaling rules, and runtime versions sit at that scope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A real custom claims provider in production has to harden every item in that list. Missing any one of them is enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example B: a Logic App backing dynamic approval for an access package
&lt;/h2&gt;

&lt;p&gt;The second scenario is governance-shaped. An access package in &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Microsoft Entra entitlement management&lt;/a&gt; is configured to use &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;dynamic approval&lt;/a&gt;, so that the approver of a request is selected at runtime by an Azure Logic App rather than statically declared in the policy.&lt;/p&gt;

&lt;p&gt;The flow. A user requests assignment to the access package. Entitlement management invokes the configured Logic App, passing context about the requestor and the package. The Logic App runs whatever workflow you have authored. Typically that workflow needs to call Microsoft Graph (to resolve the requestor's manager, project assignment, or group membership) and at least one external system (a leave or vacation calendar, a shift roster, an IT change management board, a project allocation tool). The Logic App returns the identity of the human or group who should approve, or in some variants it short-circuits and returns an approval or denial directly. Entitlement management then routes the request accordingly.&lt;/p&gt;

&lt;p&gt;The Logic App is now part of your access governance. If an attacker can rewrite the workflow, the attacker can name themselves as the approver for any access package that uses this dynamic approval policy. Or worse: rewrite the workflow to return "auto-approve" for a specific requestor and never invoke a human at all.&lt;/p&gt;

&lt;p&gt;Most of the Function App blast-radius list applies here too, with one extra wrinkle. A Logic App often has fewer source-code artifacts (the workflow can be authored directly in the portal, although &lt;a href="https://learn.microsoft.com/en-us/azure/logic-apps/create-standard-workflows-visual-studio-code" rel="noopener noreferrer"&gt;authoring it through a code project under source control&lt;/a&gt; is strongly recommended for exactly the reasons we are discussing). What a Logic App always has, in abundance, is &lt;em&gt;connections&lt;/em&gt;. The Microsoft Graph connection, the SharePoint connection, the HTTP connector pointing at the change-management API, the Salesforce connection for project data. Each connection is a credential the Logic App will use, and each credential is something an attacker would happily inherit if they took ownership of the Logic App resource. The connection objects themselves are Azure resources in the same resource group, subject to the same RBAC inheritance chain.&lt;/p&gt;

&lt;p&gt;The surface is broader than the Function App's. The principle is the same. The Logic App is Control Plane because of who calls it and what its answer is used for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The credential ladder
&lt;/h2&gt;

&lt;p&gt;Both examples force the same question. Once your code is running, what does it use to authenticate when it calls downstream? Microsoft Graph, the corporate HR system, a SharePoint list, an external SaaS API. Pick one. There are three honest answers, and they are not equivalent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static keys: assume already compromised
&lt;/h3&gt;

&lt;p&gt;A static API key, or the symmetric &lt;code&gt;client_id&lt;/code&gt; plus &lt;code&gt;client_secret&lt;/code&gt; pair, is the least secure credential you can use, and you should treat it as already compromised. That is not hyperbole and it is not a "best practice" framing. It is the structural reality.&lt;/p&gt;

&lt;p&gt;You did not pick the entropy. You do not know who pasted it into a chat window during an incident three years ago. You do not know whether the developer who first received it stored a copy in their personal note-taking app. You do not know whether the CI variable holding it was visible to the build agent's logs at some point during a regression. You do not know whether the rotation calendar reminder fired, and you do not know whether the previous value was scrubbed from every place it was ever stored.&lt;/p&gt;

&lt;p&gt;For non-critical workloads, this is a manageable risk. For code that decides what claims your applications see, or who approves access to your most sensitive packages, it is not. If your Control Plane Entra extensions are still authenticating outbound with static symmetric credentials, the rest of your Zero Trust program is theater for that path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificates: better, but only as strong as their issuance and storage
&lt;/h3&gt;

&lt;p&gt;Certificate-based authentication is genuinely a step up. The credential is asymmetric, the private key never has to leave the host that uses it, and the rotation story is cleaner. The honest assessment, though, is that a certificate is only as secure as the process that produces, transports and stores it.&lt;/p&gt;

&lt;p&gt;Be specific about the questions a certificate forces you to answer. How was the certificate issued: by a managed PKI, by your tenant's own CA, by a developer with &lt;code&gt;openssl&lt;/code&gt; at a laptop? How did the public key end up in the Entra app registration credentials: a portal upload by a human, a pipeline call to Microsoft Graph, a manual step in a runbook? Where is the private key stored: on the host filesystem, in an HSM, in a Key Vault? If it is in Key Vault, is it &lt;a href="https://learn.microsoft.com/en-us/azure/key-vault/certificates/about-certificates" rel="noopener noreferrer"&gt;non-exportable&lt;/a&gt;, or did someone tick the wrong box when they imported it? Was the certificate generated &lt;em&gt;inside&lt;/em&gt; the Key Vault, or was it generated elsewhere and imported, in which case the original private key existed somewhere outside the HSM at least once?&lt;/p&gt;

&lt;p&gt;And then the question Part 1 of this series spent an entire section on: who has Owner on that Key Vault? Walk the &lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC inheritance chain&lt;/a&gt; from the Key Vault up through its resource group, its subscription, its management groups, and the root. Every Owner along that chain is, for practical purposes, an Owner of every certificate in that Key Vault. The certificate is not as static as a symmetric key, but the principals who can extract or replace it follow the same inheritance rules every other Azure resource follows.&lt;/p&gt;

&lt;p&gt;A certificate in a Key Vault inside a wide-RBAC subscription is, operationally, not very different from a static key with extra steps. The credential improved. The principal model around it did not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managed Identities: the right baseline
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview" rel="noopener noreferrer"&gt;managed identity&lt;/a&gt; is the right baseline for any Entra extension hosted on Azure compute. The platform manages the credential. The compute resource (Function App, Logic App, App Service, container) requests an access token from a local metadata endpoint. Entra issues it. No human, no pipeline, no developer ever sees a secret.&lt;/p&gt;

&lt;p&gt;When the call has to leave Azure, &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt; extends the same pattern across trust boundaries. A managed identity can act as the credential for an Entra app registration through a federated identity credential, and an Entra-issued token can be exchanged for an external system's token as long as that system supports the &lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;OpenID Connect / RFC 7523&lt;/a&gt; pattern. The longer treatment of how this composes for AI agent platforms is in &lt;a href="https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602"&gt;Your agentic platform doesn't need a single secret&lt;/a&gt;, and most of the same building blocks apply when the workload happens to be an Entra extension instead of an agent runtime.&lt;/p&gt;

&lt;p&gt;Even the right answer comes with its own discipline. A managed identity is not a free pass. Two specific guardrails matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System-assigned by default.&lt;/strong&gt; A system-assigned managed identity is lifecycle-bound to a single Azure resource. Delete the resource, the identity is gone. Replace the resource, the new instance gets a brand-new identity that must be re-granted permissions. That friction is the whole point. It makes the credential and the resource indistinguishable, which is exactly what you want for a Control Plane workload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-assigned managed identities are subscription-scoped reusable identities.&lt;/strong&gt; A user-assigned MI lives in a resource group and can be attached to any number of resources in the same tenant. That convenience is also a risk. Any Contributor on a resource in the same subscription can attach the same managed identity to a &lt;em&gt;new&lt;/em&gt; resource of their choosing, and that new resource can now act as the privileged caller into Microsoft Graph. Treat user-assigned MIs that hold real downstream permissions as Control Plane objects of their own, place them in the same dedicated Control Plane subscription as the workloads that use them, and never share one across planes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a managed identity is placed correctly, on a Control Plane resource, in a Control Plane subscription, under a Control Plane management group, the answer to "who can use this credential against Microsoft Graph" collapses to "exactly the workloads I deployed there, and exactly the principals who have rights to deploy to that subscription." That is the credential model these extensions deserve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the two halves together
&lt;/h2&gt;

&lt;p&gt;Part 1 of this series argued that Entra extensions belong on the Control Plane because of what they decide. This article argued that the security of those decisions is the security of the smallest detail underneath them: the source repository, the pipeline identity, the runtime principal model, the credential the runtime uses to call out, the Key Vault that backs it, the RBAC inheritance above all of them.&lt;/p&gt;

&lt;p&gt;There is a uniform answer to the placement question. The Function App, the Logic App, the Key Vault, the user-assigned managed identities, the connections, the API connectors all belong in a dedicated Control Plane subscription under a dedicated Control Plane management group, directly under the root. Not in the platform identity subscription, not in a shared services subscription, not in an application landing zone. The list of principals with Contributor or Owner anywhere on the chain above those resources has to be short, audited, and reviewed on a cadence shorter than your access-review cycle.&lt;/p&gt;

&lt;p&gt;There is a uniform answer to the credential question too. The default credential for a Control Plane Entra extension is a managed identity, scoped tightly, lifecycle-bound where possible, system-assigned where possible, with &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;federated identity credentials&lt;/a&gt; covering the cases where the call has to leave Azure. Certificates are a tolerated middle step when the call truly has nowhere else to go. Static symmetric keys are not an option for code that influences Entra decisions, full stop.&lt;/p&gt;

&lt;p&gt;Use the extensibility. It is what makes Entra a great product, and the next year will keep adding new hooks worth using. Just host the code behind those hooks the same way you host the things Entra is protecting. The point of a Zero Trust enforcement engine is undermined the moment its decisions are shaped by code anyone can quietly replace.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;Custom authentication extensions overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Entitlement management Logic Apps integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;Externally determine the approval requirements for an access package using custom extensions (dynamic approval)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview" rel="noopener noreferrer"&gt;Managed identities for Azure resources overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/key-vault/certificates/about-certificates" rel="noopener noreferrer"&gt;About Azure Key Vault certificates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/logic-apps/create-standard-workflows-visual-studio-code" rel="noopener noreferrer"&gt;Create a Standard logic app workflow with Visual Studio Code (source-controlled authoring)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523: JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model" rel="noopener noreferrer"&gt;Enterprise Access Model (Control Plane / Management Plane)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoftentra</category>
      <category>managedidentity</category>
      <category>identitygovernance</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>Microsoft Entra extensibility is a gift. It is also Control Plane.</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 30 Jun 2026 09:17:00 +0000</pubDate>
      <link>https://dev.to/astaykov/microsoft-entra-extensibility-is-a-gift-it-is-also-control-plane-4egn</link>
      <guid>https://dev.to/astaykov/microsoft-entra-extensibility-is-a-gift-it-is-also-control-plane-4egn</guid>
      <description>&lt;p&gt;Microsoft Entra is the Zero Trust policy enforcement engine sitting at the intersection of identities, endpoints, AI agents, networks and data. Every conversation about Conditional Access, every Privileged Identity Management workflow, every access package review, every token issued to an application or an agent: it all funnels through Entra. That is the entire point of the product, and that is why so much of the security industry's energy is spent hardening it.&lt;/p&gt;

&lt;p&gt;The most interesting thing happening to Entra right now is not a new policy type. It is &lt;em&gt;extensibility&lt;/em&gt;. In the last few years Entra has quietly become one of the most extensible identity platforms in the enterprise market. &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;Custom authentication extensions&lt;/a&gt; let your code shape token issuance and authentication events. &lt;a href="https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-external-method-manage" rel="noopener noreferrer"&gt;External authentication methods&lt;/a&gt; plug third-party MFA providers into the sign-in pipeline. &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/privileged-identity-management-custom-extensions" rel="noopener noreferrer"&gt;PIM custom extensions&lt;/a&gt; let your business logic approve or deny privileged role activation. &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/lifecycle-workflow-extensibility" rel="noopener noreferrer"&gt;Lifecycle workflow custom task extensions&lt;/a&gt; let your code influence whether an employee account is enabled, disabled, or transformed. Entitlement management talks to &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Logic Apps for governance workflows&lt;/a&gt;, and a specific variant, &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;dynamic approval&lt;/a&gt;, lets your code decide &lt;em&gt;who&lt;/em&gt; approves an access package request, or whether the request should be approved at all. More extension points are coming. The trend line is obvious.&lt;/p&gt;

&lt;p&gt;This is genuinely good. Identity decisions are business decisions, and business logic belongs in code your business writes. Extensibility is what turns Entra from a fixed product into a programmable enforcement engine, and that is the right direction.&lt;/p&gt;

&lt;p&gt;It is also the part where the security conversation almost always stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reframe nobody wants to write
&lt;/h2&gt;

&lt;p&gt;Every extension point hands a piece of the Entra trust boundary to a system Entra does not own. The decision is still announced from Entra. The &lt;em&gt;signal&lt;/em&gt; that drives the decision is shaped somewhere else. That somewhere else is now Control Plane, in the language of Microsoft's current &lt;a href="https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model" rel="noopener noreferrer"&gt;Enterprise Access Model&lt;/a&gt; (what the legacy tiered administration model used to call Tier 0). Control Plane is the set of systems that grant access to everything else: the directory itself, the privileged groups inside it, and now, by extension, any code that shapes a directory decision at runtime.&lt;/p&gt;

&lt;p&gt;Sit with the implications for a minute, scenario by scenario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/privileged-identity-management-custom-extensions" rel="noopener noreferrer"&gt;PIM custom extension&lt;/a&gt; can influence whether an identity gets a high-privilege role activated, in milliseconds, with no human in the loop. The endpoint that responds to PIM is now part of your privileged-access path.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/lifecycle-workflow-extensibility" rel="noopener noreferrer"&gt;lifecycle workflow custom task extension&lt;/a&gt; can be the anchor that decides whether a leaver's account is actually disabled tomorrow morning. The code that answers that workflow call is now part of your joiner-mover-leaver process.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;Logic App driving dynamic approval&lt;/a&gt; can decide who is allowed to approve a request for a sensitive access package, or whether the request gets pre-approved with no human reviewer at all. The Logic App is now part of your access governance.&lt;/li&gt;
&lt;li&gt;An Azure Function backing a &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;custom claims provider&lt;/a&gt; can inject custom claims into the access tokens your applications consume. The Function is now part of your token issuance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those endpoints is a Control Plane system. Not "important." Not "in scope for the next pentest." Control Plane. The same category you reserve for your domain controllers, your Entra Connect server, your root CA, and the accounts that can write to any of them.&lt;/p&gt;

&lt;p&gt;Most security writing about Entra extensibility focuses on the Entra side: how to register the extension, what scopes it needs, how the audit log captures the call. That work is necessary and well covered. The Azure side, where the actual code runs, is barely covered at all. That is the gap I want to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cloud Adoption Framework already saw half of this
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/overview" rel="noopener noreferrer"&gt;Azure Cloud Adoption Framework&lt;/a&gt; and the &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/" rel="noopener noreferrer"&gt;Azure landing zones&lt;/a&gt; reference architecture do something deliberate: they split &lt;em&gt;platform&lt;/em&gt; landing zones from &lt;em&gt;application&lt;/em&gt; landing zones, and inside platform they carve out a dedicated &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/identity-access" rel="noopener noreferrer"&gt;identity subscription&lt;/a&gt; for identity-related infrastructure. The &lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/resource-org-subscriptions" rel="noopener noreferrer"&gt;subscription organization guidance&lt;/a&gt; treats identity as its own thing, not as a workload that shares a subscription with marketing analytics.&lt;/p&gt;

&lt;p&gt;That is exactly the right instinct. The problem is that the identity subscription is almost always described as the home of legacy identity infrastructure: Active Directory Domain Services domain controllers, Entra Connect, a Privileged Access Workstation jump box, maybe a few related VMs. It is described as the place where the &lt;em&gt;identity infrastructure that runs in Azure&lt;/em&gt; lives.&lt;/p&gt;

&lt;p&gt;It is not yet described as the place where the &lt;em&gt;Azure compute that influences Entra decisions at runtime&lt;/em&gt; lives. That is a different category, and it did not really exist five years ago in the volume it exists now. A landing zone designed in 2021 was not built to host the Function App behind your custom claims provider, the Logic App behind your dynamic approval policy, the queue-triggered Function that decides whether a lifecycle workflow disables an account. None of that was a workload pattern then.&lt;/p&gt;

&lt;p&gt;It is now. And those workloads are at least as sensitive as Entra Connect, because they execute on every relevant identity event, and a successful tamper does not generate the noisy reconfiguration footprint that touching Entra Connect would generate. They are quieter and at least as powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Azure RBAC inheritance chain is the trap
&lt;/h2&gt;

&lt;p&gt;Here is the part of the picture that does not get said out loud enough. &lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure role-based access control&lt;/a&gt; inherits top-down, and the inheritance is unbreakable. A role assignment at a resource, resource group, subscription, &lt;a href="https://learn.microsoft.com/en-us/azure/governance/management-groups/overview" rel="noopener noreferrer"&gt;management group&lt;/a&gt;, or the tenant root management group flows downward to every resource underneath. Contributor at the resource group flows to the resource. Contributor at the subscription flows to every resource group in it. Contributor at the management group flows to every subscription. Contributor at the root management group flows to &lt;em&gt;everything&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is fine for most workloads.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;It is corrosive for workloads that influence Entra decisions.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence is the entire argument of this article. Read it once more. The same RBAC model that makes Azure pleasant to operate at scale is the model that lets a Contributor four management groups above your Function App quietly become the author of the claims your applications trust. There is no Entra-side hardening that fixes this. There is no Conditional Access policy that fixes this. The fix is structural, and it lives in Azure.&lt;/p&gt;

&lt;p&gt;Picture an organization with a moderately mature Azure footprint. There is a platform management group, an application management group, a couple of regional management groups, a Sandbox management group, a Production management group. The custom claims provider Function App lives in a shared "platform services" subscription because that is where the platform team puts platform things. Now ask the inheritance question: who is Contributor on that subscription? Who is Contributor on its parent management group? Who is Contributor on the root? In most organizations the answer is "more people than you think, and at least one service principal you forgot about." Any one of them can replace the deployed code on that Function App. They do not need to touch the source repository. They do not need to break the CI/CD pipeline. They just deploy a new ZIP. The next time Entra calls your custom claims provider, it is calling their code.&lt;/p&gt;

&lt;p&gt;Source-control hardening is necessary and frequently celebrated. It is also not sufficient on its own, because Entra does not invoke your repository. Entra invokes the runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where these resources actually belong
&lt;/h2&gt;

&lt;p&gt;My direct take. The Azure resources that host code which influences Entra decisions are Control Plane resources. They belong either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;directly under the &lt;strong&gt;root management group&lt;/strong&gt;, in a dedicated subscription that exists for exactly this purpose, or&lt;/li&gt;
&lt;li&gt;one level deeper, under a dedicated &lt;strong&gt;Control Plane management group&lt;/strong&gt; that itself sits directly under the root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not deeper. Not in the platform identity subscription where your domain controllers live. Not in a shared services subscription. Not in an application landing zone, ever. The blast radius of "any Contributor anywhere on the inheritance path can swap the runtime" forces the resources up the tree, into a place where the list of principals with write rights is short, audited, and reviewed.&lt;/p&gt;

&lt;p&gt;That choice has follow-on consequences. Resources at the top of the hierarchy do not benefit from the policies and guardrails the landing zone reference architecture applies further down. The dedicated Control Plane management group needs its own policy set: deny-by-default network rules, mandatory diagnostic settings to a tenant-level log analytics workspace, deny-create policies for everything except the resource types you actually need, and Resource Locks at the resource group level that survive the people who set them. Pinning these things at the top of the tree means designing them at the top of the tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions you must be able to answer
&lt;/h2&gt;

&lt;p&gt;Before any Entra extension goes into production, the team that owns it has to be able to answer each of these without hedging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who controls the source code? Specifically, who has push rights on the protected branch, who can bypass branch protection, and who can approve a pull request?&lt;/li&gt;
&lt;li&gt;Who controls the CI/CD pipeline? Who can edit the pipeline definition? Who can re-run a failed deployment with a different artifact?&lt;/li&gt;
&lt;li&gt;What identity does the pipeline run as when it deploys to Azure? If it is a service principal with a client secret or a stored certificate, treat that credential as already known to the threat actor.&lt;/li&gt;
&lt;li&gt;Who can directly replace the runtime, bypassing source and pipeline entirely? Walk the full RBAC inheritance chain from the resource up to the root, including every group, service principal, and managed identity with Contributor, Owner, or User Access Administrator.&lt;/li&gt;
&lt;li&gt;Where do the credentials this extension uses to call downstream systems live? Who has Owner on that Key Vault? Walk the inheritance chain again.&lt;/li&gt;
&lt;li&gt;Does the extension run with a system-assigned managed identity, a user-assigned managed identity shared with other workloads, a certificate in a Key Vault, or a client secret? Each answer has a different blast radius.&lt;/li&gt;
&lt;li&gt;How fast can you detect that the deployed code on the Function App or the deployed logic on the Logic App has changed? How fast can you detect that a role assignment was created on the hosting subscription or one of its parents?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of those questions is uncomfortable, the extension is not yet ready to be invoked by Entra in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;The two scenarios I want to walk in detail are a &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;custom claims provider&lt;/a&gt; backed by an Azure Function and a &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;dynamic approval Logic App&lt;/a&gt; backing an entitlement management access package. Both are concrete. Both are increasingly common. Both expose the entire chain: source repository, pipeline identity, runtime resource, hosting subscription, RBAC inheritance, downstream credentials, Key Vault, the lot.&lt;/p&gt;

&lt;p&gt;And both lead into a question that deserves a fair, opinionated answer: what credential should that code use when it has to call back into Microsoft Graph or out to an external system? Static API key, certificate, or managed identity? Each option has a real failure mode and a real guardrail. The right answer is rarely the convenient one.&lt;/p&gt;

&lt;p&gt;That is the subject of the next article in this series. Where Part 1 makes the case that Entra extensibility is Control Plane, Part 2 will walk the two examples end to end and grade the credential ladder beneath them. Watch this space.&lt;/p&gt;

&lt;p&gt;The goal of this series is not to talk anyone out of using Entra extensibility. The opposite. Use it. It is what makes Entra a great product. Just host the code that influences Entra decisions the same way you host the things Entra is protecting.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/custom-extension-overview" rel="noopener noreferrer"&gt;Custom authentication extensions overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-external-method-manage" rel="noopener noreferrer"&gt;Manage an external authentication method in Microsoft Entra ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/privileged-identity-management-custom-extensions" rel="noopener noreferrer"&gt;PIM custom extensions for role activation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/lifecycle-workflow-extensibility" rel="noopener noreferrer"&gt;Lifecycle workflow extensibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-logic-apps-integration" rel="noopener noreferrer"&gt;Entitlement management Logic Apps integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-dynamic-approval" rel="noopener noreferrer"&gt;Externally determine approval requirements for an access package using custom extensions (dynamic approval)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/overview" rel="noopener noreferrer"&gt;Azure Cloud Adoption Framework overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/" rel="noopener noreferrer"&gt;Azure landing zones&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/resource-org-subscriptions" rel="noopener noreferrer"&gt;Landing zone subscription organization and governance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/landing-zone/design-area/identity-access" rel="noopener noreferrer"&gt;Landing zone identity and access management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview" rel="noopener noreferrer"&gt;Azure RBAC overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/governance/management-groups/overview" rel="noopener noreferrer"&gt;Azure management groups&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model" rel="noopener noreferrer"&gt;Enterprise Access Model (Control Plane / Management Plane)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoftentra</category>
      <category>zerotrust</category>
      <category>identitygovernance</category>
      <category>cloudadoptionframework</category>
    </item>
    <item>
      <title>One identity, two clouds: an AWS-hosted agent that authorizes with Microsoft Entra Agent ID</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Tue, 23 Jun 2026 09:23:00 +0000</pubDate>
      <link>https://dev.to/astaykov/one-identity-two-clouds-an-aws-hosted-agent-that-authorizes-with-microsoft-entra-agent-id-2bkl</link>
      <guid>https://dev.to/astaykov/one-identity-two-clouds-an-aws-hosted-agent-that-authorizes-with-microsoft-entra-agent-id-2bkl</guid>
      <description>&lt;p&gt;There is a quiet assumption baked into most identity conversations, and it usually goes unstated because everyone in the room already believes it: Microsoft identity is for Microsoft resources, and you run it on Microsoft infrastructure. Both halves feel so obvious that nobody bothers to test them.&lt;/p&gt;

&lt;p&gt;So I tested them. I built a proof-of-concept where an AI agent runs entirely on AWS, holds its own Microsoft Entra Agent ID, and uses that single identity to authorize calls to resources on &lt;em&gt;both&lt;/em&gt; sides of the cloud boundary: a Microsoft service and an AWS Lambda. The agent never touches Azure compute. The Lambda never sees an AWS credential on the call. And nothing about it required a special bridge, because the bridge already exists and it is called OAuth.&lt;/p&gt;

&lt;p&gt;The sample is &lt;a href="https://github.com/astaykov/agentid-agentcore" rel="noopener noreferrer"&gt;astaykov/agentid-agentcore&lt;/a&gt;. This is the walkthrough of why it works, written for the architect whose first reaction to "Microsoft Entra in my AWS stack" is a raised eyebrow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of it, stated plainly
&lt;/h2&gt;

&lt;p&gt;A browser single-page app signs the user in with &lt;a href="https://learn.microsoft.com/entra/identity-platform/msal-overview" rel="noopener noreferrer"&gt;MSAL.js&lt;/a&gt; and hands the resulting access token to an agent. The agent is hosted on &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html" rel="noopener noreferrer"&gt;AWS Bedrock AgentCore Managed Runtime&lt;/a&gt;, a fully managed runtime for agents. Inside that runtime, the agent acts &lt;em&gt;on behalf of the signed-in user&lt;/em&gt; against two downstream resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;AWS Lambda&lt;/strong&gt;, fronted by an API Gateway HTTP API (the PoC calls it the Echo API), and&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;&lt;a href="https://learn.microsoft.com/graph/mcp-server/get-started" rel="noopener noreferrer"&gt;Microsoft MCP Server for Enterprise&lt;/a&gt;&lt;/strong&gt;, the Microsoft-hosted tool surface over Microsoft Graph.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  ↓  sign-in (MSAL.js)
Browser SPA
  ↓  Entra access token  [Bearer, header-only]
AgentCore Managed Runtime  ── hosted on AWS, holds an Entra Agent ID
  ├─→  AWS Lambda  (API Gateway HTTP API, native JWT authorizer)
  └─→  Microsoft MCP server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that topology again and notice what is &lt;em&gt;not&lt;/em&gt; in it. There is no Azure virtual machine, no Azure Container App, no Azure Function. The compute is 100% AWS. The identity is 100% Microsoft Entra. Those two facts are usually assumed to travel together. They do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AWS resource is the whole point
&lt;/h2&gt;

&lt;p&gt;It would be an unremarkable demo if the agent only called Microsoft services. Of course a Microsoft identity opens Microsoft doors. The claim that actually needs proving is the other one: that a Microsoft Entra-issued token can open an &lt;em&gt;AWS&lt;/em&gt; door.&lt;/p&gt;

&lt;p&gt;So look closely at the Echo API. It is an &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop.html" rel="noopener noreferrer"&gt;AWS API Gateway v2 HTTP API&lt;/a&gt; in front of a Lambda function. Nothing about it is Microsoft. And its access control is a &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html" rel="noopener noreferrer"&gt;native API Gateway JWT authorizer&lt;/a&gt;, the AWS-built-in kind, configured with exactly two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;issuer&lt;/strong&gt; of &lt;code&gt;https://login.microsoftonline.com/{tenant}/v2.0&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;audience&lt;/strong&gt; equal to the Echo API's own app registration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the entire trust configuration. An AWS resource, gated by an AWS-native authorizer, that grants access only when presented with a token &lt;em&gt;minted by Microsoft Entra&lt;/em&gt; and bound to the signed-in user. No Cognito. No IAM SigV4 signing on that request. No custom Lambda authorizer doing bespoke validation. The AWS platform validates a Microsoft token because, to an OIDC-aware authorizer, Microsoft Entra is simply another standards-compliant issuer.&lt;/p&gt;

&lt;p&gt;This is the sentence I want the skeptical AWS architect to sit with: &lt;em&gt;your API Gateway already knows how to trust Microsoft Entra. You have not been told to think of it that way, but the capability has been there all along.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Same identity, the Microsoft side
&lt;/h2&gt;

&lt;p&gt;The same agent, carrying the same identity, also calls the Microsoft MCP server. One agent. One Entra Agent ID. Two destinations in two different clouds, each handed a token scoped to it, each validating that token on its own terms.&lt;/p&gt;

&lt;p&gt;This is what makes Entra Agent ID interesting as an identity layer rather than a Microsoft-only convenience. The agent is not "an AWS thing that occasionally phones Microsoft." It is a governed identity in Microsoft Entra (&lt;a href="https://learn.microsoft.com/entra/agent-id/what-is-microsoft-entra-agent-id" rel="noopener noreferrer"&gt;an agent identity created from a blueprint&lt;/a&gt;) that happens to be hosted on AWS, and it reaches whatever it is authorized to reach regardless of which cloud the resource lives in. Microsoft's own documentation is explicit that &lt;a href="https://learn.microsoft.com/entra/agent-id/what-is-microsoft-entra-agent-id" rel="noopener noreferrer"&gt;Entra Agent ID works with agents built on non-Microsoft platforms, AWS Bedrock among them&lt;/a&gt;. The PoC is just that statement made concrete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this should land for an AWS architect
&lt;/h2&gt;

&lt;p&gt;The instinct to distrust cross-vendor identity is healthy, because most of it historically meant proprietary glue: a connector, an SDK shim, a sync job that copied identities from one directory into another and drifted out of band within a quarter. None of that is happening here.&lt;/p&gt;

&lt;p&gt;What is happening is plain OAuth 2.0 and OpenID Connect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ The AWS authorizer trusts an &lt;strong&gt;OIDC issuer&lt;/strong&gt;. Entra publishes standard OIDC discovery metadata; the authorizer validates &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;aud&lt;/code&gt;, and signature against it. This is the same mechanism you would use to trust any external IdP.&lt;/li&gt;
&lt;li&gt;✅ The agent acquires &lt;strong&gt;resource-scoped, user-bound tokens&lt;/strong&gt; through a standard &lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-on-behalf-of-flow" rel="noopener noreferrer"&gt;on-behalf-of style exchange&lt;/a&gt;. The user's sign-in is turned into a token audienced to each specific downstream resource before the call is made.&lt;/li&gt;
&lt;li&gt;⚙️ The agent runtime does use &lt;strong&gt;&lt;a href="https://learn.microsoft.com/entra/msal/python/" rel="noopener noreferrer"&gt;MSAL Python&lt;/a&gt;&lt;/strong&gt; to perform the exchange. That is not a proprietary bridge between the clouds; it is Microsoft's standards-based authentication library, the same one you would reach for to speak OAuth and OIDC to Entra from any Python application. The wire protocol is still OAuth and OIDC. MSAL is simply the most direct way to speak it from the agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already federate your AWS workloads to Microsoft Entra ID for human sign-in, this is the next step: it is time for your AI agents to get their identity from there too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The token exchange, kept in its lane
&lt;/h2&gt;

&lt;p&gt;There is real machinery behind "acts on behalf of the user," and it is worth one honest paragraph rather than a victory lap of acronyms. Before each downstream call, the agent converts the inbound user token into a token scoped to the specific target resource, in-process, using &lt;a href="https://learn.microsoft.com/entra/msal/python/" rel="noopener noreferrer"&gt;MSAL Python&lt;/a&gt;. The result is a token whose audience is the AWS Lambda for the Lambda call, and the MCP server for the MCP call, with the original user identity preserved throughout. The two-stage exchange that Entra Agent ID uses to do this is &lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow" rel="noopener noreferrer"&gt;documented here&lt;/a&gt;; it is the plumbing, not the headline. The headline is that the user's delegated context survives the entire trip, across clouds, to the downstream resources. And this chain can be infinite hops long.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security posture, briefly
&lt;/h2&gt;

&lt;p&gt;A few properties matter more than the rest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔐 The agent never hands a credential to the model. Tools return only their response payloads; the inbound token lives in a tightly scoped variable that is cleared in a &lt;code&gt;finally&lt;/code&gt; block after every invocation and never reaches the LLM.&lt;/li&gt;
&lt;li&gt;🔍 Tokens are never logged. Only non-secret routing claims (issuer, audience, app id, scope, and the user's object id as a cache key) are emitted, never a token, header, or signature.&lt;/li&gt;
&lt;li&gt;The blueprint credential is read once per process and dropped from memory immediately after the client is constructed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the honest production caveat, because a PoC that hides its rough edges is selling something: this sample authenticates the agent's blueprint with a client secret, which is fine for a demo and &lt;a href="https://learn.microsoft.com/entra/agent-id/best-practices-agent-id" rel="noopener noreferrer"&gt;explicitly not recommended for production&lt;/a&gt;. The production path is a certificate or, better, a &lt;a href="https://learn.microsoft.com/entra/agent-id/create-blueprint#configure-credentials-for-the-agent-identity-blueprint" rel="noopener noreferrer"&gt;managed identity used as a federated credential&lt;/a&gt; so there is no stored secret at all. I have made &lt;a href="https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602"&gt;the broader case for a secret-less agentic platform before&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next, and the thesis
&lt;/h2&gt;

&lt;p&gt;The current PoC still keeps one secret on the AWS side: the blueprint credential. The next step closes that gap in the most fitting way possible: register AWS's own STS as a federated identity credential the Entra blueprint trusts, and let AWS issue the token that proves the agent's identity to Microsoft. At that point the loop is closed in both directions. AWS resources trust Microsoft-issued tokens, and Microsoft trusts AWS-issued tokens, with no shared secret anywhere between them.&lt;/p&gt;

&lt;p&gt;That is the whole thesis, and it is smaller than it sounds: a resource trusts a token, not a cloud. The delegation semantics that have governed &lt;a href="https://dev.to/astaykov/from-obo-apis-to-agent-identities-entra-conditional-access-still-works-the-same-140c"&gt;middle-tier APIs for years&lt;/a&gt; did not change when the middle tier became an AI agent, and they do not change when that agent moves to AWS. An identity is a protocol boundary. If your Lambda can trust Microsoft Entra Agent ID, your agent can run anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/astaykov/agentid-agentcore" rel="noopener noreferrer"&gt;astaykov/agentid-agentcore (the PoC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/what-is-microsoft-entra-agent-id" rel="noopener noreferrer"&gt;What is Microsoft Entra Agent ID?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/autonomous-agent-authentication-authorization-flow" rel="noopener noreferrer"&gt;Authenticate and acquire tokens for autonomous agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/best-practices-agent-id" rel="noopener noreferrer"&gt;Best practices for Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/agent-id/create-blueprint" rel="noopener noreferrer"&gt;Create an agent identity blueprint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/v2-oauth2-on-behalf-of-flow" rel="noopener noreferrer"&gt;Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/msal/python/" rel="noopener noreferrer"&gt;MSAL for Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/entra/identity-platform/msal-overview" rel="noopener noreferrer"&gt;MSAL overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/graph/mcp-server/get-started" rel="noopener noreferrer"&gt;Get started with the Microsoft MCP Server for Enterprise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agents-tools-runtime.html" rel="noopener noreferrer"&gt;AWS Bedrock AgentCore Runtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/identity.html" rel="noopener noreferrer"&gt;AWS Bedrock AgentCore Identity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html" rel="noopener noreferrer"&gt;Control access to HTTP APIs with JWT authorizers (API Gateway)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop.html" rel="noopener noreferrer"&gt;Develop HTTP APIs in API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>aws</category>
      <category>bedrockagentcore</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>A transformational AI agent does not start with the permissions it needs</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Wed, 17 Jun 2026 09:13:00 +0000</pubDate>
      <link>https://dev.to/astaykov/a-transformational-ai-agent-does-not-start-with-the-permissions-it-needs-4g8l</link>
      <guid>https://dev.to/astaykov/a-transformational-ai-agent-does-not-start-with-the-permissions-it-needs-4g8l</guid>
      <description>&lt;p&gt;Most teams are still building AI agents the way they built automation in 2019: create an identity, shovel a pile of permissions onto it, pray the list is not too dangerous, and call that least privilege. That model was weak for static applications. For AI agents, it is worse.&lt;/p&gt;

&lt;p&gt;An agent is not a static script. Its value comes from encountering new situations, discovering new APIs, and taking on work that was not fully known at deployment time. Pre-authorizing that kind of system is like issuing a master key because you do not know which doors the employee will need next month. Convenient, yes. Serious architecture, no.&lt;/p&gt;

&lt;p&gt;The more useful pattern is the opposite: start the agent with effectively zero useful permissions for the task in front of it, let it discover what permission is actually missing when it hits a boundary, and force any expansion of access through a governed path such as &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt;. That is the pattern I wanted to understand better, so I built a practical sample around it.&lt;/p&gt;

&lt;p&gt;The sample is &lt;a href="https://github.com/astaykov/n8n-aca" rel="noopener noreferrer"&gt;astaykov/n8n-aca&lt;/a&gt;, a one-command starter for &lt;a href="https://github.com/astaykov/n8n-aca" rel="noopener noreferrer"&gt;n8n on Azure Container Apps with Entra Agent ID&lt;/a&gt;. From Azure Cloud Shell, &lt;code&gt;azd up&lt;/code&gt; provisions the Azure infrastructure, creates the Entra objects, installs the custom Entra Agent ID node for n8n, wires the credentials, imports the workflows, and activates the triggers. It is the fastest way I know to get from "I want to test agentic identity patterns" to a running system without spending the afternoon on scaffolding.&lt;/p&gt;

&lt;p&gt;That convenience is not the interesting part, though. The interesting part is what the sample made easy to test: an autonomous agent on &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt; that authenticates with its own identity, uses &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id#microsoft-graph-application-permissions" rel="noopener noreferrer"&gt;application permissions&lt;/a&gt; when it needs Microsoft Graph, and expands its access only through &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;ID Governance&lt;/a&gt; when a real task justifies it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sample is the on-ramp, not the point
&lt;/h2&gt;

&lt;p&gt;The repo gives a practical "one-click" starting point, even if the literal mechanism is a single command rather than a portal button. It deploys n8n on Azure Container Apps, provisions the surrounding Azure services, and creates the Entra building blocks you need: an &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;agent identity blueprint and agent identity&lt;/a&gt;, plus the n8n credentials and workflows that let you immediately test autonomous and assistive patterns.&lt;/p&gt;

&lt;p&gt;That matters because identity architects do not need another abstract reference architecture. They need something they can stand up, inspect, and break. The sample gives you that. But the design pattern it enables is the real story.&lt;/p&gt;

&lt;p&gt;Microsoft Entra Agent ID exists precisely because traditional, deterministic application identities are the wrong abstraction for agents that evolve in production. Microsoft says that plainly: classic application identities were designed for static workloads and do not bring the governance model agents need, while &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;agent identities and blueprints&lt;/a&gt; add lifecycle, sponsorship, and policy structure that maps to how agents actually operate. Once you accept that framing, the next question becomes obvious: if the identity model is dynamic, why are we still designing permissions as if they were static?&lt;/p&gt;

&lt;h2&gt;
  
  
  The right starting state is deliberate under-permissioning
&lt;/h2&gt;

&lt;p&gt;When people hear "start with zero permissions," they often imagine an unusable toy. That is not what I mean.&lt;/p&gt;

&lt;p&gt;I mean zero useful permissions for the specific task the agent has not encountered yet.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;authorization model for Entra Agent ID&lt;/a&gt; already points in that direction. Agents can use delegated permissions or application permissions, but high-risk permissions are blocked, lower-privilege access is preferred, and administrators are expected to grant access in a scoped way. &lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;Governance for agent identities&lt;/a&gt; goes one step further by describing access packages as the structure through which agents can receive OAuth application permissions, group memberships, and Entra roles in a time-bound, auditable way.&lt;/p&gt;

&lt;p&gt;That combination changes the design target. The goal is no longer "figure out every permission the agent might need before go-live." The goal is "build an agent that can recognize a missing permission, identify the governed package that provides it, and request it through policy."&lt;/p&gt;

&lt;p&gt;That is the shift. It is subtle, but it is the whole game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the pattern actually lives in n8n
&lt;/h2&gt;

&lt;p&gt;At a glance, the workflow looks almost too simple to carry this much identity behavior: webhook in, token acquisition, AI agent, webhook out. That is exactly why the image earns its place here. The interesting part is not the number of nodes. It is the control logic concentrated inside the agent and its tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdzmccc6o7dmyf2yltxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdzmccc6o7dmyf2yltxe.png" alt="The n8n workflow is visually simple by design: webhook, Entra token acquisition, AI agent, and response, with Azure OpenAI, memory, Microsoft Learn MCP, and Microsoft Graph attached as the agent's runtime dependencies." width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The workflow is intentionally compact. The design pattern does not live in a sprawling orchestration graph; it lives in the policy encoded into the AI Agent node and the constraints encoded into the Microsoft Graph tool.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_9lcMliF0DU"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In my workflow, the pattern is not hidden in a policy engine or spread across ten helper scripts. It lives in two very concrete places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The system prompt for the AI Agent node.&lt;/li&gt;
&lt;li&gt;The custom configuration of the HTTP Request tool that calls Microsoft Graph.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is worth emphasizing because too many "agentic architecture" discussions still pretend the prompt is fluff. It is not. In a tool-using agent, the system prompt is operational policy.&lt;/p&gt;

&lt;p&gt;The n8n &lt;a href="https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/" rel="noopener noreferrer"&gt;AI Agent node&lt;/a&gt; is a tools-based agent. It receives the user request, chooses tools, reasons about tool responses, and keeps going until it can answer or it hits a policy boundary. In this workflow, the agent is told something very specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an AI Agent with your own Microsoft Entra identity.
You authenticate as yourself using Microsoft Graph application permissions.
When any API call returns 401 or 403, do NOT give up.
Identify the exact permission, search access packages, inspect each package,
and request the matching package if self-request is allowed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That instruction is doing real architectural work. It teaches the agent to treat &lt;code&gt;401&lt;/code&gt; and &lt;code&gt;403&lt;/code&gt; as a discovery loop, not as a dead end.&lt;/p&gt;

&lt;p&gt;It also teaches the agent to stay honest about what kind of access it has. The prompt explicitly says it only works with application permissions for Microsoft Graph. That matters because &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts#agent-operation-patterns" rel="noopener noreferrer"&gt;Entra Agent ID distinguishes autonomous agents from assistive agents&lt;/a&gt;, and the permission model is different depending on whether the agent is acting as itself or on behalf of a user. For this workflow, the autonomous case is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Microsoft Learn MCP server is the underrated component
&lt;/h2&gt;

&lt;p&gt;The agent has two tools in the workflow: a Microsoft Graph HTTP tool and a Microsoft Learn MCP client pointing at &lt;code&gt;https://learn.microsoft.com/api/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That second tool is the underrated component.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/training/support/mcp" rel="noopener noreferrer"&gt;Microsoft Learn MCP Server&lt;/a&gt; is public, free, and does not require authentication. More importantly, it gives the agent runtime access to official documentation and code examples through MCP instead of forcing you to hardcode every Microsoft Graph rule into the prompt. That is what turns the workflow from "an agent with a bag of fixed API calls" into "an agent that can learn how to call Graph correctly when it needs to."&lt;/p&gt;

&lt;p&gt;In practice, that means the agent can look up an endpoint, confirm which permission is required, and use the error it just received to tighten the search. That is the missing bridge between language-model flexibility and enterprise identity controls. Without it, the agent is guessing. With it, the agent has a documentation brain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The HTTP tool matters just as much as the prompt
&lt;/h2&gt;

&lt;p&gt;The n8n &lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/" rel="noopener noreferrer"&gt;HTTP Request node&lt;/a&gt; is versatile enough to be dangerous, which is exactly why the custom tool configuration matters.&lt;/p&gt;

&lt;p&gt;In this workflow, the HTTP tool is constrained to &lt;code&gt;https://graph.microsoft.com&lt;/code&gt;, prefers &lt;code&gt;/v1.0/&lt;/code&gt; over &lt;code&gt;/beta/&lt;/code&gt;, uses a predefined bearer credential, and is instructed to research the endpoint before writes. More importantly, it is configured to return the &lt;strong&gt;full response&lt;/strong&gt; and &lt;strong&gt;never error&lt;/strong&gt;. That is not a cosmetic choice. It means the agent gets the status code and body back even when Graph refuses the call, so it can reason over the authorization failure instead of having the workflow short-circuit.&lt;/p&gt;

&lt;p&gt;That one setting turns a generic REST tool into a permission-discovery instrument.&lt;/p&gt;

&lt;p&gt;The tool instructions also tell the agent to confirm state-changing operations before execution, except for one case: creating an &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-post-assignmentrequests?view=graph-rest-1.0" rel="noopener noreferrer"&gt;access package assignment request&lt;/a&gt;. That exception is deliberate. If the whole point is to let the agent acquire governed access in context, then the request path itself needs to be part of the autonomous loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the governed escalation loop works
&lt;/h2&gt;

&lt;p&gt;The loop is simple enough to explain, which is usually a sign you are finally dealing with the right abstraction.&lt;/p&gt;

&lt;p&gt;First, the agent attempts the Graph operation the user asked for.&lt;/p&gt;

&lt;p&gt;If Microsoft Graph returns &lt;code&gt;401&lt;/code&gt; or &lt;code&gt;403&lt;/code&gt;, the agent uses the error plus the Learn MCP server to identify the exact missing permission. The prompt forces it to write down the permission explicitly and to treat it as an application permission in this workflow.&lt;/p&gt;

&lt;p&gt;Next, it enumerates the available access packages with &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-accesspackages?view=graph-rest-1.0" rel="noopener noreferrer"&gt;GET &lt;code&gt;/identityGovernance/entitlementManagement/accessPackages&lt;/code&gt;&lt;/a&gt;. Then it inspects the package contents through &lt;a href="https://learn.microsoft.com/en-us/graph/api/accesspackage-list-resourcerolescopes?view=graph-rest-1.0" rel="noopener noreferrer"&gt;resource role scopes&lt;/a&gt;, looking for the role that maps to the missing permission. This is the key architectural move: the agent is not inventing new access. It is discovering which pre-governed package the identity team already made available.&lt;/p&gt;

&lt;p&gt;Once it finds the matching package, it reads the &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-assignmentpolicies?view=graph-rest-1.0" rel="noopener noreferrer"&gt;assignment policies&lt;/a&gt; to determine whether self-request is allowed. If it is, the agent creates a &lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-post-assignmentrequests?view=graph-rest-1.0" rel="noopener noreferrer"&gt;POST &lt;code&gt;/identityGovernance/entitlementManagement/assignmentRequests&lt;/code&gt;&lt;/a&gt; call for itself. If self-request is not allowed, it gives the user the package name, what it grants, and the My Access link so the user, their manager, or the sponsor can request it on the agent's behalf.&lt;/p&gt;

&lt;p&gt;That is the pattern in one sentence: the agent can discover and request governed access, but it cannot bypass governance.&lt;/p&gt;

&lt;p&gt;This is why &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;access packages for agent identities&lt;/a&gt; matter so much. Microsoft explicitly supports three pathways: the agent can request access programmatically, the sponsor can request it on behalf of the agent, or an administrator can assign it directly. The workflow leans into those pathways instead of trying to outsmart them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why identity architects should care
&lt;/h2&gt;

&lt;p&gt;This is not just a clever n8n trick. It is a better control model.&lt;/p&gt;

&lt;p&gt;When an identity team pre-grants broad application permissions to an agent, they are making an up-front guess about future behavior. Those guesses age badly. They are rarely revisited, almost never shrunk, and eventually become the authorization equivalent of legacy firewall rules.&lt;/p&gt;

&lt;p&gt;Access packages change the conversation. They let the identity team define bundles of allowed access, the &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;approval and request policies&lt;/a&gt;, the expiration model, and the human accountability path. The agent still moves fast, but the boundary conditions are owned by governance, not improvised in code.&lt;/p&gt;

&lt;p&gt;That is why I think this pattern matters: it decouples &lt;strong&gt;permission discovery&lt;/strong&gt; from &lt;strong&gt;permission approval&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The agent is free to discover what it needs. The organization is still in control of whether that need maps to an approved package, whether self-request is allowed, who must approve it, and when the access expires. That is a much more adult architecture than stuffing &lt;code&gt;Directory.Read.All&lt;/code&gt; into an app registration because the pilot customer wants the demo today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable but necessary conclusion
&lt;/h2&gt;

&lt;p&gt;The industry still talks about agent permissions as if the main design question were "what should this agent have?" That is already the wrong question.&lt;/p&gt;

&lt;p&gt;The better question is: &lt;em&gt;how should this agent acquire access when reality teaches us that the original permission set was incomplete?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If your answer is "we will update the app registration manually," you do not have an agentic permission model. You have an operations backlog.&lt;/p&gt;

&lt;p&gt;If your answer is "the agent starts narrow, learns what permission is missing from the task and the docs, maps that need to a governed access package, and either requests it or hands the human the approval path," then you are finally designing for the world these systems actually live in.&lt;/p&gt;

&lt;p&gt;That is what this sample helped me understand. n8n on Azure Container Apps with Entra Agent ID is a useful accelerator. The deeper lesson is the design pattern itself: agents should not begin life over-permissioned. They should begin life constrained, then &lt;em&gt;earn&lt;/em&gt; their scope through policy, context, and governance.&lt;/p&gt;

&lt;p&gt;That shift is the part identity architects should care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/astaykov/n8n-aca" rel="noopener noreferrer"&gt;astaykov/n8n-aca&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID key concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authorization-agent-id" rel="noopener noreferrer"&gt;Authorization in Microsoft Entra Agent ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-access-packages" rel="noopener noreferrer"&gt;Access packages for agent identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/id-governance/agent-id-governance-overview" rel="noopener noreferrer"&gt;Governing agent identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-post-assignmentrequests?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Create accessPackageAssignmentRequest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-accesspackages?view=graph-rest-1.0" rel="noopener noreferrer"&gt;List accessPackages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/accesspackage-list-resourcerolescopes?view=graph-rest-1.0" rel="noopener noreferrer"&gt;List resourceRoleScopes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-list-assignmentpolicies?view=graph-rest-1.0" rel="noopener noreferrer"&gt;List assignmentPolicies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/training/support/mcp" rel="noopener noreferrer"&gt;Microsoft Learn MCP Server overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/" rel="noopener noreferrer"&gt;n8n AI Agent node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/" rel="noopener noreferrer"&gt;n8n HTTP Request node&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>n8n</category>
      <category>identitygovernance</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Seeing every agent sign-in in one place: an Azure Monitor workbook for Entra Agent ID</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Wed, 10 Jun 2026 10:23:00 +0000</pubDate>
      <link>https://dev.to/astaykov/seeing-every-agent-sign-in-in-one-place-an-azure-monitor-workbook-for-entra-agent-id-4dgp</link>
      <guid>https://dev.to/astaykov/seeing-every-agent-sign-in-in-one-place-an-azure-monitor-workbook-for-entra-agent-id-4dgp</guid>
      <description>&lt;p&gt;The earlier articles in this series (&lt;a href="https://dev.to/astaykov/the-overlooked-gem-in-microsoft-entra-that-gives-your-ai-agents-super-powers-3mde"&gt;Part 1&lt;/a&gt;, &lt;a href="https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3"&gt;Part 2&lt;/a&gt;, and &lt;a href="https://dev.to/astaykov/finding-out-what-your-ai-agents-actually-got-discovering-consents-and-active-agents-in-microsoft-1eeo"&gt;Part 3&lt;/a&gt;) built the case from first principles: dynamic consent lets an agent earn its access in context, that accumulation quietly turns the agent into a high-value target, and the raw queries to discover what agents have been granted and what they actually exercise already exist in Microsoft Graph and Log Analytics. All of that is useful. None of it is visible unless someone remembers to run the query.&lt;/p&gt;

&lt;p&gt;A KQL query in a Log Analytics window is something you execute when you already suspect a problem. An &lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/visualize/workbooks-overview" rel="noopener noreferrer"&gt;Azure Monitor workbook&lt;/a&gt; is a parameterized view you open (or pin to a shared dashboard) to see the shape of agent traffic &lt;em&gt;before&lt;/em&gt; suspicion sets in. That is a different posture entirely, and it is the difference between investigation and awareness.&lt;/p&gt;

&lt;p&gt;This article walks through an open-source workbook that provides that awareness for every agent identity in the tenant, grouped by the construct that matters most for governance: the Agent Identity Blueprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the workbook shows
&lt;/h2&gt;

&lt;p&gt;The workbook, &lt;a href="https://github.com/Dayzure/entra-agent-id-workbooks" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;, queries two log tables from Microsoft Entra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AADNonInteractiveUserSignInLogs&lt;/code&gt;&lt;/strong&gt; for delegated agent sign-ins, where an agent identity acts on behalf of a human user (or on behalf of another agent user).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AADServicePrincipalSignInLogs&lt;/code&gt;&lt;/strong&gt; for autonomous agent sign-ins, where the agent identity authenticates with its own application-level credentials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both views are grouped by &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Agent Identity Blueprint&lt;/a&gt;, which is the parent construct that defines the agent's trust boundary. This is deliberate. &lt;a href="https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3"&gt;Part 2&lt;/a&gt; argued that the blueprint is the right choke point for Conditional Access and governance policy, because a policy applied to a blueprint propagates to every agent identity created from it. The workbook follows the same principle: start at the blueprint, drill into the agents underneath, then into the individual sign-in events.&lt;/p&gt;

&lt;p&gt;The top-level summary tiles show total sign-in counts, unique agent identities, successes, failures, and interrupts (pending user action), each with a sparkline trend. Clicking a tile filters the rest of the workbook to that status category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites and deployment
&lt;/h2&gt;

&lt;p&gt;The workbook requires a &lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/howto-integrate-activity-logs-with-azure-monitor-logs" rel="noopener noreferrer"&gt;Log Analytics workspace with Microsoft Entra sign-in logs flowing&lt;/a&gt; into it. Specifically, the &lt;strong&gt;non-interactive user sign-in logs&lt;/strong&gt; and &lt;strong&gt;service principal sign-in logs&lt;/strong&gt; diagnostic categories must be enabled in the Entra diagnostic settings. If you already have Entra sign-in logs routed to Log Analytics (most organizations with any monitoring posture do), you are ready.&lt;/p&gt;

&lt;p&gt;To deploy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;a href="https://github.com/Dayzure/entra-agent-id-workbooks/blob/main/agent-id-sign-ins.json" rel="noopener noreferrer"&gt;&lt;code&gt;agent-id-sign-ins.json&lt;/code&gt;&lt;/a&gt; from the GitHub repo.&lt;/li&gt;
&lt;li&gt;In the Azure portal, navigate to &lt;strong&gt;Azure Monitor &amp;gt; Workbooks &amp;gt; New&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;Advanced Editor&lt;/strong&gt; (the &lt;code&gt;&amp;lt;/&amp;gt;&lt;/code&gt; icon in the toolbar).&lt;/li&gt;
&lt;li&gt;Paste the JSON content and click &lt;strong&gt;Apply&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select your Log Analytics workspace and save.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is it. The parameters at the top of the workbook (time range, agent identity filter, user filter) populate automatically from the data in your workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delegated agent sign-ins
&lt;/h2&gt;

&lt;p&gt;The upper section of the workbook covers sign-ins where an agent identity acts in a delegated context, on behalf of a user. This is the pattern that &lt;a href="https://dev.to/astaykov/the-overlooked-gem-in-microsoft-entra-that-gives-your-ai-agents-super-powers-3mde"&gt;Part 1&lt;/a&gt; described: the agent acquires tokens with the user's consent, and each sign-in event is recorded in &lt;code&gt;AADNonInteractiveUserSignInLogs&lt;/code&gt; with the &lt;code&gt;Agent&lt;/code&gt; property that identifies the agent type and its parent blueprint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsm1v3zidsnnv4v3uvzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsm1v3zidsnnv4v3uvzu.png" alt="Workbook view showing delegated sign-ins grouped by Agent Blueprint, with the Entra SDK Blueprint expanded to show two human users and one agent user, plus sign-in details with resources and OAuth scopes" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The hierarchy works like this: blueprints sit at the top level, each showing total sign-in count, a trend sparkline, failure count, and interrupt count. Expanding a blueprint reveals the agent identities created from it. Selecting a row on the left populates the detail grid on the right.&lt;/p&gt;

&lt;p&gt;The detail grid is where the operational value lives. Each sign-in event shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;type indicator&lt;/strong&gt;: 🙋 for a human user or 🤖 for an agent user (identified by &lt;code&gt;agentSubjectType == 'agentIDuser'&lt;/code&gt; in the sign-in log).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;display name&lt;/strong&gt; of the user or agent user who was the subject of the sign-in.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;resource&lt;/strong&gt; that was accessed (Microsoft Graph, Azure DevOps, a custom API).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;OAuth scopes&lt;/strong&gt; that were included in the token, extracted from the &lt;code&gt;AuthenticationProcessingDetails&lt;/code&gt; field of the sign-in event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign-in status&lt;/strong&gt;, &lt;strong&gt;Conditional Access status&lt;/strong&gt;, and error details when something went wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent identity names in the left panel are clickable links that open the &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Agent Identity blade&lt;/a&gt; directly in the Microsoft Entra admin center. No copying of object IDs, no navigating through menus.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabm5le7spsga344sbjvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabm5le7spsga344sbjvu.png" alt="Workbook view with two blueprints expanded, showing the Az DevOops Blueprint with a selected agent identity whose sign-ins show Azure DevOps access with user_impersonation scope" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice what this view makes immediately obvious. In the screenshot above, the "Az DevOops Blueprint" has agent identities signing into Azure DevOps with &lt;code&gt;user_impersonation&lt;/code&gt; scope. That is the exact scenario from &lt;a href="https://dev.to/astaykov/your-ai-agent-doesnt-need-a-pat-to-work-in-azure-devops-fl8"&gt;the Azure DevOps article&lt;/a&gt;: an agent identity replacing a PAT with proper delegated access. The workbook shows it happening in production, with the user on whose behalf the agent acted, the resource it touched, and the scopes it used. If an unexpected resource or scope appears in that list, it is visible here without running a query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autonomous agent sign-ins
&lt;/h2&gt;

&lt;p&gt;The lower section covers the other half: agents authenticating with application-level credentials, without a user context. These are the autonomous agents, the ones that run scheduled jobs, background processing, or act as service accounts with &lt;code&gt;appRoleAssignments&lt;/code&gt; rather than delegated consent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7aqmz2qi6amycxoiqrr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo7aqmz2qi6amycxoiqrr.png" alt="Workbook view showing autonomous agent sign-ins grouped by blueprint, with agent identities accessing Claude Service Account, Anthropic WIF Trust, Anthropic APIs, and Microsoft Graph" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This section queries &lt;code&gt;AADServicePrincipalSignInLogs&lt;/code&gt; filtered to &lt;code&gt;agentType == 'agenticAppInstance'&lt;/code&gt;, and uses the same blueprint-first hierarchy. Expanding a blueprint shows each agent identity, and now each agent identity row also shows the &lt;strong&gt;Resources&lt;/strong&gt; column: a comma-separated list of every resource that agent has signed into during the selected time range. That is the agent's behavioral surface area in one cell.&lt;/p&gt;

&lt;p&gt;The screenshot tells a real story. The Anthropic agent identity, created from its blueprint, has signed into Claude Service Account, Anthropic WIF Trust, Anthropic APIs, and Microsoft Graph. That is the &lt;a href="https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602"&gt;secretless authentication pattern&lt;/a&gt; in action: the agent uses Workload Identity Federation to authenticate to Anthropic's APIs without an API key, and the sign-in logs capture every hop. The four failures against "Unknown Resource" are worth investigating (resource identity not resolved at sign-in time, typically a transient issue or a misconfigured resource), and they are visible here instead of hiding in a raw log table.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the workbook does not show
&lt;/h2&gt;

&lt;p&gt;This workbook covers the &lt;em&gt;behavioral&lt;/em&gt; side of agent visibility: who signed in, to what resource, with which scopes, and whether it worked. It does not show what was &lt;em&gt;granted&lt;/em&gt; via &lt;code&gt;oauth2PermissionGrants&lt;/code&gt; or &lt;code&gt;appRoleAssignments&lt;/code&gt;. That is the &lt;em&gt;entitlement&lt;/em&gt; side, and it lives in the Microsoft Graph queries covered in &lt;a href="https://dev.to/astaykov/finding-out-what-your-ai-agents-actually-got-discovering-consents-and-active-agents-in-microsoft-1eeo"&gt;Part 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The two are complementary. The workbook answers "what are agents doing?" The Graph queries answer "what could agents do?" The gap between the two, the granted-but-never-exercised permissions, is what &lt;a href="https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3"&gt;Part 2&lt;/a&gt; called the access-review backlog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;The workbook is open source and available at &lt;a href="https://github.com/Dayzure/entra-agent-id-workbooks" rel="noopener noreferrer"&gt;github.com/Dayzure/entra-agent-id-workbooks&lt;/a&gt;. Import it, point it at your Log Analytics workspace, and see what your agents have been doing. If the answer surprises you, the earlier articles in this series explain what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Dayzure/entra-agent-id-workbooks" rel="noopener noreferrer"&gt;Entra Agent ID workbook (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/azure-monitor/visualize/workbooks-overview" rel="noopener noreferrer"&gt;Azure Monitor workbooks overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-ins" rel="noopener noreferrer"&gt;Microsoft Entra sign-in logs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/howto-integrate-activity-logs-with-azure-monitor-logs" rel="noopener noreferrer"&gt;Integrate Entra activity logs with Azure Monitor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID key concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entra</category>
      <category>entraagentid</category>
      <category>aiagents</category>
      <category>azuremonitor</category>
    </item>
    <item>
      <title>Your AI agent doesn't need a PAT to work in Azure DevOps</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 04 Jun 2026 09:17:00 +0000</pubDate>
      <link>https://dev.to/astaykov/your-ai-agent-doesnt-need-a-pat-to-work-in-azure-devops-fl8</link>
      <guid>https://dev.to/astaykov/your-ai-agent-doesnt-need-a-pat-to-work-in-azure-devops-fl8</guid>
      <description>&lt;p&gt;At &lt;a href="https://collabsummit.eu/" rel="noopener noreferrer"&gt;Collab Summit 2026&lt;/a&gt; in the beginning of May, a fellow I had not seen in years caught me between sessions. We did the usual catching up, and then he got to the real question: "We're building an AI agent that works inside Azure DevOps. It creates work items, pushes code to branches, opens pull requests. Right now it authenticates with a PAT. How do we do this &lt;em&gt;properly&lt;/em&gt;?"&lt;/p&gt;

&lt;p&gt;The room around us was full of people building similar things. AI agents that are not chatbots answering questions, but &lt;em&gt;digital co-workers&lt;/em&gt;: autonomous participants on a development team, shipping code, triaging issues, responding to reviewer feedback. The pattern is already in production across more organizations than most conference talks acknowledge.&lt;/p&gt;

&lt;p&gt;His question deserved a better answer than "use a service principal." So I went home and built one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The identity primitives that were never designed for this
&lt;/h2&gt;

&lt;p&gt;When a non-human caller needs to authenticate &lt;em&gt;to&lt;/em&gt; Azure DevOps, two standard options exist today. Neither was designed for an autonomous AI agent acting as a member of a development team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personal Access Tokens (PATs).&lt;/strong&gt; A &lt;a href="https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate" rel="noopener noreferrer"&gt;PAT&lt;/a&gt; is a static bearer credential tied to a human user. It carries that user's identity and permissions, which means the agent's actions appear in audit logs as if the human performed them. There is no way to distinguish "the developer pushed a commit" from "the developer's agent pushed a commit." The token does not expire unless someone sets a date and remembers it. It cannot be scoped to a governed agent identity. It is a secret that sits in an environment variable and waits to be leaked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entra workload identities (service principals and managed identities).&lt;/strong&gt; Azure DevOps now supports &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/add-devops-entra-service-connection?view=azure-devops" rel="noopener noreferrer"&gt;Entra workload identity authentication&lt;/a&gt; for pipelines, which is a genuine improvement: service principals and managed identities can be added as users to an Azure DevOps organization, and pipelines authenticate to Azure DevOps resources through Workload Identity Federation with zero stored secrets. The credentials problem is solved. But the identity &lt;em&gt;model&lt;/em&gt; is still the pipeline model. A service principal added to an ADO organization has no concept of owner, sponsor, or blueprint. It has no delegated user pattern. Your SOC cannot filter its sign-in events as agentic activity. It is infrastructure plumbing designed for CI/CD jobs accessing repos and artifact feeds, not for a digital co-worker whose commits, work items, and PR comments should carry a traceable, governed agent identity with lifecycle metadata.&lt;/p&gt;

&lt;p&gt;Both options were designed for a world where non-human callers were pipelines, scheduled jobs, or integration hooks. That world is not the one we are building in anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an AI agent actually needs
&lt;/h2&gt;

&lt;p&gt;An AI agent that operates as a digital co-worker on a development team needs something fundamentally different from a pipeline credential. It needs an &lt;em&gt;identity&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not a token. Not a secret. An identity with the same governance surface that a human team member carries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An owner&lt;/strong&gt; who is accountable for what the agent does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A sponsor&lt;/strong&gt; with business context for why the agent exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditable sign-in events&lt;/strong&gt; that your SOC can filter, correlate, and alert on, tagged explicitly as agentic activity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A credential model&lt;/strong&gt; where the agent's runtime code never touches secrets (because secrets in agent memory are an attack surface, not a convenience).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A lifecycle&lt;/strong&gt; that ties the identity to the agent's existence, so decommissioning the agent means decommissioning its access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a wishlist for a future platform. This is what &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/agent-id-overview" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt; provides today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entra Agent ID: the IAM shift
&lt;/h2&gt;

&lt;p&gt;If you have followed this series, you have seen Entra Agent ID applied to &lt;a href="https://dev.to/astaykov/your-ai-agent-doesnt-need-an-api-key-entra-agent-id-and-anthropics-workload-identity-federation-el0"&gt;calling third-party model APIs without API keys&lt;/a&gt; and to &lt;a href="https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602"&gt;building secret-less agentic platforms with managed identities and Workload Identity Federation&lt;/a&gt;. The pattern is consistent, but it is worth stating the fundamental shift plainly, because it changes how we think about IAM for AI agents.&lt;/p&gt;

&lt;p&gt;Traditional IAM for non-human workloads treats the workload as infrastructure. You create a service principal, hand it credentials, scope its permissions, and monitor it (if you remember) through workload identity logs that most SOC teams never look at. The workload has no owner in the governance sense. It has no sponsor. It has no blueprint that propagates policy to every instance. It is a row in an app registration table that someone created two years ago and nobody is sure whether it is still in use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt; treats the AI agent as a &lt;em&gt;participant&lt;/em&gt;. The identity constructs are purpose-built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-blueprint" rel="noopener noreferrer"&gt;Agent Identity Blueprints&lt;/a&gt;&lt;/strong&gt; are the authentication foundation and the policy anchor. A blueprint holds (or federates) the credentials and parents one or more agent identities. Conditional Access policies applied to a blueprint propagate to every agent identity it creates. One policy decision, enforced across an entire family of agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent Identities&lt;/a&gt;&lt;/strong&gt; are the runtime identity of a specific agent. No credentials of their own. They authenticate through their blueprint. They carry owner and sponsor metadata. They produce sign-in events tagged as agentic, visible in the same logs your SOC already monitors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints" rel="noopener noreferrer"&gt;Entra SDK Auth Sidecar&lt;/a&gt;&lt;/strong&gt; handles all credential work in a separate container. The agent code never sees a secret, never handles token acquisition, never stores credentials in memory. The sidecar is &lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/security" rel="noopener noreferrer"&gt;not exposed to the host network&lt;/a&gt;, so the token acquisition surface is isolated from the agent's own attack surface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a better way to do service principals. It is a different category.&lt;/p&gt;

&lt;h2&gt;
  
  
  The delegated user pattern and Azure DevOps
&lt;/h2&gt;

&lt;p&gt;For an AI agent that acts as a digital co-worker on a development team, the &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;Agent ID User (delegated) pattern&lt;/a&gt; is the right fit. The agent acts on behalf of a specific user (its own agent user account in Entra), and every action it takes in Azure DevOps carries that user's identity.&lt;/p&gt;

&lt;p&gt;The identity flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The sidecar authenticates to Entra ID using the blueprint's credentials.&lt;/li&gt;
&lt;li&gt;The agent requests a token from the sidecar for Azure DevOps, specifying its Agent Identity and its Agent Username.&lt;/li&gt;
&lt;li&gt;The sidecar performs the Federated Identity Credential (FIC) token exchange with Entra ID.&lt;/li&gt;
&lt;li&gt;Entra issues a Bearer token scoped to the Azure DevOps resource (&lt;code&gt;499b84ac-1321-427f-aa17-267ca6975798&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The agent calls the &lt;a href="https://learn.microsoft.com/en-us/rest/api/azure/devops/" rel="noopener noreferrer"&gt;Azure DevOps REST API&lt;/a&gt; with that token.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent user is a real member of the Azure DevOps organization, with its own access level, project permissions, and repository access. When the agent creates a work item, it appears as created by the agent user. When it pushes a commit, the commit author is the agent user. When it opens a pull request, the PR creator is the agent user. The audit trail is clean, attributable, and governed.&lt;/p&gt;

&lt;p&gt;No PAT. No service connection. No secret stored in agent code. The only credential in the system is the blueprint's, managed by the sidecar in an isolated container.&lt;/p&gt;

&lt;h2&gt;
  
  
  The proof of concept
&lt;/h2&gt;

&lt;p&gt;I built a &lt;a href="https://github.com/astaykov/agentid-azure-devops" rel="noopener noreferrer"&gt;proof of concept&lt;/a&gt; that demonstrates the full pattern. The agent is powered by Azure OpenAI with &lt;a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling" rel="noopener noreferrer"&gt;function calling&lt;/a&gt; and can create work items, push file changes to new branches, open pull requests with reviewer assignments, read PR comment threads, and reply to reviewer feedback.&lt;/p&gt;

&lt;p&gt;The architecture is deliberately simple: two containers on a Docker bridge network. The &lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/configuration" rel="noopener noreferrer"&gt;Entra SDK auth sidecar&lt;/a&gt; handles token acquisition; the agent handles DevOps operations. The sidecar has no host port exposure (per &lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/security" rel="noopener noreferrer"&gt;Microsoft's security guidance&lt;/a&gt;); the agent exposes a chat interface on port 4192 for demonstration purposes only.&lt;/p&gt;

&lt;p&gt;The chat interface is there because it makes it easy to observe what the agent is doing during a demo. In a production deployment, the agent would be fully autonomous: triggered by events (a new issue, a failing build, a PR review request), executing its DevOps workflow, and reporting results through whatever channel the team uses. The identity model is identical either way.&lt;/p&gt;

&lt;p&gt;The PoC includes a chat UI where you can give the agent instructions like "Create an issue titled 'Fix login timeout', push a fix to a new branch, and open a PR for it." The agent orchestrates the entire flow, calling Azure DevOps REST APIs through the sidecar-acquired tokens. The &lt;a href="https://github.com/astaykov/agentid-azure-devops#readme" rel="noopener noreferrer"&gt;repository README&lt;/a&gt; covers the prerequisites, the Entra configuration (including the &lt;a href="https://learn.microsoft.com/en-us/graph/api/oauth2permissiongrant-post?view=graph-rest-1.0" rel="noopener noreferrer"&gt;delegated permission grants&lt;/a&gt; that are easy to miss), and the Docker Compose setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Entra side requires
&lt;/h2&gt;

&lt;p&gt;The Entra configuration involves four objects, and the relationship between them is worth understanding even if the README walks you through it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Blueprint app registration&lt;/strong&gt; that holds the credentials (client secret for local development; &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity" rel="noopener noreferrer"&gt;managed identity as a federated credential&lt;/a&gt; for production, as covered in a &lt;a href="https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602"&gt;previous article in this series&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An Agent Identity&lt;/strong&gt; (service principal) created from the blueprint. This is the agent's runtime identity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An Agent User&lt;/strong&gt; in your Entra tenant. This is the user account the agent acts on behalf of.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegated permission grants&lt;/strong&gt; created via Microsoft Graph's &lt;a href="https://learn.microsoft.com/en-us/graph/api/oauth2permissiongrant-post?view=graph-rest-1.0" rel="noopener noreferrer"&gt;&lt;code&gt;oauth2PermissionGrant&lt;/code&gt;&lt;/a&gt; endpoint. The Agent Identity needs &lt;code&gt;user_impersonation&lt;/code&gt; on the Azure DevOps resource and the standard OpenID Connect scopes on Microsoft Graph. These grants must be scoped to the Agent User (consent type &lt;code&gt;Principal&lt;/code&gt;, not &lt;code&gt;AllPrincipals&lt;/code&gt;). This is the step most people miss on their first attempt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the Azure DevOps side requires
&lt;/h2&gt;

&lt;p&gt;The Azure DevOps side is surprisingly straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The organization must be backed by the same Entra tenant.&lt;/li&gt;
&lt;li&gt;The Agent User must be a member of the organization with &lt;strong&gt;Basic&lt;/strong&gt; access level (Stakeholders cannot access Code/Repos).&lt;/li&gt;
&lt;li&gt;The Agent User needs project-level permissions: Work Items (Read/Write), Code (Contribute), and Pull Requests (Create) on the target project and repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is it. No service connection to configure. No PAT to mint and rotate. The agent authenticates through Entra, and Azure DevOps sees a regular user with scoped permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  From demo to production
&lt;/h2&gt;

&lt;p&gt;The PoC runs on Docker Compose with a client secret for the blueprint. A production deployment changes three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Replace the client secret&lt;/strong&gt; with a &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity" rel="noopener noreferrer"&gt;managed identity federated credential&lt;/a&gt; or a &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt; credential from your compute platform (AKS, Container Apps, etc.). The sidecar supports both through its &lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/configuration" rel="noopener noreferrer"&gt;credential source configuration&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the sidecar for Azure OpenAI authentication too.&lt;/strong&gt; The PoC uses an API key for Azure OpenAI because the focus is on the Azure DevOps identity pattern, but in production, the sidecar should acquire tokens for the Cognitive Services resource as well. No reason to have &lt;em&gt;any&lt;/em&gt; static secret in the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add the governance layer.&lt;/strong&gt; Wire the agent's sign-in events into your SOC monitoring. Set up access reviews keyed to actual API usage, not calendar recertification. Apply Conditional Access policies to the blueprint. Earlier articles in this series (&lt;a href="https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3"&gt;governance&lt;/a&gt; and &lt;a href="https://dev.to/astaykov/finding-out-what-your-ai-agents-actually-got-discovering-consents-and-active-agents-in-microsoft-1eeo"&gt;discovering consents&lt;/a&gt;) cover the governance model in detail.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The answer to the question
&lt;/h2&gt;

&lt;p&gt;The fellow at Collab Summit asked how to connect an AI agent to Azure DevOps properly. The answer is not a better secret. It is a better identity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/agent-id-overview" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt; gives the agent a governed, auditable, purpose-built identity. The &lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints" rel="noopener noreferrer"&gt;Entra SDK auth sidecar&lt;/a&gt; keeps credentials out of agent code. The delegated user pattern makes the agent a traceable participant in Azure DevOps, not an anonymous caller hiding behind a PAT.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/astaykov/agentid-azure-devops" rel="noopener noreferrer"&gt;proof of concept&lt;/a&gt; is on GitHub. Clone it, configure the Entra objects, run &lt;code&gt;docker compose up&lt;/code&gt;, and watch an AI agent create issues, push code, and open pull requests, all authenticated through its own agent identity.&lt;/p&gt;

&lt;p&gt;If your AI agents are still running on PATs, the question from Collab Summit applies to you too. And the answer is the same: stop managing secrets. Start managing identities.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/agent-id-overview" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-blueprint" rel="noopener noreferrer"&gt;Agent Identity Blueprints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent Identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Agent ID key concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;Agent ID design patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/endpoints" rel="noopener noreferrer"&gt;Entra SDK Auth Sidecar — Endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/configuration" rel="noopener noreferrer"&gt;Entra SDK Auth Sidecar — Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/msidweb/agent-id-sdk/security" rel="noopener noreferrer"&gt;Entra SDK Auth Sidecar — Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/rest/api/azure/devops/" rel="noopener noreferrer"&gt;Azure DevOps REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate" rel="noopener noreferrer"&gt;Azure DevOps PATs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/library/add-devops-entra-service-connection?view=azure-devops" rel="noopener noreferrer"&gt;Access Azure DevOps with Microsoft Entra workload identity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/oauth2permissiongrant-post?view=graph-rest-1.0" rel="noopener noreferrer"&gt;oauth2PermissionGrant — Microsoft Graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity" rel="noopener noreferrer"&gt;Configure an app to trust a managed identity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling" rel="noopener noreferrer"&gt;Azure OpenAI Function Calling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/astaykov/agentid-azure-devops" rel="noopener noreferrer"&gt;Proof of concept — agentid-azure-devops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://collabsummit.eu/" rel="noopener noreferrer"&gt;Collab Summit 2026&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entraagentid</category>
      <category>azuredevops</category>
      <category>aiagents</category>
      <category>identity</category>
    </item>
    <item>
      <title>Your agentic platform doesn't need a single secret: Managed Identities, Workload Identity Federation and Agent Identity Blueprints</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 28 May 2026 10:17:00 +0000</pubDate>
      <link>https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602</link>
      <guid>https://dev.to/astaykov/your-agentic-platform-doesnt-need-a-single-secret-managed-identities-workload-identity-3602</guid>
      <description>&lt;p&gt;Every enterprise has a drawer full of static API keys it pretends are under control. They are not.&lt;/p&gt;

&lt;p&gt;A static API key is a bearer credential in the purest sense: it does not know who is using it, does not bind to a caller or a session, and does not expire unless someone remembers to set a date. It grants access to anyone who holds it, full stop.&lt;/p&gt;

&lt;p&gt;And keys never stay where they are supposed to. They travel in plaintext emails, get committed to source control in &lt;code&gt;.env&lt;/code&gt; files that &lt;code&gt;.gitignore&lt;/code&gt; was supposed to catch, get pasted into wiki pages and Slack messages during incident response, and settle into CI/CD pipeline definitions that predate the team's current secrets-management policy. None of this is negligence. It is structural: the tooling makes keys trivially easy to mint and almost impossible to track once they leave the admin console. The result is technical security debt that compounds silently. Keys issued for a proof of concept three years ago are still valid, still embedded in a pipeline nobody touches, still granting day-one access. Revoking them is a game of "what breaks?" that nobody volunteers to play.&lt;/p&gt;

&lt;p&gt;This is the foundation too many organizations are building their agentic platforms on. A foundation made of sand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managed Identities for Azure resources: the first answer
&lt;/h2&gt;

&lt;p&gt;If your workload runs on Azure, the answer has existed for years: &lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview" rel="noopener noreferrer"&gt;Managed Identities for Azure resources&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A managed identity is a service principal in Microsoft Entra ID whose credentials are entirely platform-managed. Your code never sees a secret, never stores a certificate, never rotates anything. The compute resource requests a token from the local metadata endpoint; Entra issues one. The credential that backs it is inaccessible to anyone, including the team that deployed the workload.&lt;/p&gt;

&lt;p&gt;Three properties make this the default choice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complete elimination of static credentials.&lt;/strong&gt; No API key to leak, no connection string to embed, no vault entry to maintain. The identity &lt;em&gt;is&lt;/em&gt; the credential. This is not "better secrets management." It is the absence of secrets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic credential lifecycle.&lt;/strong&gt; Rotation, renewal, and revocation happen without human intervention. No calendar reminders, no runbooks, no incidents when someone forgets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle binding (system-assigned).&lt;/strong&gt; When the Azure resource is deleted, the identity is deleted with it. No orphaned service principals, no zombie credentials. &lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview#managed-identity-types" rel="noopener noreferrer"&gt;User-assigned managed identities&lt;/a&gt; offer an independent lifecycle model for workloads that span multiple resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conclusion is not nuanced: if your workload runs on Azure, you should be using a managed identity. Every client secret stored in Key Vault "for safety," every certificate rotated on a quarterly schedule, is a choice to do things the hard way when a safer path is right there. Managed identities are not a best practice. They are the baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload Identity Federation: the cross-cloud, cross-provider standard
&lt;/h2&gt;

&lt;p&gt;Managed identities solve the problem when your workload runs on Azure. But workloads do not stay neatly inside one cloud. A GitHub Actions pipeline needs to deploy to Azure. A service on AWS needs to call Microsoft Graph. A Kubernetes cluster on GKE needs to reach an Azure Storage account.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt; (WIF) enters the picture. WIF is not a Microsoft product. It is a pattern built on open standards: &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt; for identity assertions and &lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523&lt;/a&gt; for the JWT bearer token exchange. Instead of handing a workload a long-lived secret, you let it prove who it is using a signed JWT from an identity provider the receiving system already trusts. GitHub, Google Cloud, AWS, Kubernetes, and SPIFFE/SPIRE all issue OIDC-compliant tokens that participate in WIF flows today.&lt;/p&gt;

&lt;p&gt;Microsoft Entra supports WIF in both directions, and this bidirectional capability is what makes the rest of this story possible:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outbound (Entra as issuer).&lt;/strong&gt; Your tenant issues JWTs that external systems can validate and trust, enabling Entra-authenticated workloads to call services outside the Microsoft ecosystem without static credentials. The &lt;a href="https://dev.to/astaykov/your-ai-agent-doesnt-need-an-api-key-entra-agent-id-and-anthropics-workload-identity-federation-el0"&gt;earlier article in this series&lt;/a&gt; explored exactly this: an AI agent using its Entra-issued JWT to authenticate to Anthropic's Claude API through WIF, with no API key involved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inbound (Entra as relying party).&lt;/strong&gt; Your tenant &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential" rel="noopener noreferrer"&gt;trusts JWTs from external identity providers&lt;/a&gt; and exchanges them for Entra-issued access tokens. This is what lets a GitHub Actions workflow or an AWS Lambda function access Entra-protected resources without a stored secret. The &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation#supported-scenarios" rel="noopener noreferrer"&gt;supported scenarios&lt;/a&gt; span AKS, EKS, GKE, GitHub Actions, Azure DevOps, SPIFFE/SPIRE, and any platform with an OIDC-compliant identity provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Federated Identity Credentials: the inbound trust mechanism
&lt;/h2&gt;

&lt;p&gt;The inbound direction of WIF in Microsoft Entra is implemented through &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Federated Identity Credentials&lt;/a&gt; (FICs). A FIC is a configuration object attached to an &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust" rel="noopener noreferrer"&gt;app registration&lt;/a&gt; or a &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust-user-assigned-managed-identity" rel="noopener noreferrer"&gt;user-assigned managed identity&lt;/a&gt; that tells Entra: "when a JWT arrives from this issuer, with this subject, for this audience, treat it as a valid credential and issue an access token."&lt;/p&gt;

&lt;p&gt;Three properties define the trust: &lt;strong&gt;&lt;code&gt;issuer&lt;/code&gt;&lt;/strong&gt; (the external IdP's URL, matched against &lt;code&gt;iss&lt;/code&gt;), &lt;strong&gt;&lt;code&gt;subject&lt;/code&gt;&lt;/strong&gt; (the external workload's identifier, matched against &lt;code&gt;sub&lt;/code&gt;), and &lt;strong&gt;&lt;code&gt;audiences&lt;/code&gt;&lt;/strong&gt; (typically &lt;code&gt;api://AzureADTokenExchange&lt;/code&gt;). All matching is case-sensitive. When an external workload presents a JWT, Entra validates the signature against the issuer's published OIDC keys, checks the claims, and issues an Entra access token. No secret involved. No certificate involved. The trust is cryptographic and scoped to exactly one external workload.&lt;/p&gt;

&lt;p&gt;The mental model: there must be a workload identity in Entra (app registration or managed identity) that &lt;em&gt;trusts&lt;/em&gt; the external token and represents that workload within the Entra ecosystem. The external workload never gets a secret; it gets a trust relationship. (Design note: a maximum of 20 FICs can be configured per app registration or managed identity.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Managed identity as a federated credential: the elegant twist
&lt;/h2&gt;

&lt;p&gt;Managed identities eliminate secrets on Azure. FICs eliminate secrets for external workloads. But what about scenarios where you need an app registration (because the downstream API requires it, because your token needs specific optional claims) and yet you still want zero secrets?&lt;/p&gt;

&lt;p&gt;The answer: &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity" rel="noopener noreferrer"&gt;configure the app registration to trust a managed identity as its federated credential&lt;/a&gt;. The workload acquires a managed identity token from the local metadata endpoint, presents it to Entra as a FIC on the app registration, and receives an access token scoped to the app. No client secret. No certificate. The managed identity &lt;em&gt;is&lt;/em&gt; the credential. The managed identity handles the credential lifecycle; the app registration handles token shape, optional claims, and API permissions. Each does what it is good at, and no secret sits between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The convergence: Agent Identity Blueprints without secrets
&lt;/h2&gt;

&lt;p&gt;Now bring in &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-blueprint" rel="noopener noreferrer"&gt;Agent Identity Blueprint&lt;/a&gt; is the authentication foundation for one or more agent identities. The blueprint holds the credentials and acquires tokens on behalf of all agent identities created from it. Conditional Access policies applied to the blueprint propagate to every agent identity it parents. It is, by design, the central point where credential management happens for an entire family of AI agents.&lt;/p&gt;

&lt;p&gt;In the simplest deployment model, a blueprint holds a client secret. For production, teams typically upgrade to a certificate. Both are secrets that must be stored, rotated, and protected. Both are liabilities.&lt;/p&gt;

&lt;p&gt;But a blueprint is, at its core, backed by an app registration in Microsoft Entra. And we just established that an app registration can trust a managed identity as its federated credential.&lt;/p&gt;

&lt;p&gt;The implication is direct: configure a managed identity as the federated identity credential on the blueprint's underlying app registration, deploy the agent runtime on Azure compute with that managed identity assigned, and the blueprint acquires tokens using the managed identity token exchange. No client secret. No certificate. No vault. The managed identity, lifecycle-bound to the Azure compute resource, is the only credential in the system.&lt;/p&gt;

&lt;p&gt;Three concepts, designed independently for different purposes, converge into a single architecture: &lt;strong&gt;Managed Identities&lt;/strong&gt; provide the credential-free primitive for Azure compute. &lt;strong&gt;Workload Identity Federation&lt;/strong&gt; extends it across trust boundaries via OIDC and RFC 7523. &lt;strong&gt;Federated Identity Credentials&lt;/strong&gt; bind an external identity (including a managed identity) to an Entra workload identity, replacing secrets with cryptographic trust.&lt;/p&gt;

&lt;p&gt;Applied to an Agent Identity Blueprint, these three deliver a secret-less agentic platform. Not a roadmap item. Not a preview. A production-ready architecture available &lt;em&gt;today&lt;/em&gt;, using generally available constructs in Microsoft Entra ID.&lt;/p&gt;

&lt;p&gt;If you are building an agentic platform in 2026 and your agents still hold secrets, the question is no longer "should we fix this?" The question is "why haven't we?"&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview" rel="noopener noreferrer"&gt;Managed identities for Azure resources overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/federatedidentitycredentials-overview?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Federated Identity Credentials overview (Microsoft Graph)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity" rel="noopener noreferrer"&gt;Configure an app to trust a managed identity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/concept-agent-id-design-patterns" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID design patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-blueprint" rel="noopener noreferrer"&gt;Agent Identity Blueprints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523 — JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microsoftentra</category>
      <category>entraagentid</category>
      <category>aiagents</category>
      <category>wif</category>
    </item>
    <item>
      <title>Finding Out What Your AI Agents Actually Got: Discovering Consents and Active Agents in Microsoft Entra</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Mon, 25 May 2026 12:36:00 +0000</pubDate>
      <link>https://dev.to/astaykov/finding-out-what-your-ai-agents-actually-got-discovering-consents-and-active-agents-in-microsoft-1eeo</link>
      <guid>https://dev.to/astaykov/finding-out-what-your-ai-agents-actually-got-discovering-consents-and-active-agents-in-microsoft-1eeo</guid>
      <description>&lt;p&gt;The first two articles in this series argued that incremental and dynamic consent, paired with Microsoft Entra Agent ID, lets an interactive AI agent earn its access in the wild — and that the resulting accumulation quietly turns the agent into the highest-value target in your environment.&lt;/p&gt;

&lt;p&gt;That argument is only useful if the accumulation is &lt;em&gt;visible&lt;/em&gt;. This article is about that visibility. Specifically, two questions every identity and security architect should be able to answer on demand for any agent in the tenant:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;What has this agent been granted?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What is this agent actually doing with what it was granted?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither answer requires a new product. Both live in surfaces that already exist in Microsoft Entra and Microsoft Graph — they just have not been wired into most operating models yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two halves of the picture
&lt;/h2&gt;

&lt;p&gt;Granted permissions and exercised permissions are different things, and they live in different places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Granted delegated permissions&lt;/strong&gt; — the consents Aria collects every time a user approves a new scope — are stored as &lt;code&gt;oauth2PermissionGrants&lt;/code&gt; in Microsoft Entra and exposed through Microsoft Graph. Each grant is a tuple of &lt;em&gt;(client service principal, resource service principal, principal the consent was granted on behalf of, scope string)&lt;/em&gt;. For an interactive agent, the &lt;em&gt;client&lt;/em&gt; is the agent's identity and the &lt;em&gt;principal&lt;/em&gt; is the human who consented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Granted application permissions&lt;/strong&gt; — used mostly by autonomous agents — are stored as &lt;code&gt;appRoleAssignments&lt;/code&gt; against the agent's identiy. They require admin consent, so they should be a smaller and more deliberate list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exercised permissions&lt;/strong&gt; — what the agent actually used a token against — show up first in &lt;a href="https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-ins" rel="noopener noreferrer"&gt;Microsoft Entra sign-in logs&lt;/a&gt;, specifically the &lt;strong&gt;non-interactive&lt;/strong&gt; stream. Every time an agent requests a token for a resource (Microsoft Graph, SharePoint, a custom API), Entra records a non-interactive sign-in tagged with the agent identity, the &lt;code&gt;appId&lt;/code&gt;, and the &lt;code&gt;resourceId&lt;/code&gt; / &lt;code&gt;resourceDisplayName&lt;/code&gt; of what was accessed. For agent identities provisioned through Microsoft Entra Agent ID, those events also carry an &lt;code&gt;agent&lt;/code&gt; property describing the &lt;code&gt;agentType&lt;/code&gt; and &lt;code&gt;agentSubjectType&lt;/code&gt; — which is what lets you separate agent traffic from the rest of the workload identity noise without guessing.&lt;/p&gt;

&lt;p&gt;For endpoint-level detail — the exact &lt;code&gt;RequestUri&lt;/code&gt;, method, and response code per Microsoft Graph call — &lt;a href="https://learn.microsoft.com/en-us/graph/microsoft-graph-activity-logs-overview" rel="noopener noreferrer"&gt;Microsoft Graph activity logs&lt;/a&gt; are the deeper layer, available once the diagnostic setting that streams them into a Log Analytics workspace is turned on. Sign-in logs answer &lt;em&gt;which resources&lt;/em&gt;; Graph activity logs answer &lt;em&gt;which endpoints on Microsoft Graph&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Holding the granted and exercised sides side by side is the entire game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovering what Aria was granted
&lt;/h2&gt;

&lt;p&gt;For a single agent like Aria, a Microsoft Graph call is enough.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://graph.microsoft.com/v1.0/oauth2PermissionGrants?$filter=clientId eq '{aria-service-principal-id}'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is every delegated consent in force for Aria — the consenting user (&lt;code&gt;principalId&lt;/code&gt;), the resource being accessed (&lt;code&gt;resourceId&lt;/code&gt;, e.g. Microsoft Graph, SharePoint, the Finance API), and the space-separated &lt;code&gt;scope&lt;/code&gt; string. Joining &lt;code&gt;resourceId&lt;/code&gt; against &lt;code&gt;/servicePrincipals&lt;/code&gt; resolves the resource to a name; joining &lt;code&gt;principalId&lt;/code&gt; against &lt;code&gt;/users&lt;/code&gt; resolves the consenter.&lt;/p&gt;

&lt;p&gt;For a tenant-wide view — every agent identity, every grant — drop the filter and group by &lt;code&gt;clientId&lt;/code&gt;. The shape is usually surprising the first time anyone runs it: a long tail of agents with two or three scopes, a smaller set with a dozen, and one or two outliers worth an immediate conversation with their owner.&lt;/p&gt;

&lt;p&gt;Application permissions warrant a parallel sweep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://graph.microsoft.com/v1.0/servicePrincipals/{id}/appRoleAssignments
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an agent identity is meant to be interactive and you find non-trivial app role assignments here, that is a finding by itself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Microsoft Entra defines an Agent Identity as a speciliazed subtype of the service principal type. That's why the above query works directly on the &lt;code&gt;/servicePrincipals&lt;/code&gt; collection. Of course you also use the specialized collection endpoint &lt;code&gt;/servicePrincipals/microosft.graph.agentIdentity/{id}/appRoleAssignments&lt;/code&gt;. With respect to the information we are looking for - the result will be the same.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Identifying the most active agents
&lt;/h2&gt;

&lt;p&gt;Granted scopes describe potential. Sign-in logs describe behavior, and behavior is what matters for triage.&lt;/p&gt;

&lt;p&gt;The single most useful call for tenant-wide agent visibility is a filtered read of the non-interactive &lt;a href="https://learn.microsoft.com/en-us/graph/api/resources/signin?view=graph-rest-beta" rel="noopener noreferrer"&gt;sign-in&lt;/a&gt; stream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://graph.microsoft.com/beta/auditLogs/signIns
    ?$filter=signInEventTypes/any(t:t eq 'nonInteractiveUser') 
        and agent/agentType eq 'agenticAppInstance' 
        and agent/agentSubjectType ne 'agentIDuser'
    &amp;amp;$select=id,createdDateTime,userPrincipalName,appId,appDisplayName,
        resourceId,resourceDisplayName,signInEventTypes,agent
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one query already returns the resources every agent identity in the tenant has touched in the last 24 hours (default), who the agent acted on behalf of, and how often. No diagnostic setting, no Log Analytics workspace, no extra licensing — just Microsoft Graph and the sign-in logs that Entra is already collecting.&lt;/p&gt;

&lt;p&gt;Once those events are routed into Log Analytics (via the Entra diagnostic setting for &lt;code&gt;SignInLogs&lt;/code&gt; / &lt;code&gt;NonInteractiveUserSignInLogs&lt;/code&gt;), ranking the most active agents in the tenant becomes a one-liner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AADNonInteractiveUserSignInLogs
| where TimeGenerated &amp;gt; ago(7d)
| where tostring(parse_json(Agent).agentType) == 'agenticAppInstance'
| summarize Calls = count(),
            DistinctResources = dcount(ResourceDisplayName),
            LastSeen = max(TimeGenerated)
            by AppId, UserPrincipalName
| sort by Calls desc
| take 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single result set answers more operational questions than most identity dashboards: which agents are loud, what resource surface area they cover, and on whose behalf. The &lt;code&gt;AppId&lt;/code&gt; ties each row back to the agent's service principal and its &lt;code&gt;oauth2PermissionGrants&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A second query reveals &lt;em&gt;which resources&lt;/em&gt; Aria is touching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AADNonInteractiveUserSignInLogs
| where TimeGenerated &amp;gt; ago(30d)
| where AppId == '{aria-app-id}'
| summarize Calls = count(),
            LastCall = max(TimeGenerated)
            by ResourceDisplayName, ResourceIdentity, ConditionalAccessStatus
| sort by Calls desc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is Aria's behavioral fingerprint at the resource level: Microsoft Graph, SharePoint, the Finance API, with cadence and conditional-access outcome. Sudden new entries are leading indicators that the next consent prompt is about to fire — or that something has already gone sideways.&lt;/p&gt;

&lt;p&gt;For teams that need endpoint-level detail on Microsoft Graph specifically — exact paths, methods, response codes — &lt;code&gt;MicrosoftGraphActivityLogs&lt;/code&gt; is the natural follow-on, joined back on &lt;code&gt;ServicePrincipalId&lt;/code&gt;. It is a deeper layer, not the entry point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The query that should be on every architect's wall
&lt;/h2&gt;

&lt;p&gt;The most useful query joins the two halves — granted scopes against resources actually touched:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let granted = externaldata(AppId: string, ResourceIdentity: string, Scope: string)
    [/* loaded from your oauth2PermissionGrants snapshot,
         joined to servicePrincipals to resolve resourceId */];
let used = AADNonInteractiveUserSignInLogs
    | where TimeGenerated &amp;gt; ago(30d)
    | where tostring(parse_json(Agent).agentType) == 'agenticAppInstance'
    | distinct AppId, ResourceIdentity;
granted
| join kind=leftouter used on AppId, ResourceIdentity
| where isnull(used_AppId)
| project AppId, GrantedButUnusedResource = ResourceIdentity, Scope
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the &lt;strong&gt;grant-versus-use gap&lt;/strong&gt; introduced in the &lt;a href="https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3"&gt;previous article&lt;/a&gt;, made operational at the resource level. Every row is a resource an agent has consent for but has not touched in 30 days — a candidate for automated revocation, or at minimum a row in next quarter's access review, keyed to actual behavior rather than the calendar. Teams that have Microsoft Graph activity logs flowing can refine the same join down to the individual scope by replacing the &lt;code&gt;used&lt;/code&gt; block with a &lt;code&gt;MicrosoftGraphActivityLogs&lt;/code&gt; projection over &lt;code&gt;Scopes&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building this into an operating rhythm
&lt;/h2&gt;

&lt;p&gt;Three lightweight habits convert these queries from one-off discoveries into a control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A weekly snapshot of &lt;code&gt;oauth2PermissionGrants&lt;/code&gt; per agent identity&lt;/strong&gt;, written to a Log Analytics custom table or a storage account. This produces a time series of consent accumulation — the basis for velocity-based detections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A scheduled top-callers query&lt;/strong&gt; over &lt;code&gt;AADNonInteractiveUserSignInLogs&lt;/code&gt; filtered to &lt;code&gt;agentType == 'agenticAppInstance'&lt;/code&gt;, run daily, feeding a workbook tile and an alert for sudden movers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A monthly grant-versus-use report&lt;/strong&gt; scoped to agent identities and owned by the agent's sponsor (the business-accountable role defined in &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt;). The sponsor either justifies the unused resources and scopes or signs off on revocation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this requires custom code beyond a handful of Microsoft Graph calls and Kusto queries. All of it scales with the agent population, and the entry-point query — non-interactive sign-ins filtered to agentic app instances — works against any tenant with Microsoft Entra Agent ID and the standard sign-in log retention, no extra diagnostic plumbing required.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The queries above are the minimum viable instrumentation. They work, but they are scattered across the Microsoft Graph console, the Log Analytics blade, and whatever notebook the architect happens to keep open.&lt;/p&gt;

&lt;p&gt;The fourth and final article in this series brings them together as a custom &lt;strong&gt;Azure Monitor workbook for AI agent governance&lt;/strong&gt; — agent inventory, consent accumulation, top callers, grant-versus-use gap, sponsor sign-off — designed to drop into an existing Microsoft Entra monitoring solution.&lt;/p&gt;

&lt;p&gt;The visibility problem is solved as soon as someone decides to look. The next article is about making it impossible &lt;em&gt;not&lt;/em&gt; to look.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>entraagentid</category>
      <category>aisecurity</category>
    </item>
    <item>
      <title>Your AI Agent Doesn't Need an API Key: Entra Agent ID and Anthropic's Workload Identity Federation</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Thu, 21 May 2026 10:30:00 +0000</pubDate>
      <link>https://dev.to/astaykov/your-ai-agent-doesnt-need-an-api-key-entra-agent-id-and-anthropics-workload-identity-federation-el0</link>
      <guid>https://dev.to/astaykov/your-ai-agent-doesnt-need-an-api-key-entra-agent-id-and-anthropics-workload-identity-federation-el0</guid>
      <description>&lt;p&gt;Every system that authenticates with a static API key is carrying a liability disguised as a convenience. The key does not expire unless someone sets a calendar reminder. It does not know who is using it. It cannot tell you whether the request that just hit the endpoint came from the production agent it was minted for or from a laptop in a coffee shop where someone pasted it into a terminal two months ago. Static keys are the skeleton key of modern distributed systems — they open the door for anyone who holds them, and they never ask why.&lt;/p&gt;

&lt;p&gt;This is not a new problem, but it is becoming a &lt;em&gt;dangerous&lt;/em&gt; one. As AI agents proliferate across enterprise environments — calling model APIs, orchestrating workflows, accessing downstream services — the number of static secrets embedded in configuration files, environment variables, and CI pipelines is growing faster than any rotation policy can keep up with. The question is no longer whether your organization has a leaked key somewhere. The question is how many, and which ones an attacker has already found.&lt;/p&gt;

&lt;p&gt;The industry's answer has been converging for years, and it has a name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload Identity Federation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation&lt;/a&gt; (WIF) is a pattern — not a product, not a proprietary protocol — built on top of &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt; and the &lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523 JWT bearer grant&lt;/a&gt;. The idea is disarmingly simple: instead of minting a long-lived secret and handing it to a workload, you let the workload &lt;em&gt;prove who it is&lt;/em&gt; using a short-lived, signed JSON Web Token issued by an identity provider (IdP) you already trust. The receiving system validates the JWT's signature against the IdP's published keys, checks the claims against rules you configured, and — if everything lines up — issues a short-lived access token in return. No secrets to store. No secrets to rotate. No secrets to leak.&lt;/p&gt;

&lt;p&gt;The pattern has been adopted across the industry — by major cloud providers, CI/CD platforms, container orchestrators, and increasingly by model providers. Microsoft Entra, for its part, supports WIF both as an &lt;em&gt;issuer&lt;/em&gt; (your Entra tenant issues JWTs that external systems trust) and as a &lt;em&gt;relying party&lt;/em&gt; (your Entra tenant &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation#how-it-works" rel="noopener noreferrer"&gt;trusts JWTs from external identity providers&lt;/a&gt; to grant access to Entra-protected resources). That bidirectional capability is what makes the rest of this story possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anthropic embraces the standard
&lt;/h2&gt;

&lt;p&gt;Anthropic has brought native Workload Identity Federation support to the Claude API — and this deserves more attention than it has received.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://platform.claude.com/docs/en/manage-claude/workload-identity-federation" rel="noopener noreferrer"&gt;Anthropic's WIF implementation&lt;/a&gt;, any OIDC-capable identity provider can authenticate workloads to the Claude API without a static &lt;code&gt;sk-ant-...&lt;/code&gt; key ever being involved. You register your IdP as a &lt;em&gt;federation issuer&lt;/em&gt; in the Anthropic Console, define a &lt;em&gt;federation rule&lt;/em&gt; that maps incoming JWT claims to a &lt;em&gt;service account&lt;/em&gt;, and your workload does the rest: present the JWT, receive a short-lived Claude access token, call the API. The SDKs handle the exchange and the refresh loop. API keys can be disabled entirely on the Anthropic workspace.&lt;/p&gt;

&lt;p&gt;Three concepts on the Anthropic side matter here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service accounts&lt;/strong&gt; (&lt;code&gt;svac_...&lt;/code&gt;) — non-human identities inside your Anthropic organization. A federated token acts &lt;em&gt;as&lt;/em&gt; a service account. Unlike an API key, a service account has credentials minted for it on demand, and you can audit which workloads acted as which service account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Federation issuers&lt;/strong&gt; (&lt;code&gt;fdis_...&lt;/code&gt;) — the registration of your OIDC identity provider with your Anthropic organization. Each issuer tells Anthropic "JWTs signed by this provider may assert workload identity for my org."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Federation rules&lt;/strong&gt; (&lt;code&gt;fdrl_...&lt;/code&gt;) — the bridge between an issuer and a service account: "when a JWT from issuer X has claims that look like Y, mint a token for service account Z."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Console includes presets for common providers and a generic OIDC option that works with any standards-compliant issuer — including Microsoft Entra ID. That last bullet is the one this article cares about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft Entra Agent ID — identity built for agents
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt; introduces first-class identity constructs purpose-built for AI agents. Not repurposed service principals. Not human user accounts pressed into service. Dedicated objects with a dedicated governance model.&lt;/p&gt;

&lt;p&gt;The constructs that matter for this story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-blueprint" rel="noopener noreferrer"&gt;Agent identity blueprints&lt;/a&gt;&lt;/strong&gt; — the template and authentication foundation for one or more agent identities. The blueprint holds the credentials (client secret, certificate, or federated identity credential) and uses them to acquire tokens on behalf of all agent identities created from it. Conditional Access policies applied to a blueprint propagate to every agent identity it parents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent identities&lt;/a&gt;&lt;/strong&gt; — the runtime identity of a specific AI agent. An agent identity has no credentials of its own. It authenticates through its blueprint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authentication-with-auth-sdk-sidecar" rel="noopener noreferrer"&gt;Microsoft Entra SDK for Agent ID&lt;/a&gt;&lt;/strong&gt; — a containerized sidecar that handles token acquisition, validation, and secure downstream API calls. Your agent code asks the sidecar for a token; the sidecar handles the identity plumbing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The proof of concept
&lt;/h2&gt;

&lt;p&gt;The question I wanted to answer was concrete: &lt;em&gt;can an AI agent, using Microsoft Entra Agent ID as its native identity, call the Anthropic Claude API through Workload Identity Federation — with no API key, no certificate in agent memory, and no cloud LLM proxy in between?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer is yes. I built a &lt;a href="https://github.com/astaykov/claude-wif-agentid" rel="noopener noreferrer"&gt;proof of concept&lt;/a&gt; that does exactly this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The architecture
&lt;/h3&gt;

&lt;p&gt;The PoC runs as two containers on a Docker bridge network:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;claude-wif-agent&lt;/code&gt;&lt;/strong&gt; — a Flask application that receives user queries, asks the sidecar for an Entra JWT, exchanges that JWT for a Claude access token, and calls the Claude Messages API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;claude-wif-sidecar&lt;/code&gt;&lt;/strong&gt; — the &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authentication-with-auth-sdk-sidecar" rel="noopener noreferrer"&gt;Microsoft Entra Auth SDK sidecar&lt;/a&gt;, which handles the client-credentials flow against Entra ID and returns a signed JWT scoped to the agent identity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The token flow has nine steps, but the critical insight lives in three of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Steps 4–5:&lt;/strong&gt; The sidecar uses the blueprint's credentials to obtain an Entra-issued JWT &lt;em&gt;for the agent identity&lt;/em&gt;. The JWT carries the agent's &lt;code&gt;appid&lt;/code&gt;, &lt;code&gt;oid&lt;/code&gt;, and — crucially — the &lt;code&gt;xms_par_app_azp&lt;/code&gt; &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-token-claims" rel="noopener noreferrer"&gt;optional claim&lt;/a&gt; that identifies the parent blueprint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 7:&lt;/strong&gt; The agent application exchanges that Entra JWT for a Claude access token by posting to &lt;code&gt;POST https://api.anthropic.com/v1/oauth/token&lt;/code&gt; using the &lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523 jwt-bearer grant&lt;/a&gt;. Anthropic validates the JWT's signature, checks the claims against the federation rule, and returns a short-lived token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 9:&lt;/strong&gt; The agent calls &lt;code&gt;POST https://api.anthropic.com/v1/messages&lt;/code&gt; with the Claude token. No API key is involved. No MSAL library is needed. No certificates sit in agent memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Three Entra objects — and why the third is easy to miss
&lt;/h3&gt;

&lt;p&gt;The PoC requires three distinct Microsoft Entra objects. Two of them sound similar and the third is implicit in WIF mechanics — which is exactly why it trips people up.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An Agent Identity Blueprint&lt;/strong&gt; — holds the credentials (client secret for local dev; managed identity or federated identity credential in production) and parents the agent identity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An Agent Identity&lt;/strong&gt; — the runtime identity of the specific AI agent. No credentials of its own — the blueprint mints tokens on its behalf.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;An App Registration representing the Anthropic API&lt;/strong&gt; — and this is the one that is easy to miss. Anthropic's WIF rule validates an Entra-issued JWT, and the only way that JWT carries the right &lt;code&gt;aud&lt;/code&gt; (audience) claim is if Entra issues it &lt;em&gt;for a registered resource application&lt;/em&gt; whose ID matches what you configured as the audience of the federation issuer in the Anthropic Console. This app registration uses v2.0 tokens (&lt;code&gt;requestedAccessTokenVersion: 2&lt;/code&gt;), has &lt;code&gt;acceptMappedClaims: true&lt;/code&gt;, and configures the &lt;code&gt;xms_par_app_azp&lt;/code&gt; optional claim on the access token — so that a single Anthropic federation rule can match &lt;em&gt;all&lt;/em&gt; agent identities parented by the same blueprint, rather than requiring a rule per individual agent.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The token that reaches Anthropic carries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;iss&lt;/code&gt; = &lt;code&gt;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aud&lt;/code&gt; = the Application ID of the Anthropic API app registration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;appid&lt;/code&gt; = the Agent Identity's client ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oid&lt;/code&gt; = the Agent Identity's object ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xms_par_app_azp&lt;/code&gt; = the Agent Identity Blueprint's application ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anthropic validates all of this against the federation issuer and rule you configured. No Anthropic API key is involved at any point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Both flows work
&lt;/h3&gt;

&lt;p&gt;The PoC supports both access patterns that the agent identity platform defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous (app-only):&lt;/strong&gt; The agent identity acts independently. The sidecar obtains a client-credentials token, the agent exchanges it for a Claude token, and the response comes back tagged &lt;code&gt;"flow": "autonomous"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-Behalf-Of (OBO):&lt;/strong&gt; When a signed-in user's Entra Bearer token is available, the sidecar performs an OBO exchange and mints an agent-on-behalf-of-user token, which is then exchanged with Anthropic WIF. The response comes back tagged &lt;code&gt;"flow": "obo"&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both flows use the same Anthropic WIF endpoint. The only difference is &lt;em&gt;whose authority&lt;/em&gt; the Entra JWT represents — the agent's own, or the agent acting on behalf of a human.&lt;/p&gt;

&lt;h3&gt;
  
  
  From local dev to production
&lt;/h3&gt;

&lt;p&gt;The PoC uses a client secret on the blueprint for local development — a pragmatic shortcut for proving the concept. Moving to production requires changing exactly two environment variables in the sidecar configuration to switch from client secret to managed identity, and adding a &lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;federated identity credential&lt;/a&gt; on the blueprint for the managed identity. No agent code changes. The sidecar abstracts the credential source entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The credentials-free agent
&lt;/h2&gt;

&lt;p&gt;This is where the threads converge. Workload Identity Federation is an industry standard. Anthropic has built native support for it into the Claude API. Microsoft Entra Agent ID provides purpose-built identity constructs for AI agents — with blueprints that centralize credential management, agent identities that carry no secrets of their own, and a sidecar SDK that abstracts the entire token lifecycle.&lt;/p&gt;

&lt;p&gt;Put them together and you get something that would have been difficult to describe two years ago: an AI agent that authenticates to a third-party model provider using &lt;em&gt;its own agentic identity&lt;/em&gt;, issued by the enterprise identity provider, validated through standards-based federation — with no static API key, no certificate in memory, and no cloud LLM proxy sitting in between. The agent's identity &lt;em&gt;is&lt;/em&gt; its credential.&lt;/p&gt;

&lt;p&gt;The proof of concept is open on GitHub: &lt;strong&gt;&lt;a href="https://github.com/astaykov/claude-wif-agentid" rel="noopener noreferrer"&gt;astaykov/claude-wif-agentid&lt;/a&gt;&lt;/strong&gt;. It is minimal by design — a Flask app, a sidecar, a &lt;code&gt;docker-compose.yml&lt;/code&gt;, and a &lt;code&gt;.env&lt;/code&gt; file. The README walks through every Entra object, every Anthropic Console configuration step, and the full token flow. Fork it, break it, extend it.&lt;/p&gt;

&lt;p&gt;The age of the static API key — for AI agents, at least — is ending. The identity infrastructure to replace it is already here.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation" rel="noopener noreferrer"&gt;Workload Identity Federation (Microsoft Entra)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/manage-claude/workload-identity-federation" rel="noopener noreferrer"&gt;Anthropic Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID Key Concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-blueprint" rel="noopener noreferrer"&gt;Agent Identity Blueprints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-identities" rel="noopener noreferrer"&gt;Agent Identities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/authentication-with-auth-sdk-sidecar" rel="noopener noreferrer"&gt;Microsoft Entra Auth SDK Sidecar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/agent-token-claims" rel="noopener noreferrer"&gt;Agent Token Claims Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc7523" rel="noopener noreferrer"&gt;RFC 7523 — JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/astaykov/claude-wif-agentid" rel="noopener noreferrer"&gt;Proof of Concept: astaykov/claude-wif-agentid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>entra</category>
      <category>aiagents</category>
      <category>anthropic</category>
      <category>security</category>
    </item>
    <item>
      <title>Your Agent Is Becoming the Crown Jewel: SOC, Reviews, and Governance for the Dynamic-Consent Era</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Mon, 18 May 2026 13:34:00 +0000</pubDate>
      <link>https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3</link>
      <guid>https://dev.to/astaykov/your-agent-is-becoming-the-crown-jewel-soc-reviews-and-governance-for-the-dynamic-consent-era-23l3</guid>
      <description>&lt;p&gt;The &lt;a href="https://dev.to/astaykov/the-overlooked-gem-in-microsoft-entra-that-gives-your-ai-agents-super-powers-3mde"&gt;previous article&lt;/a&gt; in this series argued that the combination of &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer#incremental-and-dynamic-user-consent" rel="noopener noreferrer"&gt;incremental and dynamic user consent&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt; gives interactive AI agents something genuinely new: the ability to &lt;em&gt;earn&lt;/em&gt; their access in the wild, scope by scope, prompted by the humans and other agents they work alongside. Aria, the example agent, started with two delegated permissions and grew into a productive contributor across SharePoint, ServiceNow, and the Finance API in roughly a quarter — without its creators pre-declaring any of it.&lt;/p&gt;

&lt;p&gt;That was the optimistic half. This is the other half.&lt;/p&gt;

&lt;p&gt;By the end of that quarter, Aria is — by any reasonable measurement — the most over-privileged identity in the tenant. No one noticed, because there was nothing to notice. Every grant was legitimate, contextual, and user-approved. The risk did not arrive in a single bad decision. It arrived as a hundred reasonable yeses.&lt;/p&gt;

&lt;h2&gt;
  
  
  A different kind of over-permissioning
&lt;/h2&gt;

&lt;p&gt;Classic over-permissioning is an event. Someone hands a service account &lt;code&gt;Directory.ReadWrite.All&lt;/code&gt; because the deployment was due Friday, an auditor flags it months later, a ticket is opened. Slow, but the control loop exists, and it is built around discrete moments of poor judgment.&lt;/p&gt;

&lt;p&gt;Permission accumulation through dynamic consent is structurally different. There is no single bad decision to find. The permission graph grows monotonically — one narrow, well-justified scope at a time — because the mechanism that makes the agent useful is the same mechanism that makes it dangerous. Nothing in the platform prunes that graph by default, and nothing in most organizations does either: access-review tooling was designed around human role changes, not around agents whose role &lt;em&gt;is&lt;/em&gt; to absorb new capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents become the target
&lt;/h2&gt;

&lt;p&gt;A compromised agent identity is qualitatively worse than a compromised user account, and the reasons are worth stating plainly.&lt;/p&gt;

&lt;p&gt;A user holds permissions scattered across teams, sick days, role changes, and eventual departures. Their access constantly churns, and the blast radius of any single compromise is naturally bounded by the messiness of human work.&lt;/p&gt;

&lt;p&gt;An agent does none of that. It persists. It centralizes. Every scope a hundred different users granted to it is reachable through one set of tokens, one blueprint, one set of credentials issued by that blueprint. Add the realistic threat surface of a modern agent — token theft, blueprint compromise, prompt injection used as a lateral-movement primitive — and the picture becomes uncomfortable: the most attractive principal in the tenant is also the one whose authority grew quietly enough to escape notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the SOC must change
&lt;/h2&gt;

&lt;p&gt;Most security operations centers treat sign-in logs as the primary identity signal. For agents under dynamic consent, that is no longer sufficient. &lt;strong&gt;The consent log itself becomes a first-class detection surface.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three signal families deserve attention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scope-acquisition velocity.&lt;/strong&gt; A productive agent acquires new scopes in bursts that follow human work. An agent that suddenly requests broad scopes — especially ones approaching admin-consent thresholds — outside its normal pattern is worth waking someone up for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grant-versus-use gap.&lt;/strong&gt; Scopes that were granted but are never exercised are liability at best, pre-positioned capability for an attacker at worst. Track them, and feed the gap into automated revocation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introduction chains.&lt;/strong&gt; When agent A pulls agent B into a workflow and B requests new scopes as a result, that chain is part of the audit story. SOC tooling needs to render it as a graph, not as isolated events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are exotic. They are sign-in analytics one layer up the stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What in identity governance must change
&lt;/h2&gt;

&lt;p&gt;Access reviews built for humans assume a relatively stable role. The reviewer is asked, in effect, "does this person still need what they had last quarter?" That question does not work for an agent whose entire purpose is to absorb new capabilities continuously.&lt;/p&gt;

&lt;p&gt;Three adjustments are required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reviews keyed to recent use, not recent grant.&lt;/strong&gt; The relevant question is no longer "should the agent have this scope?" but "did the agent actually exercise this scope in the last &lt;em&gt;N&lt;/em&gt; days, and was the use consistent with the original justification?" Scopes that fail both halves of that test should expire automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Owners and sponsors as the accountable humans.&lt;/strong&gt; Microsoft Entra Agent ID separates technical owners from business sponsors precisely so that someone with operational context and someone with business context can both be on the hook. Wire those roles into the review workflow. An agent without a current sponsor should not be holding sensitive delegated permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blueprint-level Conditional Access as the choke point.&lt;/strong&gt; Because policies applied to a blueprint propagate to every agent identity created from it, the blueprint is the right place to enforce the constraints that should never be negotiable — geographic boundaries, sensitive-resource exclusions, step-up requirements for specific scope families. Treat the blueprint the way you treat a privileged-access workstation: small, hardened, watched.&lt;/p&gt;

&lt;h2&gt;
  
  
  A governance posture that grows with the agent
&lt;/h2&gt;

&lt;p&gt;Three principles are worth taking back to the architecture board.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consent is telemetry.&lt;/strong&gt; Treat every dynamic consent event as a security signal of equal weight to a sign-in. Pipe it into the same analytics and the same review workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Least privilege is a verb, not a noun.&lt;/strong&gt; A static least-privilege list cannot survive contact with an agent that earns its access. The control objective is no longer to &lt;em&gt;define&lt;/em&gt; the minimum scope set — it is to &lt;em&gt;continuously prune&lt;/em&gt; toward it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grow with the agent; do not be the hurdle.&lt;/strong&gt; The organizations that succeed will be the ones whose governance moves at the same cadence as the agent's learning. Quarterly reviews and annual recertifications were already too slow for humans. They are unworkable for agents.&lt;/p&gt;

&lt;p&gt;Aria is going to keep growing. So will every other interactive agent in the tenant. The question for identity and security architects is not whether to allow it — that decision has already been made by the people on the other side of the chat window. The question is whether the controls, the detections, and the operating model are ready for what dynamic consent has quietly enabled.&lt;/p&gt;

&lt;p&gt;If they are not yet, that is the work for this year.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>entraagentid</category>
      <category>security</category>
    </item>
    <item>
      <title>The Overlooked Gem in Microsoft Entra That Gives Your AI Agents Super-Powers</title>
      <dc:creator>Anton Staykov</dc:creator>
      <pubDate>Sat, 09 May 2026 13:41:16 +0000</pubDate>
      <link>https://dev.to/astaykov/the-overlooked-gem-in-microsoft-entra-that-gives-your-ai-agents-super-powers-3mde</link>
      <guid>https://dev.to/astaykov/the-overlooked-gem-in-microsoft-entra-that-gives-your-ai-agents-super-powers-3mde</guid>
      <description>&lt;p&gt;Most enterprise conversations about Microsoft Entra stop at the obvious: single sign-on, multifactor authentication, Conditional Access, a handful of well-policed app registrations. Useful, mature, well understood. And almost entirely beside the point for what is about to happen inside every large organization.&lt;/p&gt;

&lt;p&gt;Buried in the Microsoft identity platform developer documentation sits one short section that quietly rewires what an AI agent can become inside an enterprise. It is not a new product. It does not have a launch event. It is a paragraph titled &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer#incremental-and-dynamic-user-consent" rel="noopener noreferrer"&gt;Incremental and dynamic user consent&lt;/a&gt;, and it is the most underrated capability in the Microsoft Entra surface area for the agentic era.&lt;/p&gt;

&lt;h2&gt;
  
  
  A 30-second consent primer
&lt;/h2&gt;

&lt;p&gt;Microsoft Entra exposes three broad consent shapes, all covered in the same &lt;a href="https://learn.microsoft.com/en-us/entra/identity-platform/consent-types-developer" rel="noopener noreferrer"&gt;developer consent guide&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static consent.&lt;/strong&gt; Every permission an application could ever need is declared up front in the app registration. Tidy, predictable, and brittle — users and admins are asked to approve the entire future of the app on day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin consent.&lt;/strong&gt; A tenant administrator approves a permission set on behalf of the whole organization. Necessary for application permissions and for sensitive delegated scopes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental and dynamic user consent.&lt;/strong&gt; The application requests a minimal set of scopes at first and then asks for additional delegated permissions over time, exactly when a feature needs them, by including the new scopes in the &lt;code&gt;scope&lt;/code&gt; parameter of an authorization request. The user approves in context. Crucially, this mechanism applies to &lt;strong&gt;delegated permissions&lt;/strong&gt; — permissions exercised on behalf of a signed-in human.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last constraint is normally treated as a footnote. For AI agents it is the entire story.&lt;/p&gt;

&lt;h2&gt;
  
  
  From applications to agents
&lt;/h2&gt;

&lt;p&gt;The other piece of the puzzle is &lt;a href="https://learn.microsoft.com/en-us/entra/agent-id/key-concepts" rel="noopener noreferrer"&gt;Microsoft Entra Agent ID&lt;/a&gt;. Microsoft Entra Agent ID introduces first-class identity constructs for AI agents — agent identities, agent identity blueprints, owners, sponsors, managers — and a purpose-built &lt;strong&gt;agent's user account&lt;/strong&gt;: an optional user object paired 1:1 with an agent identity, for systems (Exchange Online mailboxes, Teams channels, and similar) that require a user principal. A deliberate construct, so agents can participate in user-shaped systems without being misrepresented as humans.&lt;/p&gt;

&lt;p&gt;On top of that, the industry designs AI Agents with two main data access patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous access&lt;/strong&gt; act as themselves, with their own authorizations on workloads, with or without human in the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive access&lt;/strong&gt; sign a user in, and act on that user's behalf through a chat-style interface, using &lt;strong&gt;delegated permissions&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read those points together. Interactive agents live on delegated permissions. Dynamic consent is the only Microsoft Entra mechanism that lets delegated permissions grow organically after deployment. The two features were designed for different reasons, in different teams, at different times. They meet exactly where the modern AI agent operates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Aria
&lt;/h2&gt;

&lt;p&gt;Consider Aria, an internal productivity agent built on Microsoft Entra Agent ID, deployed as an interactive agent behind a corporate chat surface. Aria's blueprint ships with the bare minimum: &lt;code&gt;User.Read&lt;/code&gt; and &lt;code&gt;offline_access&lt;/code&gt;. On day one, Aria literally knows nothing about the tenant's systems and can do nothing inside them.&lt;/p&gt;

&lt;p&gt;Then work happens.&lt;/p&gt;

&lt;p&gt;In the first week, a product manager asks Aria to summarize discussions on a SharePoint site. Aria does not have that scope. Instead of failing, the runtime issues an authorization request that includes &lt;code&gt;Sites.Read.All&lt;/code&gt;. The user sees a contextual consent prompt, approves it in the moment, and Aria returns the summary.&lt;/p&gt;

&lt;p&gt;In the third week, a separate workflow agent invites Aria into a ticket-triage loop and points it at a ServiceNow connector. Aria requests a narrow read scope on incidents. The on-call engineer approves.&lt;/p&gt;

&lt;p&gt;In the sixth week, a finance analyst asks Aria to reconcile an invoice against a vendor record. Aria requests a tightly scoped read permission on the Finance API. The analyst approves.&lt;/p&gt;

&lt;p&gt;At no point did Aria's creators have to sit down and pre-declare Aria's world. Aria grew because &lt;strong&gt;humans and other agents pulled it into their work&lt;/strong&gt;, and Microsoft Entra provided the mechanism for each step of that growth to be explicit, recorded, and reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model shift
&lt;/h2&gt;

&lt;p&gt;Static permissions are a job description written before a new hire arrives. Whoever writes the description has to imagine every meeting that hire will ever attend, every system they will ever touch, every workflow they will ever join. The description is always wrong, usually in both directions: too narrow to be useful and, in places, too broad to be safe.&lt;/p&gt;

&lt;p&gt;Dynamic consent is a different metaphor entirely. The agent earns scope by being useful. Three discovery vectors drive the growth:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Human delegation in context.&lt;/strong&gt; A user asks the agent to do something it cannot yet do. The consent prompt becomes part of the act of asking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-agent introduction.&lt;/strong&gt; Other agents — orchestrators, copilots, MCP-style tool surfaces — pull the agent into flows it did not know existed and surface the resources it needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-discovery.&lt;/strong&gt; The agent encounters a tool catalog or an API description and recognizes a capability worth requesting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not exotic. It is exactly how a competent new hire becomes productive in a complex organization: they are introduced to systems, granted access on demand, and gradually accumulate the reach required to contribute. Microsoft Entra is finally letting our non-human colleagues follow the same path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cliffhanger
&lt;/h2&gt;

&lt;p&gt;After one quarter, Aria looks very different from the agent that booted with two scopes. It holds dozens of delegated permissions across half the enterprise — every single one of them granted legitimately, in context, by a real human, against a recorded purpose.&lt;/p&gt;

&lt;p&gt;That is the fascinating half. The other half is that Aria is now, by any reasonable measure, the most over-privileged identity in the tenant — and nobody noticed, because there was no single moment of bad judgment to notice. Just a hundred reasonable yeses.&lt;/p&gt;

&lt;p&gt;That accumulation is the crown jewel an adversary will eventually come looking for. It is also the thing your access reviews, your SOC playbooks, and your governance model were not designed to see.&lt;/p&gt;

&lt;p&gt;That is the subject of the next article in this series: what changes in detection, in identity governance, and in the operating model of the security organization once dynamic consent meets agent identity at scale.&lt;/p&gt;

&lt;p&gt;The gem is real. So is the bill that comes with it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>entraagentid</category>
      <category>agents</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
