<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Microsoft - Medium]]></title>
        <description><![CDATA[Any language. Any platform. Our team is focused on making the world more amazing for developers and IT operations communities with the best that Microsoft Azure can provide. If you want to contribute in this journey with us, contact us at medium@microsoft.com - Medium]]></description>
        <link>https://medium.com/microsoft?source=rss----8bec1183ada9---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Microsoft - Medium</title>
            <link>https://medium.com/microsoft?source=rss----8bec1183ada9---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 20:36:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/microsoft" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Agent Governance on Microsoft Foundry, Part I: Which identity makes the call?]]></title>
            <link>https://medium.com/microsoft/agent-governance-on-microsoft-foundry-part-i-which-identity-makes-the-call-0a49f3f12e1f?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/0a49f3f12e1f</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[microsoft-foundry]]></category>
            <category><![CDATA[azure]]></category>
            <dc:creator><![CDATA[Ozgur Guler]]></dc:creator>
            <pubDate>Thu, 05 Nov 2026 18:01:06 GMT</pubDate>
            <atom:updated>2026-11-03T05:59:13.047Z</atom:updated>
            <content:encoded><![CDATA[<h4>Entra identities, managed identities, and one Search experiment</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J-6EI7Nll_Owgh0TQ5kZwQ.png" /></figure><p>Azure is a platform of many services that call one another, plus your own apps and microservices that call those services and each other. The number of caller-to-target pairs is enormous, so the platform cannot solve access one pair at a time. It solves it with two shared systems, and keeping them apart is most of what this series is about.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VCYoN0eT-GxRws8v9MP7bQ.png" /></figure><p><strong>Entra</strong> is the single identity authority. Every caller — a person, one of your apps, or an Azure service with a managed identity switched on — is a <em>security principal</em> in the tenant, and proves who it is by obtaining a short-lived signed token from Entra for a specific target. Managed identities are how Azure workloads get a principal without you ever handling a credential; app registrations are how your own apps and APIs get one, and how an API declares the permissions it understands. <strong>Azure RBAC</strong> is the single authorisation store for Azure resources, kept by Azure Resource Manager: rows of <em>principal + role + scope</em>, where a role is a set of actions and the scope is where it applies. Every request carries the token as a bearer header. The target verifies it offline with Entra’s public key, reads the caller’s object id from it, and only then decides. For an Azure resource that means consulting the role assignments Resource Manager holds — each service keeps a synced copy of the assignments that apply to it, which is why a new assignment can take a few minutes to take effect — and checking whether any of them grants this id the requested action at this scope. For your own API it means reading the roles or scopes Entra placed inside the token, because Resource Manager knows nothing about your API. Authentication is the token; authorisation is what the target does with it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*xphVKwJLcNKhFbOPt-T18w.png" /></figure><p>A Foundry agent adds nothing to this machinery. It adds <em>callers</em> — several of them, with different names and different defaults — and the governance question is simply which one makes which call. That sounds obvious until you try it. I ran one small experiment — a 124-page IMF <em>World Economic Outlook</em> embedded with text-embedding-3-small and loaded into Azure AI Search as 315 chunks — and a single agent question crossed two authentication boundaries, each authorised by a different identity, neither of them the one the portal puts in front of you. This post names them.</p><h4>The vocabulary, in one pass</h4><p>Six terms carry the whole series. If these are already second nature, skip to the next section.</p><p>First, the mechanism underneath all of them. Entra does exactly one thing: a caller proves who it is, Entra issues a signed token naming that caller and the intended target, the target validates the token and then decides — from its own role assignments — whether the operation is allowed. <strong>Authentication is the token; authorisation is what the target does with it.</strong> <strong>Managed identity is not an exception to this.</strong> When the Foundry project MI “calls” AI Search, the Azure fabric, which holds the identity’s certificate, performs a client-credentials request to Entra on its behalf, gets an ordinary access token, and Search sees nothing more than a bearer token with an oid it checks against RBAC. Keys, passwords and certificates differ only in how the caller proves itself; what crosses the wire to the target is always the same kind of token, which is why one set of checks covers the whole series.</p><ul><li><strong>Tenant</strong> — your organisation’s Entra directory. Every identity below lives in it, and every token names it (the tid claim, and the tenant GUID inside the issuer URL).</li><li><strong>App registration</strong> — the <em>definition</em> of an application or API: its client id (appId), the scopes and app roles it exposes, and its Application ID URI, which becomes the aud in tokens sent to it. A registration cannot sign in or be granted anything; it is a blueprint, not an actor. One per client that requests tokens or resource that receives them.</li><li><strong>Service principal</strong> — the registration’s presence in a tenant: the <em>actor</em>. Role assignments, consent grants and the oid you see in tokens and sign-in logs all attach here, never to the registration. Keep the two ids apart: appId is for the protocol, the principal&#39;s object id is for authorisation and audit.</li><li><strong>Managed identity</strong> — a service principal Azure creates for a resource and whose credential Azure holds and rotates, so your code never sees a secret. <em>System-assigned</em> lives and dies with one resource; <em>user-assigned</em> is standalone and can be attached to many. A managed identity can call things but cannot <em>be</em> called: there is no registration behind it, so it exposes no scopes and is never an aud.</li><li><strong>Roles</strong> — two different systems that are easy to conflate. <em>Azure RBAC</em> authorises operations on Azure resources: principal + role + scope (Search Index Data Contributor on a search service). <em>Entra app roles and scopes</em> authorise calls to <em>your</em> APIs and arrive inside the token as roles (application) or scp (delegated, a user is present). Part I is all Azure RBAC; Parts IV–V are app roles and scopes.</li><li><strong>Token</strong> — a signed JWT from Entra. A receiving API checks signature, issuer, audience and lifetime, then reads oid (who), azp (through which app) and roles/scp (allowed to do what). A valid signature alone proves nothing about <em>you</em>: a Graph token is signed by the same keys as a token for your MCP server, which is why audience is the check that matters. Full validation walkthrough in Part IV.</li></ul><p>With that: everything below is just “which service principal presented the token, and which role assignment let the call succeed”.</p><h4>What Azure RBAC actually is</h4><p>It is tempting to summarise Azure RBAC as “give each app a managed identity and add roles to it”. The second half is right; the first half is narrower than the real thing, and the gap is where misconfigurations hide.</p><p>Azure RBAC is a single store of <strong>role assignments</strong>. Each assignment is one row with three independent parts, and Azure Resource Manager (or the data-plane service itself) evaluates those rows on every request:</p><ul><li><strong>Principal</strong> — <em>any</em> Entra security principal: a user, a group, a service principal you registered, or a managed identity. A managed identity is just the most convenient principal for a workload, because Azure holds its credential. Your own az login identity being Owner on the subscription is RBAC too — it is what let me create the index.</li><li><strong>Role definition</strong> — a named set of allowed operations, split into control-plane Actions (manage the resource: Microsoft.Search/searchServices/write) and data-plane DataActions (use the resource: Microsoft.Search/searchServices/indexes/documents/read). Owner, Contributor and Reader are control-plane only — which is exactly why being Owner gave my CLI identity no right to query the index, and ingestion fell back to an admin key.</li><li><strong>Scope</strong> — where the row applies, inheriting <em>downward</em> only: management group → subscription → resource group → resource. One assignment at subscription level reaches every search service in it; one at the search service reaches nothing else.</li></ul><p>So the complete sentence is: <em>RBAC assigns roles to principals at scopes; managed identities are the principals you use when the caller is a workload rather than a person.</em> Two consequences follow for agents:</p><ul><li>The managed identity is not “the app having RBAC”. It is only the <em>principal</em> column of the row. You could give the same app a registration with a client secret and assign the identical roles to that service principal — same authorisation, worse credential hygiene. Managed identity changes how the caller authenticates, never what it is authorised to do.</li><li>RBAC governs calls to <strong>Azure resources</strong> — Search, Storage, Key Vault, the Foundry project itself. It does not govern calls to <em>your own</em> API. When the agent calls the tickets MCP server in Part IV, the permission check happens inside that server, against app roles and scopes carried in the token (roles, scp); Azure Resource Manager is not consulted at all. &quot;Where is this permission checked — in ARM or in the token?&quot; is the question that separates the two systems, and every hop in this series has exactly one answer to it.</li></ul><p><strong>Rule of thumb.</strong> Use a managed identity for every Azure-to-Azure hop; it is the default, not an optimisation. Reach for something else only when a managed identity is impossible, and name which case you are in: the caller runs outside Azure (a CI runner, a laptop) → an app registration with a <em>federated</em> credential, still no secret; the target does not trust your Entra tenant (GitHub, a SaaS API) → that target’s own OAuth or key; a person must be represented in the call → a delegated token, never an app identity; the service simply does not accept Entra on the tier you are on (the Free-tier vectorizer here) → a key, in Key Vault, with an expiry and a ticket to remove it. Everything in this series that is not a managed identity is one of those four exceptions, and a production review should be able to point at which.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XPUVA8kZ7lAMcqsZAXfjFg.png" /></figure><h4>Three principals, not one</h4><p>Microsoft Entra ID authenticates an identity and issues a token; the target service authorises the operation. For Azure resources that authorisation is an RBAC assignment: <em>principal + role + scope</em>. So the only question that matters at each hop is <strong>who is the principal</strong>.</p><p>A Foundry deployment has three of its own, and they are separate objects in your tenant:</p><p><strong>1. Foundry resource managed identity</strong> — system-assigned, on the parent Foundry resource.</p><ul><li><em>Authenticates:</em> resource-level operations, e.g. reading a customer-managed key in Key Vault.</li><li><em>Roles go on:</em> the resource it needs (Key Vault).</li></ul><p><strong>2. Foundry project managed identity</strong> — system-assigned, on the project.</p><ul><li><em>Authenticates:</em> project connections set to use it — including the built-in Search, file-search and code-interpreter tools, which the Foundry runtime executes on the project’s behalf.</li><li><em>Roles go on:</em> the target service (here: AI Search).</li></ul><p><strong>3. Entra agent identity</strong> — a service principal Foundry provisions under an <em>agent identity blueprint</em>. In the current agent object model every new agent gets its own blueprint and a dedicated default identity at creation; only legacy agents share the project-level identity.</p><ul><li><em>Authenticates:</em> tools reached through a project connection whose <em>authentication type</em> is agent identity. The tool kind alone does not decide this: an MCP or A2A connection can use it, while an OpenAPI connection set to managed identity runs as the project MI. Never the built-in tools.</li><li><em>Roles go on:</em> whatever those tools call.</li></ul><p>Two consequences follow. Nesting is not inheritance: a role on the resource MI grants nothing to the project MI or the agent identity, and one project’s roles do not reach another project. And the agent identity, although it exists the moment you create an agent, is <strong>not the caller</strong> for a built-in Search tool — so putting Search roles on it would leave the real caller unauthorised.</p><p><em>(Microsoft’s Search-tool article mixes “account” and “project” MI wording; this post reports the connection as inspected — ProjectManagedIdentity — and the roles verified.)</em></p><h4>The experiment: two calls, two authorisation decisions</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SHlnYofqcAXmtJinLfQM2g.png" /><figcaption>Four principals, two boundaries. Solid arrows are calls with the role granted; the agent identity is present but makes no call in this experiment.</figcaption></figure><pre>Agent invokes the built-in Search tool<br>      │  caller: project MI<br>      │  grant:  Search Service Contributor + Search Index Data Contributor<br>      │          on the search service<br>      ▼<br>Azure AI Search index  (vectorizer on the vector profile)<br>      │  caller: Free tier      → apiKey of the Foundry resource (no role)<br>      │          Basic and above → the search service&#39;s system-assigned MI<br>      │  grant:  Cognitive Services OpenAI User on the Foundry resource,<br>      │          assigned to that search service MI<br>      ▼<br>Foundry embedding deployment (text-embedding-3-small)<br>      └─ query vector back to Search → matching chunks back to the agent</pre><p><strong>Boundary 1 — Foundry → Search.</strong> I added the index as a tool through a project connection and chose managed-identity authentication. That choice selects the <em>credential Foundry presents</em>; it grants nothing. On the search service’s IAM page the only principal with roles was me.</p><p>With <strong>Search Index Data Reader</strong> alone the tool failed. Reader permits the document query, so the failure most likely came from the connector’s service-level calls — listing indexes or reading the index definition — which need <strong>Search Service Contributor</strong>; I did not isolate the exact operation, so treat that as the probable cause rather than an observed one. The documented pair for the built-in integration is Search Service Contributor + <strong>Search Index Data Contributor</strong>, scoped to the search service, assigned to the project MI. That worked. Note that Data Contributor also permits writes and deletes: for a read-only agent this is broader than least privilege, and it is the kind of grant a production review flags.</p><p>What ended up assigned, for the record:</p><ul><li><strong>Principal:</strong> the Foundry <em>project</em> managed identity (not the resource MI, not the agent identity).</li><li><strong>Roles:</strong> <strong>Search Service Contributor</strong> (control plane: list indexes, read index definitions — presumably what the connector needs to discover the index) and <strong>Search Index Data Contributor</strong> (data plane: query the documents; also write/delete, which is the over-grant noted above).</li><li><strong>Scope:</strong> the Azure AI Search service resource.</li><li><strong>Not assigned:</strong> anything on the agent identity, and nothing to the project MI beyond these two.</li></ul><p>The rule: identify the caller, then assign the role.</p><h4>On the wire: a managed-identity call is still a bearer token</h4><p>It is worth being exact about what “the project MI calls Search” means, because the phrase hides the mechanism and the mechanism is where things break.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LLAHHCQMQPjK4Pzfshwo5g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KvL6xzLsPtU2dS5mPNBbUw.png" /></figure><ol><li><strong>Token request, locally.</strong> The Foundry runtime asks the managed-identity endpoint (on your own compute this is the Instance Metadata Service at 169.254.169.254, reachable only from inside the resource) for a token with resource=https://search.azure.com.</li><li><strong>Client-credentials grant, by Azure.</strong> The Managed Identity resource provider holds the identity’s certificate — the credential you never see and never rotate — and uses it to authenticate to Entra on the identity’s behalf. Entra returns an ordinary JWT access token.</li><li><strong>The HTTP request.</strong> Search receives a plain REST call. The only thing managed identity changed is one header: Authorization: Bearer &lt;JWT&gt; where a key-based connection would have sent api-key.</li></ol><pre>POST https://my-search.search.windows.net/indexes/weo-index/docs/search?api-version=2024-07-01<br>Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ij...<br>Content-Type: application/json<br>{ &quot;search&quot;: &quot;IMF global growth projection 2026&quot;,<br>  &quot;vectorQueries&quot;: [{ &quot;kind&quot;: &quot;text&quot;, &quot;text&quot;: &quot;IMF global growth projection 2026&quot;,<br>                      &quot;fields&quot;: &quot;contentVector&quot;, &quot;k&quot;: 10 }],<br>  &quot;top&quot;: 10 }<br>----- decoded payload of that bearer token -----<br>{ &quot;aud&quot;:   &quot;https://search.azure.com&quot;,<br>  &quot;iss&quot;:   &quot;https://sts.windows.net/&lt;tenant id&gt;/&quot;,<br>  &quot;oid&quot;:   &quot;&lt;object id of the project MI&#39;s service principal&gt;&quot;,<br>  &quot;appid&quot;: &quot;&lt;client id of the project MI&gt;&quot;,<br>  &quot;idtyp&quot;: &quot;app&quot;,<br>  &quot;exp&quot;:   1762188700 }          &lt;- no roles, no scp: identity only</pre><p>Search then does two things, in order, and the status code tells you which one failed:</p><ul><li><strong>Validate the token — 401 if this fails.</strong> Read kid from the header, verify the RS256 signature against Entra&#39;s published public keys (cached JWKS), check the issuer is a trusted tenant, check aud is <em>its own</em> audience https://search.azure.com — a perfectly valid Storage token presented here fails right there — and check nbf/exp. No call to Entra is needed; the signature is enough. Search now believes &quot;the caller is oid X&quot;.</li><li><strong>Authorise the action — 403 if this fails.</strong> Map the request to a data action (Microsoft.Search/searchServices/indexes/documents/read), then evaluate Azure RBAC for that oid: walk the scope chain from the service up through resource group, subscription and management group, collect role assignments and deny assignments, and check whether any effective DataActions − NotDataActions includes the action. Match → run the query → 200.</li></ul><p>Notice what a role assignment therefore is and is not. It is one row in Azure Resource Manager — <em>principal, role, scope</em> — read by the target service at request time. It is <strong>not</strong> something that enables token issuance: Entra will mint the project MI a Search token whether it holds a role on any search service or not, and the token looks identical either way. Managed identity automates proving <em>who</em> is calling (the principal, the certificate, the token request, rotation, cleanup). It never automates deciding <em>what</em> that principal may do; that stays an explicit assignment, and the default is nothing — a project’s MI holds no rights on its own parent resource, let alone on your search service, until someone writes the row.</p><p>Two things this picture is <em>not</em> saying, because both are common misreadings. First, requests do not go through Entra, and not every request gets a new token. Entra is out of band: the caller talks to it once to obtain a token, caches it for its lifetime (about an hour), and only returns when it is near expiry. A thousand searches in an hour are one trip to Entra and a thousand trips to Search, and an unexpired token keeps working if Entra is unreachable. Search does not call Entra either — it checks the signature with Entra’s public key, which it already holds. Entra issues; it does not sit in the path.</p><pre>Conversation 1 — obtain a credential (rare; cached ~1 h)<br>  caller ──► Entra   &quot;token for https://search.azure.com; proof: MI certificate&quot;<br>  Entra  ──► caller  JWT<br>Conversation 2 — do the work (every request; direct)<br>  caller ──► Search  POST /indexes/weo-index/docs/search<br>                     Authorization: Bearer &lt;that JWT&gt;<br>  Search ──► caller  200 results  |  401 (not my token)  |  403 (no permission)</pre><p>Second, nothing is “wrapped” in the token. The request is an ordinary HTTPS call and the token is one header on it, next to Content-Type. TLS protects it in transit — which is the only reason a bearer credential is acceptable: whoever holds it can use it, so it must never travel in the clear, never be logged, and must expire quickly. The working analogy is a passport: issued by one authority, checked at many borders, none of which telephone the issuing office.</p><p><strong>Boundary 2 — Search → the embedding model.</strong> The stored vectors were already there; to vectorise the <em>query</em> at search time, the index needs an Azure OpenAI <strong>vectorizer</strong> on the vector field’s profile. The vectorizer is itself a caller and needs a credential:</p><ul><li><strong>Free tier</strong> — credential: apiKey, the key of the Foundry / Azure OpenAI resource hosting the embedding deployment (a secret stored in the index definition; remove in production). Role needed: none.</li><li><strong>Basic and above</strong> — credential: the search service’s own managed identity; omit apiKey, use authIdentity only to pin a user-assigned MI. Role needed: <strong>Cognitive Services OpenAI User</strong> on the Foundry resource.</li></ul><p>The vectorSearch block I used (portal: Search service → Indexes → index → Edit JSON; replace only this section):</p><pre>{<br>  &quot;vectorSearch&quot;: {<br>    &quot;algorithms&quot;: [{ &quot;name&quot;: &quot;weo-exhaustive&quot;, &quot;kind&quot;: &quot;exhaustiveKnn&quot;,<br>                     &quot;exhaustiveKnnParameters&quot;: { &quot;metric&quot;: &quot;cosine&quot; } }],<br>    &quot;profiles&quot;:   [{ &quot;name&quot;: &quot;weo-vector-profile&quot;, &quot;algorithm&quot;: &quot;weo-exhaustive&quot;,<br>                     &quot;vectorizer&quot;: &quot;weo-query-vectorizer&quot; }],<br>    &quot;vectorizers&quot;: [{<br>      &quot;name&quot;: &quot;weo-query-vectorizer&quot;, &quot;kind&quot;: &quot;azureOpenAI&quot;,<br>      &quot;azureOpenAIParameters&quot;: {<br>        &quot;resourceUri&quot;: &quot;https://YOUR-RESOURCE.openai.azure.com&quot;,<br>        &quot;deploymentId&quot;: &quot;text-embedding-3-small&quot;,<br>        &quot;modelName&quot;: &quot;text-embedding-3-small&quot;,<br>        &quot;apiKey&quot;: &quot;&lt;FOUNDRY_RESOURCE_KEY&gt;&quot;<br>      }<br>    }],<br>    &quot;compressions&quot;: []<br>  }<br>}</pre><p>On Basic and above the direction of the grant flips. The caller is now the <em>search service’s</em> system-assigned managed identity (turn it on under the search service’s Identity blade first), and the role — <strong>Cognitive Services OpenAI User</strong> — is assigned on the <em>Foundry resource</em> with that search MI as the assignee. Boundary 1 put a Search role on a Foundry identity; Boundary 2 puts a Foundry role on the Search identity. Same rule both times: find the caller, then grant on the target.</p><p>Query and document embeddings must share model and dimensions; a caller that already supplies a numeric vector bypasses the vectorizer entirely.</p><h4>Make retrieval explicit, then prove it</h4><p>Adding a vectorizer does not change how the agent searches. I set the tool to</p><pre>&quot;query_type&quot;: &quot;vector_simple_hybrid&quot;,<br>&quot;top_k&quot;: 10</pre><p>— keyword plus vector, up to ten <em>chunks</em> per query, no semantic reranking.</p><p>Evidence that retrieval happened: the agent’s full API response contained a completed azure_ai_search_call with its returned content. A fluent answer or a citation is not evidence. Traces are the second source — look for the azure_ai_search_call span and its connection — though in this run the traces UI lagged Application Insights, which already held the spans.</p><p>Observations worth keeping:</p><ul><li>Citation URLs point at the Search endpoint, not PDF pages.</li><li>My az login was a <em>fourth</em> principal: being Owner conferred no data-plane rights, and ingestion fell back to a Search admin key after a 403. Another credential to retire.</li><li>Setup: 7.88 MiB of the Free tier’s 50 MiB, one of three index slots, 1,536-dimensional vectors, text only (no chart interpretation). Search stayed within quota; embedding calls were billed separately.</li></ul><h4>What a reviewer would say</h4><p>We can name caller, credential, target and permission at each boundary — the project MI for the Search connection, the vectorizer’s key for the model call — and the agent identity, although present, called nothing. A production review would flag two items in this setup: Data Contributor on a read-only path, and a key inside the vectorizer. Both are fixable; both are invisible if you only check that the agent answers.</p><h4>Where the series goes next</h4><p>Each part adds exactly one mechanism, so tool approval, OAuth consent, downstream authorisation and inbound authorisation stay distinguishable.</p><ul><li><strong>Part II — public MCP and the approval loop.</strong> Microsoft Learn MCP, no auth, require_approval on; inspect the approval request; then allowed_tools = [&quot;microsoft_docs_search&quot;]. Lesson: approving a tool call is an execution decision, not an identity grant.</li><li><strong>Part III — third-party OAuth and consent.</strong> The managed GitHub MCP connector; the consent-required flow; whose account grants access and which scopes.</li><li><strong>Part IV — our own MCP API and the agent identity.</strong> A small tickets API with get_ticket, an Entra app registration with app role Tickets.Read; validate signature, issuer, audience, lifetime, then enforce the role. Denied before the grant (403 — valid token, missing permission; 401 is a different failure), successful after assignment to the <em>actual</em> agent principal. Then a second, separately tested hop: the MCP server reads the ticket from Azure Storage using its <em>own</em> managed identity (Storage Blob Data Reader on the container). Two grants, two principals — the agent identity is authorised to <em>invoke</em> get_ticket; the server MI is authorised to <em>read the data</em>. Neither grant implies the other.</li><li><strong>Part V — agent-only versus user-delegated.</strong> A user-entra-token connection to the same API; compare the validated claims side by side — oid, aud, roles versus scp. Record selected claims, never raw bearer tokens.</li><li><strong>Part VI — an application calls the agent.</strong> The inbound side, untested so far: a small client app hosted in Azure invokes the Foundry agent with its own managed identity, and we separate the application’s permission to <em>call the agent</em> (an Azure AI User-class role on the Foundry project) from the agent’s permission to <em>call its tools</em>. Run it twice, with a system-assigned and then a user-assigned MI, to show what changes in the token and in the role assignments when the identity outlives the app.</li></ul><p>Taken together the series then covers the four places an identity appears: the application calling in, the Foundry runtime identities, the agent identity calling out, and the tool server’s onward identity. The resource MI’s Key Vault role stays explanatory unless an encryption lab becomes worth its own part.</p><p>The evidence pattern throughout: a denied call, the precise grant that changes the outcome, the successful call, and the token that proves who made it.</p><h4>References</h4><ul><li><a href="https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/tools/ai-search">Connect an Azure AI Search index to Foundry agents</a> — Microsoft Foundry docs; names Search Index Data Contributor + Search Service Contributor on the search service for keyless connections (and mixes account/project MI wording).</li><li><a href="https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/agent-identity">Agent identity blueprints: dedicated vs. shared identities and supported tools</a> — Microsoft Foundry docs.</li><li><a href="https://learn.microsoft.com/entra/agent-id/agent-blueprint">Agent identity blueprint</a> — Microsoft Entra Agent ID docs.</li><li><a href="https://learn.microsoft.com/en-us/azure/search/search-security-rbac">Connect to Azure AI Search using roles</a> — what Search Service Contributor and Search Index Data Contributor each permit.</li><li><a href="https://learn.microsoft.com/en-us/azure/search/vector-search-vectorizer-azure-open-ai">Azure OpenAI vectorizer</a> — Azure AI Search docs; apiKey vs authIdentity, and &quot;requires only the Cognitive Services OpenAI User role on the target resource&quot;.</li><li><a href="https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference">Access token claims reference</a> — Microsoft identity platform (aud, oid, scp, roles).</li><li><a href="https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-managed-identities-work-vm">How managed identities for Azure resources work with Azure virtual machines</a> — Microsoft Entra docs; the IMDS endpoint, the certificate issued by the Managed Identity resource provider, and “Microsoft Entra ID returns a JSON Web Token (JWT) access token”.</li><li><a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/overview#how-azure-rbac-determines-if-a-user-has-access-to-a-resource">How Azure RBAC determines if a user has access to a resource</a> — Azure RBAC overview; the evaluation order including deny assignments and DataActions − NotDataActions.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0a49f3f12e1f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/agent-governance-on-microsoft-foundry-part-i-which-identity-makes-the-call-0a49f3f12e1f">Agent Governance on Microsoft Foundry, Part I: Which identity makes the call?</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Plan Your Java Upgrade Ahead of Microsoft’s 2027 Support Milestones]]></title>
            <link>https://medium.com/microsoft/plan-your-java-upgrade-ahead-of-microsofts-2027-support-milestones-081754e5ba23?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/081754e5ba23</guid>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[github-copilot]]></category>
            <category><![CDATA[application-modernization]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Bruno Borges]]></dc:creator>
            <pubDate>Thu, 29 Oct 2026 14:33:06 GMT</pubDate>
            <atom:updated>2026-10-29T14:33:07.732Z</atom:updated>
            <content:encoded><![CDATA[<p>In April 2021, we <a href="https://devblogs.microsoft.com/java/announcing-preview-of-microsoft-build-of-openjdk/">announced the Microsoft Build of OpenJDK</a>. We were starting with a preview of our Java 11 builds for Windows, Linux, and macOS. Today, if you’re planning a Java upgrade on Azure, you have several generations of that work to build on: 11, 17, 21, and now 25.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6M4Lu2R6D2YV8bCA" /></figure><p>Java 8 has been part of that work too, with help from the fine folks at the Eclipse Foundation’s <a href="https://adoptium.net/">Eclipse Adoptium project</a>. Microsoft has supported Java 8 on Azure using their Eclipse Temurin builds of OpenJDK 8, rather than producing a Microsoft Build of OpenJDK 8. That collaboration goes back to the launch: our first Java 11 builds also used Adoptium’s build scripts and quality assurance suite.</p><h3>Supporting those releases has meant years of updates. Now it’s time to plan the next move.</h3><p>The <a href="https://learn.microsoft.com/en-us/java/openjdk/support">Microsoft Build of OpenJDK support roadmap</a> lists September 2027 as the <strong>earliest end-of-support date for Java 11 and 17</strong>. That’s a distribution-wide milestone: if you use those Microsoft builds on a VM, in a container, on Kubernetes, on Azure platform services, or evem outside Azure, it belongs in your upgrade planning. These dates are initial targets, and Microsoft may extend them. However, consider taking action now.</p><p>Azure services also have their own deadlines. <a href="https://azure.microsoft.com/en-us/updates/?id=568585">Azure App Service and Azure Functions support for Java 8, 11, and 17 ends on September 1, 2027</a>. Existing applications will continue to run, but the affected Java versions will no longer receive security updates, improvements, or related customer support.</p><p>If that’s your application, the practical question is how to get to Java 21 or 25 while keeping it working. <a href="https://learn.microsoft.com/en-us/azure/developer/github-copilot-app-modernization/">GitHub Copilot modernization</a> gives you a tool: the <strong>Modernize CLI</strong>, which can assess your code, help plan the upgrade, and actually perform and apply changes for you to review.</p><p><a href="https://devblogs.microsoft.com/java/plan-your-java-upgrade-ahead-of-microsofts-2027-support-milestones/"><strong>Continue reading on the Java at Microsoft blog…</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=081754e5ba23" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/plan-your-java-upgrade-ahead-of-microsofts-2027-support-milestones-081754e5ba23">Plan Your Java Upgrade Ahead of Microsoft’s 2027 Support Milestones</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bullet proof security-driven BFF architecture in Azure]]></title>
            <link>https://medium.com/microsoft/bullet-proof-security-driven-bff-architecture-in-azure-5f6cbe591594?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/5f6cbe591594</guid>
            <dc:creator><![CDATA[Stephane Eyskens]]></dc:creator>
            <pubDate>Mon, 26 Oct 2026 19:13:58 GMT</pubDate>
            <atom:updated>2026-10-27T15:24:48.874Z</atom:updated>
            <content:encoded><![CDATA[<p>I was recently discussing about the Backend for Frontend (BFF) pattern and was surprised by the widespread misunderstanding of what it actually is and the problems it is designed to solve. Even more interestingly, although a BFF can significantly strengthen the security of modern web and mobile applications, it often raises concerns among security teams. This post aims to demystify the BFF pattern, explain its security benefits, and explore its implementation in Azure, highlighting the different considerations depending on the Azure service used to host it.</p><p>I briefly introduced this pattern in my book, <em>The Azure Cloud Native Architecture Mapbook </em><a href="https://www.amazon.com/Azure-Cloud-Native-Architecture-Mapbook/dp/1805805053">https://www.amazon.com/Azure-Cloud-Native-Architecture-Mapbook/dp/1805805053</a>, but I would have explored it in much greater depth had I realized how many misconceptions surround it.</p><h4>Introducing the BFF in a nutshell</h4><p>Initially, the sole purpose of a BFF was to be a tailor-made backend for a specific type of frontend, such as a mobile app or a single page application. The BFF is the bridge between the frontend and the actual backends.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*wZ_we7Fxe1O7zQBXeV0WQw.png" /><figcaption>BFF illustration</figcaption></figure><p>The frontend only talks to the BFF, which in turn routes traffic to the actual backends. The BFF can be more than a mere router when using GraphQL to strongly decouple frontend and backend development teams. On the technical level, this also helps prevent underfetching and overfecthing as well as gaining flexibility for frontend queries. While GraphQL is optional, it often plays well with the BFF pattern.</p><h4>How the BFF turned into a security thing</h4><p>A few years after having become more popular, the BFF made its way into the OAuth world and is even proposed in this OAuth RFC candidate <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps">https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps</a></p><p>When used as a security pattern, the BFF becomes the actual OAuth client instead of the frontend application. As a result, no OAuth tokens, whether ID tokens, access tokens, or refresh tokens, are ever exposed to the client (e.g. browser).</p><p>This is a significant security improvement because browser-based applications are inherently more vulnerable to client-side attacks, such as Cross-Site Scripting (XSS), which can allow malicious actors to steal tokens and impersonate users.</p><p>With the BFF pattern, the frontend never handles OAuth tokens directly. Instead, the browser exchanges only a secure, HTTP-only, Secure and SameSite-protected session cookie with the backend. Because this cookie is inaccessible to client-side JavaScript, the attack surface is significantly reduced.</p><p>Before diving into the security controls you can enforce for the BFF pattern, let’s look at the actual authentication and authorization flows, taking Entra ID as the identity provider and authorization server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dhCwPuX9tZx-Yfmf4tvopQ.png" /><figcaption>End to end authentication and authorization using BFF and API-led connectivity (open in a new tab to see it well)</figcaption></figure><p>Here is a direct pointer to the diagram <a href="https://raw.githubusercontent.com/stephaneey/azure-and-k8s-architecture/refs/heads/main/networking/bff/bff-flows.png">https://raw.githubusercontent.com/stephaneey/azure-and-k8s-architecture/refs/heads/main/networking/bff/bff-flows.png</a></p><p>In the example above, I’m using Entra ID but the same concepts apply to most providers. I’m also using API-led connectivity to illustrate that a given token should not traverse API layers, but new tokens should be requested instead for each layer and each API of that layer. Here is a step by step recap:</p><p><strong>1</strong> <strong>Login</strong>: the client connects to the BFF, which returns a 302 redirect.</p><p><strong>2</strong> <strong>User authentication</strong>: the BFF redirects the browser to Entra ID; the user signs in using the BFF client app (authorization code flow + PKCE).</p><p><strong>3</strong> <strong>Callback</strong>: Entra redirects to the BFF redirect URI with the authorization code. The BFF redeems it with its confidential credential for ID + refresh + access tokens and stores them in the token cache store (e.g Redis).</p><p><strong>4</strong> <strong>Session cookie</strong>: the BFF returns a <strong>HttpOnly; Secure; SameSite</strong> cookie. It is only a session handle: no token ever reaches the browser.</p><p><strong>5</strong> <strong>API calls</strong>: the SPA calls /bff/&lt;app-specific-bff&gt; with the cookie only; the BFF looks up the user’s tokens in the cache.</p><p><strong>6 / 6&#39;</strong> <strong>Token request</strong>: the BFF uses the refresh token to get an access token for Experience/Process API 1 / 2 — one token per audience, cached until it expires.</p><p><strong>7 / 7&#39;</strong> <strong>Routed call</strong>: the BFF forwards the call to Experience/Process API 1 / 2 with the matching bearer token. Resource servers (target APIs) validate issuer, audience and scopes.</p><p><strong>8</strong> <strong>On-behalf-of</strong>: Experience/Process API 1 request a new access token targeting a sytem API, using the incoming user token. OBO is only used when the System API needs the user context.</p><p><strong>8&#39;</strong> <strong>Client credentials</strong>: when user context is not needed at system API level, an app-only token is obtained using the Client Credentials Grant Flow (CCF).</p><p><strong>9 / 9&#39;</strong> <strong>System API call</strong>: Experience/Process APIs calls System APIs with a bearer token, obtained with OBO or CCF, issued for that System API’s audience.</p><p>So the key takeaways of the above diagram are:</p><ul><li>The BFF is a security component. It acts as the OAuth client and manages tokens for all sessions. You would typically choose a distributed token cache store such as Redis to avoid sticky sessions and make sure cache can scale. Only use in-memory cache store for testing or demo purposes.</li><li>Make sure to request resource-specific tokens and that a given token type doesn’t traverse the different API layers no even the different APIs belonging to the same layer.</li><li>Because the BFF handles the authentication, it must accept anonymous requests, or at least, clients will perform unauthenticated calls for the very first access. This alone, is often a friction point with security teams and we’ll see how to overcome and mitigate this problem in thef following sections.</li></ul><h4>Hosting the BFF in Azure</h4><p>The BFF can be hosted in many different ways:</p><ul><li>As an Azure App Service or an Azure Function</li><li>On a container orchestration platform such as Azure Kubernetes Service, Azure Container Apps or Azure Red Hat OpenShift</li></ul><p>The number of security controls you can enforce depends on the Azure service you choose. Container orchestrators let you use Service Meshes, App Service’s Easy Auth feature may play a role as well. There are multiple infrastructure-level features that help you ensure secure access to the BFF.</p><p>As you’ve probably gathered by now, the BFF must be accessible from the frontend. Consequently, for an internet-facing application, the BFF itself must also be internet-facing.</p><p>To be precise: the BFF itself doesn’t need to behind a public IP but it must be placed behind a Web Application Firewall (WAF) and/or an API gateway since the client (mobile or browser) must be able to access it. This is only true for internet facing applications of course. This consideration doesn’t apply for internal corporate apps whether mobile or SPAs, where clients are connected to the internal network perimeter. I’m only considering internet facing apps here, which is where you might have endless discussions with security teams. The below diagram shows a high-level flow for an internet facing BFF.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L-eQm7ENbrsXMHjKvJ4_qA.png" /><figcaption>BFF always behind a WAF, optionally behind an API gateway (open in a new tab to see it well)</figcaption></figure><p>Here is a direct pointer to the diagram <a href="https://raw.githubusercontent.com/stephaneey/azure-and-k8s-architecture/refs/heads/main/networking/bff/bff-with-or-without-gtw.png">https://raw.githubusercontent.com/stephaneey/azure-and-k8s-architecture/refs/heads/main/networking/bff/bff-with-or-without-gtw.png</a></p><p>Because of the internet facing nature of the BFF, using a Web Application Firewall (WAF) is a non-negotiable fact. Then comes the interesting piece, should you use an API gateway or not in front of your BFF?</p><p>As a reminder, the BFF isn’t a regular API and must support anonymous calls from the client, or at least, the client should be able to reach out anonymously, which isn’t exactly the same thing.</p><p>Additionally, even once the user signed in and the first tokens were acquired by the BFF, they will never be sent back to the browser. This means that the gateway will never be able to validate any token since there is none. The only thing the gateway will see is a session cookie, which cannot really by validated by typical API gateway policies.</p><p>However, many security teams cannot leave with the fact that a component that is not in the public edge would receive anonymous calls. This gives them cold sweats and since we love them, we want to preserve their health!</p><p>This is where the gateway can play an interesting role. Find below an overall security-driven architecture for a bullet proof BFF.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3mz2aEZVdQ6TQzg5d6sjJw.png" /><figcaption>Bullet proof BFF security-driven architecture (open in a new tab to see it well)</figcaption></figure><p>Here is a direct pointer to the diagram <a href="https://raw.githubusercontent.com/stephaneey/azure-and-k8s-architecture/refs/heads/main/networking/bff/bullet-proof-bff.png">https://raw.githubusercontent.com/stephaneey/azure-and-k8s-architecture/refs/heads/main/networking/bff/bullet-proof-bff.png</a></p><p>In the above diagram, I implement the following security controls:</p><ol><li>Azure Front Door Premium is the only internet-facing endpoint: WAF L7 inspection, DDoS protection, IP / geo restrictions and custom WAF rules. Front Door is the public edge, noting else has an inbound public IP. Additionally, beyond security, Front Door helps build resilient multi-region architectures.</li><li>Front Door reaches the App Gateway over Private Link only. Private listener only. Private Link connection approved for this Front Door profile only.</li><li>IDPS with Azure Firewall Premium. Additional traffic inspection is questionable for this flow but limited IDPS is still possible without inspection.</li><li>APIM inbound policy on every /bff/&lt;app-bff&gt; endpoint: <br>(a) authenticates with its system-assigned managed identity (token for a backend app registered in Entra with the Gateway.Invoke role.<br>(b) sends the token in a dedicated header for the BFF (X-Gateway-Authorization), validated by Istio on the BFF istio-proxy sidecar (I’ll come back to this) but key fact: traffic is authenticated before it reaches the BFF, exactly what security teams want.</li><li>NSG inbound rules restrict access to APIM and other internal callers if any.</li><li>AKS internal load balancer which doesn’t enforce any security control but is on the data path.</li><li><strong>/7&#39;.</strong> NSG inbound rules restricted to the Azure load balancer and K8s system ports, limiting node-level lateral movement. The ingress controller as well as the BFF pod live in an ingress node pool and dedicated ingress subnet to not mix such pods with the real backend ones.</li><li>Istio Gateway API; mTLS between ingress controller and istio-proxy sidecar.</li><li>istio-proxy validates the gateway token and forwards traffic to the BFF container, which ensure no unauthenticated calls can ever reach the BFF container, regardless of the BFF code.</li><li>BFF handles the authentication bits, token cache, etc.</li><li><strong>/11&#39;</strong> Calico or Cilium network policies control traffic across namespaces, with global deny policies by default.</li><li>Istio validates fine-grained authorizations between BFF and backend pods; also performs OAuth token validation.</li><li>NSG inbound rules restrict access to the user node pool subnet.</li><li>Workload identity used to authenticate against Azure services</li><li>PaaS services all behind private endpoints, public access denied</li><li>Image pull restricted to ACR and MCR</li><li>Key Vault CSI driver</li><li>Data encrypted at rest</li><li><strong>/ 19&#39; / 19&#39;’</strong> Cluster internet-bound traffic routed to the egress hub Azure Firewall. You might enable TLS inspection depending on the use case.</li><li>Log retention 90 days; monitoring alerts with Azure Monitor and Container Insights.</li></ol><p>So, I hope you noticed by yourself but I will still help you in case you didn’t! With the above setup, the traffic never reaches the BFF anonymously, although it is required by the client. So, what we do is this:</p><p>Browser ─▶ WAF ─▶ APIM (Anonymous) ─▶ APIM Authentication (gateway token) ─▶ Istio Token Validation (sidecar) ─▶ BFF container ─▶ Response</p><p>So, regardless of the BFF’s code quality, the surrounding components will ensure nothing can reach the BFF pod unauthenticated.</p><p>Worth to mention that cluster-wide settings for both Istio and Calico/Cilium are enforced to make sure everything is denied by default. You would also apply Azure Policy for Kubernetes to make sure deployments are fully hardened.</p><p>If you deal with a multi-tenant AKS cluster, you would keep the same approach but isolate workloads into their own dedicated landing zone.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WKkeaixVeV9rqhCowgxd_g.png" /><figcaption>AKS landing zones for muti-tenant clusters</figcaption></figure><p>The biggest change compared to the previous setup is the introduction of a dedicated Istio ingress and egress gateway per landing zone. In the previous setup (dedicated cluster), a single ingress controller is enough. No egress controller is required since you know the cluster belongs to a single application, which makes it easy to identity the caller for downstream services. In a multi-tenant cluster, having a dedicated egress controller with a static IP per landing zone is required to restrict access to downstream services, from the downstream services perspective.</p><h4>Key takeaways</h4><ul><li>The single entry point for internet traffic is Front Door, nothing else.</li><li>You can leverage APIM to expose an anonymous front leg to the client (the browser) but have an authenticated back leg to the BFF.</li><li>You can leverage infrastructure-level technologies such as Istio to validate tokens issued by APIM and ensure that only authorized traffic originating from APIM can reach the BFF.</li><li>Note that this architecture is primarily security-driven and comes with its own trade-offs. The multiple network hops required to reach the BFF pod introduce additional latency and processing overhead, potentially impacting performance and scalability. Make sure to evaluate these trade-offs against your key architectural drivers and non-functional requirements.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5f6cbe591594" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/bullet-proof-security-driven-bff-architecture-in-azure-5f6cbe591594">Bullet proof security-driven BFF architecture in Azure</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Explore deeper connections with unbounded variable-length paths in Microsoft Fabric Graph]]></title>
            <link>https://medium.com/microsoft/explore-deeper-connections-with-unbounded-variable-length-paths-in-microsoft-fabric-graph-c0e3e120e3e4?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/c0e3e120e3e4</guid>
            <category><![CDATA[microsoft-fabric]]></category>
            <dc:creator><![CDATA[Tushar Goyal]]></dc:creator>
            <pubDate>Wed, 14 Oct 2026 19:01:23 GMT</pubDate>
            <atom:updated>2026-10-14T19:01:22.337Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GDXHusIq_92ZdbPm0eGS-g.png" /></figure><p><a href="https://www.linkedin.com/in/justinpfine/">Justin Fine</a>, <a href="https://www.linkedin.com/in/idrismotiwala/">Idris Motiwala</a>, <a href="https://www.linkedin.com/in/goyal-tushar/">Tushar Goyal</a></p><p><strong>Microsoft Fabric Graph removes the previous hop limit for variable-length traversals, allowing graph structure to determine path depth.</strong></p><p>Graph analysis is often about discovering how entities connect when the number of relationships between them isn’t known in advance. A message might be part of a reply chain that is three levels deep — or thirty. A component might depend on another component through an unpredictable number of intermediate services. An organizational hierarchy might vary significantly across teams.</p><p>Microsoft Fabric Graph can now query unbounded variable-length paths, enabling traversals of relationships without specifying a fixed maximum number of hops while also being able to specify very large numbers for the upper bound.</p><p>Previously, variable-length traversals supported a low number of maximum hops. That limit worked well for many scenarios, but it required developers to choose an upper boundary even when the structure of the data should determine the traversal depth. With this update, a path can continue beyond this limit until the query finds a matching destination or no valid continuation remains.</p><h3><strong>What is a graph traversal?</strong></h3><p>A graph represents entities as nodes and the relationships between them as edges. For example, a social graph might include people, messages, posts, and forums as nodes. Relationships such as replied to, belongs to, or moderates connect those nodes.</p><p>A traversal follows relationships through the graph. A single-hop traversal answers a direct question, such as which post a message replies to. A multi-hop traversal follows a chain, such as identifying the original post in a reply thread or the upstream system supplying a dataset.</p><p>These chains don’t always have predictable depth. Requiring every query to specify a maximum can produce incomplete results when the selected limit is lower than the depth of the underlying data.</p><h3><strong>From bounded traversals to data-driven traversals</strong></h3><p>Before this update, a variable-length path could traverse up to predefined number of relationships. Developers could query a range of path lengths, but every query remained subject to that maximum.</p><p>Unbounded variable-length paths remove the ceiling. Instead of deciding in advance how deep the query should search, users can express the relationship pattern they want to follow and allow the graph structure to determine the required depth.</p><p>This change applies to scenarios such as:</p><ul><li>Conversation analysis: Follow reply chains back to their originating post.</li><li>Data lineage: Trace upstream or downstream dependencies across complex pipelines.</li><li>Dependency management: Identify transitive software, service, or infrastructure dependencies.</li><li>Organizational analysis: Navigate reporting or ownership hierarchies of varying depth.</li><li>Knowledge graphs: Follow semantic relationships until a relevant entity is found.</li><li>Fraud and security investigations: Explore chains of related accounts, devices, events, or transactions.</li><li>Supply-chain analysis: Trace materials, suppliers, and distribution relationships across multiple tiers.</li></ul><p>Queries can express the intent of retrieving unbounded paths, but not the use of unlimited system responses. They continue to operate within applicable service protections, including execution time, capacity, memory, and result-size constraints.</p><h3><strong>Following an unbounded reply chain</strong></h3><p>Consider a social network graph containing messages, posts, forums, and people. A message can reply to another message, which can reply to another message, eventually reaching the post that began the conversation.</p><p>The following query starts with a specific message and follows the REPLY_OF relationship for as many steps as needed to reach a post. It then identifies the forum containing that post and returns the forum’s moderator.</p><p>In this query, starting with this message, keep following the reply chain until you reach the original post. Then return the forum that contains the post and the person who moderates it.</p><pre>:params { messageId: 206158431836 } <br> <br>MATCH <br> (message:Message {id: $messageId}) <br> -[:REPLY_OF]-&gt;* <br> (post:Post) <br> &lt;-[:CONTAINER_OF]- <br> (forum:Forum) <br> -[:HAS_MODERATOR]-&gt; <br> (moderator:Person) <br>RETURN <br> forum.id AS forumId, <br> forum.title AS forumTitle, <br> moderator.id AS moderatorId, <br> moderator.firstName AS moderatorFirstName, <br> moderator.lastName AS moderatorLastName</pre><p>The query contains four logical steps:</p><ol><li><strong>Find the starting message. </strong>(message:Message {id: $messageId}) identifies the message supplied through the query parameter.</li><li><strong>Follow the complete reply chain. </strong>-[:REPLY_OF]-&gt;* repeatedly follows outgoing REPLY_OF relationships. The traversal isn’t restricted to a limited number of hops, so deeply nested conversations can still reach their originating post.</li><li><strong>Find the containing forum. </strong>(post:Post)&lt;-[:CONTAINER_OF]-(forum:Forum) identifies the forum associated with the post.</li><li><strong>Return the forum moderator. </strong>(forum:Forum)-[:HAS_MODERATOR]-&gt;(moderator:Person) connects the forum to its moderator, whose identifying information is returned.</li></ol><h3><strong>Bounded paths still matter</strong></h3><p>Unbounded paths expand what graph queries can express, but they don’t replace bounded traversal.</p><pre>MATCH (message:Message {id: $messageId})-[:REPLY_OF]-&gt;{5,23}(reply:Message) RETURN reply</pre><p>A bounded path remains the better choice when the business question has a natural maximum. For example, an application looking for professional connections within three degrees should state that limit. A query evaluating only immediate and secondary dependencies should also use a bounded range.</p><p>Use a bounded path when:</p><ul><li>The maximum useful depth is known.</li><li>Connections beyond a certain depth aren’t relevant.</li><li>Predictable query cost or latency is important.</li><li>The graph is dense and each additional hop could produce many more candidate paths.</li></ul><p>Use an unbounded path when:</p><ul><li>The required depth is unknown.</li><li>The query must reach the end of a hierarchy or chain.</li><li>An arbitrary upper limit could omit valid results.</li><li>The data provides a natural termination point, such as a root node or destination label.</li></ul><p>The best path expression is the narrowest one that completely answers the business question.</p><h3><strong>Design efficient unbounded traversals</strong></h3><p>Removing a fixed hop ceiling makes query design even more important. The number of possible paths can grow quickly in a densely connected graph, particularly when nodes have many outgoing relationships, here are some recommendations:</p><ul><li><strong>Start with a selective node</strong></li></ul><p>Anchor the traversal to a specific entity whenever possible. In the example, the message ID provides a precise starting point and avoids scanning reply chains for every message.</p><ul><li><strong>Specify relationship and node labels</strong></li></ul><p>A traversal that names REPLY_OF, Post, and Forum is more targeted than one that follows any relationship to any node. Expressing the intended graph structure constrains the search and makes the query easier to understand.</p><ul><li><strong>Apply filters early</strong></li></ul><p>Place selective conditions as close as possible to the relevant node or relationship pattern. Early filtering reduces the number of candidate paths that later stages must process.</p><ul><li><strong>Return only the required data</strong></li></ul><p>Project the properties needed by the application instead of returning every node, edge, or property encountered during traversal. Smaller results are easier to process and transfer.</p><ul><li><strong>Account for cycles</strong></li></ul><p>Some graphs contain relationships that can lead back to previously visited entities. Model traversal patterns carefully and use supported path modes (ASYCLIC, TRAIL, WALK, etc) or predicates when the scenario requires protection against repeated nodes or edges.</p><ul><li><strong>Test with representative data</strong></li></ul><p>A query that performs well on a small development graph can behave differently when applied to a dense production graph. Test using realistic path depths, relationship density, and result sizes. Monitor execution behavior before incorporating an unbounded traversal into a production workload.</p><h3><strong>Next steps</strong></h3><p>Explore unbounded variable-length paths with a scenario in which relationship depth varies across your data. Evaluate the query with representative graph density and path lengths before using it in a production workload.</p><ul><li><a href="https://learn.microsoft.com/fabric/graph/overview">Graph in Microsoft Fabric overview</a></li><li><a href="https://learn.microsoft.com/fabric/graph/gql-language-guide">GQL language guide for graph in Microsoft Fabric</a></li><li><a href="https://learn.microsoft.com/fabric/graph/gql-query-performance">Optimize GQL query performance for graph in Microsoft Fabric</a></li></ul><p><strong>Tags: #</strong>Graph, Graph Query Language (#GQL), data relationships, graph traversal, #graphsareeverywhere</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c0e3e120e3e4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/explore-deeper-connections-with-unbounded-variable-length-paths-in-microsoft-fabric-graph-c0e3e120e3e4">Explore deeper connections with unbounded variable-length paths in Microsoft Fabric Graph</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[More expressive graph analysis: GQL improvements in Microsoft Fabric Graph]]></title>
            <link>https://medium.com/microsoft/more-expressive-graph-analysis-gql-improvements-in-microsoft-fabric-graph-f461671fa4ce?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/f461671fa4ce</guid>
            <category><![CDATA[microsoft-fabric]]></category>
            <dc:creator><![CDATA[Tushar Goyal]]></dc:creator>
            <pubDate>Wed, 14 Oct 2026 19:01:16 GMT</pubDate>
            <atom:updated>2026-10-14T19:01:15.224Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="https://www.linkedin.com/in/justinpfine/">Justin Fine</a>, <a href="https://www.linkedin.com/in/idrismotiwala/">Idris Motiwala</a>, <a href="https://www.linkedin.com/in/goyal-tushar/">Tushar Goyal</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zJOh7C3aCa2wLjSa6nY9zA.png" /></figure><p><strong>January–August 2026 updates expand how users find, inspect, compose, and analyze connections.</strong></p><p>Graph data helps organizations understand connections: how data moves between systems, how services depend on one another, how conversations develop, and how people or organizations relate. Graph Query Language (GQL) provides a standardized way to ask those questions in Microsoft Fabric Graph.</p><p>From January through August 2026, the GQL feature set expanded across path finding, traversal control, query composition, aggregation, and data handling. The result is broader analytical coverage for business users and more precise query tools for developers. This post explains each improvement and includes the associated syntax.</p><h3><strong>Find and control paths</strong></h3><h4><strong>Shortest paths with ANY SHORTEST</strong></h4><p>When several routes connect two entities, many applications need one shortest route rather than every possible route. ANY SHORTEST makes that intent explicit. This is useful for dependency analysis, recommendations, network routing, and investigations where fewer intermediate steps are preferred.</p><pre>MATCH ANY SHORTEST ({id: 1})-&gt;{,2}(d) <br>RETURN d.id AS dst</pre><h4><strong>Quantified edge patterns</strong></h4><p>Quantified edge patterns repeat a complete relationship pattern, including conditions on intermediate entities. In the following example, the repeated pattern requires the source person at each step to use Chrome. This gives developers more control than repeating an edge alone.</p><pre>MATCH p=(a:Person {id: 987})((x)-[k:knows]-&gt;() WHERE x.browserUsed = &#39;Chrome&#39;){2}(b:Person) <br>FOR n IN NODES(p) WITH OFFSET off <br>RETURN n.browserUsed <br>ORDER BY a.id, b.id, off <br>LIMIT 12</pre><h4><strong>Complete path modes</strong></h4><p>Path modes define whether a traversal can revisit nodes or edges.</p><ul><li>WALK permits repetition.</li><li>TRAIL prevents repeated edges.</li><li>SIMPLE prevents repeated interior nodes.</li><li>ACYCLIC prevents any repeated node.</li><li>DIFFERENT EDGES prevents one edge from satisfying more than one edge variable in the match.</li></ul><p>These choices help query results reflect the meaning of a route, hierarchy, or dependency chain.</p><pre>MATCH WALK (:Person {firstName: &quot;Juliana&quot;})-[:studyAt]-&gt;(:University)&lt;-[:studyAt]-(p:Person) <br>RETURN p.firstName AS name</pre><pre>MATCH TRAIL (:Person {firstName: &quot;Juliana&quot;})-[:studyAt]-&gt;(:University)&lt;-[:studyAt]-(p:Person) <br>RETURN p.firstName AS name</pre><pre>MATCH SIMPLE (x)-&gt;{2}(y) <br>RETURN COUNT(*) AS path_count</pre><pre>MATCH ACYCLIC (x:`node` {id: 1})-&gt;(y)-&gt;(z) <br>RETURN x.id, y.id, z.id <br>ORDER BY x.id, y.id, z.id</pre><pre>MATCH DIFFERENT EDGES (p:Person {name: &#39;Keanu Reeves&#39;})-[e:ACTED_IN]-&gt;(m:Movie) <br>RETURN m.title AS movie</pre><h4><strong>Unbounded variable-length path patterns</strong></h4><p>Variable-length paths previously had a limited maximum hop. Unbounded quantifiers allow the graph structure to determine depth when a chain has no meaningful fixed limit. The example follows one or more relationships while ACYCLIC prevents a node from being visited twice. Unbounded describes the path expression, not unlimited compute; selective starting points and representative performance testing remain important.</p><pre>MATCH ACYCLIC ()-[]-&gt;{1,}() <br>RETURN COUNT(*) AS total</pre><h3><strong>Inspect paths and graph elements</strong></h3><h4><strong>NODES, EDGES, and INNER_NODES</strong></h4><p>These functions expose the contents of a matched path. NODES returns all nodes, EDGES returns its relationships, and INNER_NODES returns the intermediate nodes without the endpoints. They help applications explain lineage, dependencies, and investigative findings instead of returning only the destination.</p><pre>MATCH p=({id: 1})-[]-&gt;{0,4}(z) <br>RETURN NODES(p) AS res_nodes, <br> INNER_NODES(p) AS res_inner_nodes <br>ORDER BY z.id</pre><h4><strong>ELEMENT_ID</strong></h4><p>ELEMENT_ID returns the identity of a graph element. It is useful when an application must compare, track, or reference a specific node or edge independently of its business properties.</p><pre>MATCH (x) <br>RETURN ELEMENT_ID(x) AS vid, x AS actual_id <br>ORDER BY x.id <br>LIMIT 5</pre><h4><strong>SOURCE and DESTINATION</strong></h4><p>SOURCE and DESTINATION identify the endpoints of an edge. This makes directional checks and edge-centric analysis easier, especially when a query starts from a relationship rather than a node.</p><pre>MATCH (x)-[e]-&gt;() <br>RETURN x.id, SOURCE(e).id AS s <br>ORDER BY e.src, e.dst <br>LIMIT 10</pre><pre>MATCH ()-[e]-&gt;(x) <br>RETURN false IN COLLECT_LIST(x = DESTINATION(e)) AS no</pre><h3><strong>Compose larger queries</strong></h3><h4><strong>NEXT</strong></h4><p>NEXT passes the output of one query stage into the next. Analysts can describe a multistage question in the order they reason about it: calculate a measure, filter the result, follow another relationship, and then summarize again.</p><pre>MATCH (m:Message)-[:hasCreator]-(p:Person) <br>RETURN p, AVG(m.length) AS avg_msg_length GROUP BY p <br>NEXT <br>FILTER avg_msg_length &gt; 100u <br>MATCH (p)-[:knows]-&gt;(:Person) <br>RETURN avg_msg_length, COUNT(*) AS connections <br>GROUP BY p, avg_msg_length</pre><h4><strong>EXISTS subqueries</strong></h4><p>EXISTS answers a yes-or-no question about another graph pattern. It can filter rows or participate inside an aggregate without requiring the outer query to return the matching details.</p><pre>MATCH (x) <br>WHERE EXISTS { <br> MATCH (y) <br> WHERE y.id = 5 <br> RETURN y <br>} <br>RETURN x.id AS xid <br>ORDER BY xid<br>MATCH (x) <br>RETURN COUNT(*) FILTER ( <br> WHERE EXISTS { MATCH (x)-[]-&gt;(y) RETURN y } <br>) AS count_with_out</pre><h4><strong>CALL subqueries</strong></h4><p>CALL runs a contained query for the current outer result. The example calculates each node’s outgoing degree in a focused block and then returns it with the node identifier.</p><pre>MATCH (x) <br>CALL { <br> MATCH (x)-[e]-&gt;(y) <br> RETURN COUNT(e) AS out_degree <br>} <br>RETURN x.id AS xid, out_degree <br>ORDER BY xid</pre><h4><strong>OPTIONAL CALL</strong></h4><p>OPTIONAL CALL preserves the outer row when the contained query has no match. This supports enrichment scenarios where related information is useful but not required.</p><pre>MATCH (x {id: 1}) <br>OPTIONAL CALL { <br> MATCH (y)-[]-&gt;(x) <br> RETURN y.id AS yid <br>} <br>RETURN x.id AS xid, yid <br>ORDER BY yid</pre><h4><strong>UNION DISTINCT</strong></h4><p>UNION DISTINCT combines compatible result sets and removes duplicate rows. It complements UNION ALL, which preserves duplicates, so the query can choose the correct set behavior.</p><pre>MATCH () LIMIT 3 <br>RETURN 42 AS x <br>UNION <br>MATCH () LIMIT 3 <br>RETURN 100u AS x</pre><h3><strong>Summarize connected data</strong></h3><h4><strong>Filtered and paged aggregates</strong></h4><p>Filtered aggregates calculate a summary over a selected subset. Paging controls such as LIMIT can be applied within the aggregate, giving developers precise control over which values contribute to the result.</p><pre>MATCH (m:Movie) <br>RETURN COUNT(*) FILTER (WHERE m.released &lt; 1980 LIMIT 10) AS filtered_count</pre><h4><strong>Nested aggregations</strong></h4><p>Nested aggregation supports layered questions, such as finding the minimum of averages calculated across groups or paths. This reduces the need to move intermediate results into application code.</p><pre>MATCH (a:`node`)-[e:`edge`]-&gt;{1,2}(b:`node`) <br>RETURN a.id AS id, MIN(AVG(e.weight)) AS min_average <br>GROUP BY id</pre><h4><strong>COLLECT_ONE, COLLECT_LIST, and COLLECT_ELEMENTS</strong></h4><p>Collection functions retain values or graph elements for evaluation as a group. COLLECT_ONE selects one value, COLLECT_LIST builds a list, and COLLECT_ELEMENTS gathers graph elements. They are useful for path-level checks, ordered output, and analysis across repeated relationships.</p><pre>MATCH (p:`node` {id: 1})-[e]-&gt;{2}(p1:`node` {id: 1}) <br>RETURN (COLLECT_ONE(e.weight) IN [e[0].weight, e[1].weight]) AS contained</pre><pre>MATCH ()-[e]-&gt;{1,2}() <br>RETURN COLLECT_LIST(ALL e.weight) AS w <br>ORDER BY w <br>LIMIT 20</pre><pre>MATCH ()-[a:ACTED_IN]-&gt;{0,4}() <br>WHERE SIZE(COLLECT_ELEMENTS(DISTINCT a.roles)) &gt; 4 <br>RETURN TO_JSON_STRING(a.roles) AS r</pre><h3><strong>Work with values and text</strong></h3><h4><strong>Dynamic nested data</strong></h4><p>Support for dynamic nested data lets queries work with richer values that do not fit a flat property model. This is useful when graph entities contain structured application payloads, nested attributes, or evolving schemas.</p><h4><strong>CAST</strong></h4><p>CAST converts a value to a supported target type. Explicit conversion makes comparisons and calculations more predictable when source data uses different numeric representations.</p><pre>RETURN CAST(42 AS DOUBLE) AS double_res, <br> CAST(42 AS UINT) AS uint_res, <br> CAST(42.5d AS INT) AS int_res</pre><h4><strong>RANGE</strong></h4><p>RANGE creates a numeric sequence. A generated sequence can support iteration, testing, bucketing, and list-oriented calculations within a query.</p><pre>RETURN RANGE(0, 4) AS res</pre><h4><strong>POWER</strong></h4><p>POWER performs exponent calculations. It extends the numeric expressions available for scoring, weighting, and mathematical transformations.</p><pre>RETURN POWER(-2, 3) AS res</pre><h4><strong>NORMALIZE and IS NORMALIZED</strong></h4><p>Unicode text can have visually identical but technically different encodings. NORMALIZE converts text to a normalization form, while IS NORMALIZED checks its current form. This supports more consistent matching and grouping across multilingual data.</p><pre>RETURN NORMALIZE(&#39;cafe\u0301&#39;) AS normalized_text<br>RETURN &#39;cafe\u0301&#39; IS NFKD NORMALIZED AS is_normalized</pre><h4><strong>Regular-expression functions</strong></h4><p>The MSFT.REGEXP functions match, count, locate, extract, and replace text patterns. They help prepare identifiers, names, codes, and descriptions before those values are compared or summarized.</p><pre>RETURN MSFT.REGEXP_LIKE(&#39;café&#39;, &#39;caf.&#39;) AS x<br>RETURN MSFT.REGEXP_COUNT(&#39;abcabcabc&#39;, &#39;abc&#39;) AS n<br>RETURN MSFT.REGEXP_INSTR(&#39;hello world&#39;, &#39;world&#39;) AS p<br>RETURN MSFT.REGEXP_REPLACE(&#39;a1b2c3&#39;, &#39;[0–9]&#39;, &#39;#&#39;) AS s<br>RETURN MSFT.REGEXP_SUBSTR(&#39;abc123def&#39;, &#39;[0–9]+&#39;) AS s</pre><h3><strong>Design guidance</strong></h3><p>Use the narrowest pattern that answers the question. Start from selective nodes, specify relationship and node labels, and apply filters early. Use bounded traversal when the business question has a natural maximum. When depth is unknown, combine an unbounded path with an appropriate path mode and test it against representative graph density. Return only the properties or path elements the application requires.</p><p>Feature availability can vary by environment and release stage. Confirm current support and syntax in the Microsoft Fabric Graph documentation before publishing a production query.</p><h3><strong>Next steps</strong></h3><p>Choose a connected-data scenario — such as lineage, dependency analysis, recommendations, investigations, or knowledge discovery — and identify which path, composition, and aggregation features make the question easier to express.</p><ul><li><a href="https://learn.microsoft.com/fabric/graph/overview">Graph in Microsoft Fabric overview</a></li><li><a href="https://learn.microsoft.com/fabric/graph/gql-language-guide">GQL language guide for graph in Microsoft Fabric</a></li><li><a href="https://learn.microsoft.com/fabric/graph/gql-graph-patterns">GQL graph patterns</a></li><li><a href="https://learn.microsoft.com/fabric/graph/gql-conformance">GQL standard conformance for graph in Microsoft Fabric</a></li><li><a href="https://learn.microsoft.com/fabric/graph/gql-query-performance">Optimize GQL query performance for graph in Microsoft Fabric</a></li></ul><p><strong>Tags: </strong>Graph, Graph Query Language (GQL), graph traversal, connected data, developers</p><p><a href="https://www.linkedin.com/in/muriloadriano?trk=article-ssr-frontend-pulse_little-mention">Murilo Vasconcelos, </a><a href="https://de.linkedin.com/in/stefan-plantikow-49896637/en?trk=article-ssr-frontend-pulse_little-mention">Stefan Plantikow, </a><a href="https://www.linkedin.com/in/idrismotiwala?trk=article-ssr-frontend-pulse_little-mention">Idris Motiwala, </a><a href="https://www.linkedin.com/in/eldad-hagashi?trk=article-ssr-frontend-pulse_little-mention">Eldad Hagashi, </a><a href="https://www.linkedin.com/in/qian-liang-67132311?trk=article-ssr-frontend-pulse_little-mention">Qian Liang, </a><a href="https://www.linkedin.com/in/navondanny?trk=article-ssr-frontend-pulse_little-mention">Danny Navon, </a><a href="https://uk.linkedin.com/in/mrobson1975?trk=article-ssr-frontend-pulse_little-mention">Michael Robson, </a><a href="https://es.linkedin.com/in/paubaiget?trk=article-ssr-frontend-pulse_little-mention">Pau Baiget, </a><a href="https://www.linkedin.com/in/rajagopal-ravipati-79020a4?trk=article-ssr-frontend-pulse_little-mention">Rajagopal Ravipati</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f461671fa4ce" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/more-expressive-graph-analysis-gql-improvements-in-microsoft-fabric-graph-f461671fa4ce">More expressive graph analysis: GQL improvements in Microsoft Fabric Graph</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Governed Multi-Agent Content Factory on Azure]]></title>
            <link>https://medium.com/microsoft/building-a-governed-multi-agent-content-factory-on-azure-663a16dd2b7c?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/663a16dd2b7c</guid>
            <dc:creator><![CDATA[Lior Yantovski]]></dc:creator>
            <pubDate>Mon, 05 Oct 2026 23:42:16 GMT</pubDate>
            <atom:updated>2026-09-26T18:32:39.401Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How three A2A agents, Azure Container Apps Sandboxes, and Microsoft Foundry come together to turn a topic into a research brief, a blog post, social content, and a podcast — with governed, private, default-deny networking end to end.</em></p><p>As soon as an agent needs to touch the open internet — search, scrape, retrieve — “secure by design” stops being a slide and starts being an actual network problem.</p><p>This lab (<strong>aca-sandbox-foundry</strong>, an evolution of <a href="https://github.com/jkalis-MS/azure-container-apps-multi-agent-workflow">jkalis-MS/azure-container-apps-multi-agent-workflow</a>) is a reference implementation for exactly that problem: an AKS-hosted, APIM-governed multi-agent system that isolates the one risky operation — web retrieval — inside disposable Azure Container Apps (ACA) Sandboxes, while everything around it stays authenticated, private, and observable.</p><p>Link to the repository and the full documentation journey at the bottom of this article.</p><h3>What It Actually Does</h3><p>The lab turns a single topic into a complete content package.</p><p>A <strong>Researcher</strong> agent gathers and synthesizes trusted sources into a research brief.</p><p>A <strong>Content Creator</strong> agent turns that brief into a long-form blog post and a social content package.</p><p>A <strong>Podcaster</strong> agent generates a multi-speaker script, synthesizes audio, and persists the result.</p><p>A browser-based <strong>DevUI</strong> shows the whole run: live agent health, sandbox lifecycle events, egress evidence, and the generated artifacts as they land.</p><h3>Solution at a Glance</h3><p><em>High-level architecture: the DevUI orchestrator, three framework-diverse agents, APIM, Foundry, Application Insights, and the ACA Sandbox retrieval path.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HtcNIY_vursHgmVzKDPXDg.png" /><figcaption><em>High-level architecture: the DevUI orchestrator, three framework-diverse agents, APIM, Foundry, Application Insights, and the ACA Sandbox retrieval path.</em></figcaption></figure><p>The user-facing orchestrator coordinates three framework-diverse agents on AKS. APIM governs both agent and model traffic, Microsoft Foundry supplies models and agent registration, Application Insights captures telemetry end to end, and ACA Sandboxes isolate only the research retrieval tool — the sandboxes never host the three long-running agents themselves.</p><h3>Three Agents, Three Frameworks, One Contract</h3><p>The lab intentionally refuses to treat every agent as “the same app in a different container.” Each one uses a distinct framework and runtime suited to its job, and <strong>A2A (Agent2Agent)</strong> is the common contract that lets them interoperate anyway — a real test of the protocol, not a convenience wrapper around one SDK.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/1*u4GM-aZS8HXzZG8sBUf7sQ.png" /><figcaption><em>Three-agent roles: Researcher, Content Creator, and Podcaster, each independently deployable and reachable only through APIM.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PMrktAjndS80FvIgi0cz2Q.png" /></figure><p>DevUI is the user-facing orchestrator, but it is deliberately dumb about security: it runs the research step first, sends the resulting brief to the creator and podcaster in parallel, and never talks to an agent directly. Every call goes same-origin to the BFF, and every BFF call goes through APIM. No agent calls another agent — each remains an independently deployable service that happens to share an interoperability contract, not a tightly coupled pipeline.</p><h3>The Azure Stack, Component by Component</h3><p>This is where the lab earns its keep — not by picking exotic technology, but by wiring familiar Azure services together with a consistently paranoid network posture.</p><h3>Azure Kubernetes Service (AKS)</h3><p>AKS is the compute backbone for all three agents and the BFF. Each agent runs on a dedicated, tainted node pool, isolated from general workloads. No agent Pod is exposed publicly: every agent is fronted by an internal Kubernetes <em>LoadBalancer</em> Service bound to a private VNet address rather than a public IP. <em>loadBalancerSourceRanges</em> on each Service restricts inbound connections to APIM’s subnet alone, and Azure CNI powered by Cilium enforces the identical restriction a second time inside the cluster’s own dataplane. That redundancy is deliberate: even a bug in DevUI or the BFF that tried to call an agent directly, bypassing APIM, would find no route to take. AKS also hosts the Sandbox Broker, which is the only component permitted to request a sandbox on the Researcher’s behalf.</p><h3>Azure API Management (APIM)</h3><p>APIM is the single governed front door for two very different kinds of traffic, and that is the point: it is the only path in either direction. First, it fronts <strong>A2A traffic</strong> between the BFF and the three agents, so every inter-agent request is authenticated, rate-limited, and logged in one place rather than scattered across ad hoc service-to-service calls. Second, it exposes the <em>/openai</em> gateway that all three agents use for Microsoft Foundry model and text-to-speech access, so no agent holds a direct Foundry endpoint or API key of its own. Because APIM is also the only subnet allow-listed by every agent’s <em>loadBalancerSourceRanges</em>, it doubles as the network chokepoint the whole design is built around, not just an API facade.</p><h3>Microsoft Foundry</h3><p>Foundry is the model and agent-registration plane. It supplies the large language models the Researcher, Content Creator, and Podcaster call for synthesis, drafting, and script generation, along with the text-to-speech models behind the podcast audio. Foundry also handles custom-agent registration, giving each agent a governed identity in the platform rather than being an anonymous caller of a shared model endpoint. Every one of those calls is routed through the APIM <em>/openai</em> gateway — agents never dial Foundry directly, which keeps model access inside the same audit and policy boundary as every other governed call in the system.</p><h3>Azure Container Apps (ACA) Sandboxes</h3><p>Azure Container Apps Sandboxes reached <strong>general availability this week</strong> — and it is not a new isolation stack. It is Microsoft externalizing the same hardware-isolated microVM fabric that already runs Cloud Sandboxes in GitHub Copilot ,Foundry Hosted Agents, Azure ARE Agents and more. During public preview it was already creating over a million sandboxes a day. That maturity is exactly why it is the right foundation for the Researcher’s retrieval path in this lab, and for running private coding agents more broadly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kdIcCrzc22qqfqNVm-WTaw.png" /></figure><p><em>The GA announcement: sandboxes execute securely by default, resume instantly from a full memory-and-disk snapshot, and burst to hyperscale in sub-second time — with Azure Container Apps, Foundry, GitHub, and Container Apps Express all building on the same foundation layer.</em></p><p>The topology behind that announcement is straightforward once you see it laid out. A sandbox group is the unit of governance: everything inside it shares disk images, snapshots, data volumes, secrets, and identity, while every individual sandbox still gets its own CPU, memory, disk, and network boundary.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/932/1*zl2PauTF7F0tLYjKJNzMOw.jpeg" /></figure><p><em>Portal, CLI, and SDK access all converge on a sandbox group. Ingress is off by default and opt-in per sandbox and port, waking on request; egress runs through a per-sandbox proxy enforcing host allowlists and deny policies, with injected credentials that never enter the sandbox itself.</em></p><p>That last point is the one worth sitting with: credential injection happens at the egress proxy, <strong>outside</strong> the sandbox boundary. Code running inside — including AI-generated code — never sees the actual secret, only the effect of a request that was allowed to go through. It is the same shape as the Sandbox Broker pattern in this lab’s Researcher path, just implemented as a first-class platform primitive instead of something you wire up yourself.</p><h3>What This Looks Like Running</h3><p>A sandbox group in the Azure portal shows exactly what it costs to keep isolation this granular: total sandboxes, how many are actually running versus stopped, custom images, and snapshots, alongside live runtime metrics for running cores and memory.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gDjs7jvTi4TE-ogazEta6g.png" /></figure><p><em>A live sandbox group: 4 total sandboxes (one goes down), running-core and running-memory metrics over the last days, and recent sandboxes each stopped with their exact resource footprint (4 cores / 8 GiB / 80 GiB, down to 0.25 cores / 512 MiB / 5 GiB) preserved for the next resume.</em></p><p>Scoping sandboxes per task, rather than sharing one long-lived environment, is the pattern that keeps retrieval contained. Here five sandboxes are each labeled with the single host they were created to reach — one for azure.microsoft.com, one for learn.microsoft.com, one for github.com, and so on — all created within the same second and all running on identical 1 core / 2 GiB / 20 GiB footprints.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NlefV3b8J8QP2j0aconh7Q.png" /></figure><p><em>Five sandboxes, each scoped to a single allow-listed host and spun up simultaneously — the sub-second, burst-to-hyperscale behavior from the GA announcement, seen from the inside.</em></p><p>And this is the observability piece made concrete: a single running sandbox, its live terminal, and its egress network traffic panel — every outbound request logged with a timestamp, method, destination, and an explicit allowed or denied verdict, right next to real-time CPU, memory, storage, and network utilization and a live log stream.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gznGhwr6kVKkEk4NmsUTcw.png" /></figure><p><em>22 requests allowed, 0 denied — every GET call to azure.microsoft.com, learn.microsoft.com, files.pythonhosted.org, and pypi.org visible with its verdict, alongside live CPU, memory, storage, and network metrics for the sandbox.</em></p><p>This is what “governed, closed, and observable” means in practice: nothing leaves the sandbox that was not explicitly allow-listed, nothing that does leave is invisible, and none of it costs meaningful startup latency. For a Researcher agent fetching untrusted web content, or for a private Claude or GitHub Copilot coding agent running inside a customer’s tenant, that combination — hardware isolation, default-deny egress with full request-level audit, and sub-second start — is precisely the missing piece between “agents that browse the web” and “agents you can actually run in a governed enterprise environment.”</p><p>Now let’s return to our use case and see how ACA Sandboxes provides best solution for our needs.</p><p>ACA Sandboxes exist to solve one specific problem: the Researcher’s job requires fetching content whose provenance and payload it does not control, and that is the one operation in the whole system with real blast radius. The Researcher never performs that fetch itself — it hands the task to a <strong>Sandbox Broker</strong>, which spins up a short-lived, per-task ACA Sandbox with <strong>default-deny egress (</strong>in our example we blocked bing.com on purpose), scoped to exactly the destinations that single task requires. When the task completes, the sandbox is torn down with it. If a scraped page tried to phone home or pivot toward an internal service, there would be nothing left for it to reach by the time anyone noticed. Sandboxes are strictly a retrieval tool for the Researcher — they never host the long-running agents, the BFF, or DevUI.</p><h3>Identity, Storage, and Observability</h3><p>A user authenticates through Microsoft Entra ID, fronted by Application Gateway for Containers and an OAuth2 Proxy sidecar pattern, so no credential — reusable or otherwise — ever lives in the browser. The Podcaster writes generated audio to private Azure Blob Storage using Workload Identity instead of a connection string or access key in a config file. And OpenTelemetry instrumentation runs through every agent and the BFF, exporting to Application Insights and Log Analytics with APIM diagnostics stitched into the same correlation IDs, so a slow or failed run can be traced end to end — from the browser click down through the sandbox that did the retrieval.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kMmE9e0iTX4GeRcy6c7Yhg.png" /><figcaption>System Architecture</figcaption></figure><h3>Seeing It Run: User Experience</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P8oxcmPumh3BpSEft3f2nA.png" /><figcaption>DevUI is the user-facing orchestrator</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ldy8QeHxHYWFS6CSzGt91Q.png" /><figcaption><em>DevUI showing ACA Sandbox execution, egress evidence, the synthesized research brief, and generated blog content.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nwT0DxIzhn8RL7iA1EYZcA.png" /><figcaption>Finally, we got full Media blog + Social Post + Reay Podcast with Audio + Transcript</figcaption></figure><h3>How a Request Actually Flows</h3><p>1. A user authenticates with Microsoft Entra ID through Application Gateway for Containers and OAuth2 Proxy.</p><p>2. DevUI JavaScript calls same-origin protected APIs through the BFF.</p><p>3. The BFF sends A2A requests to APIM rather than exposing reusable downstream credentials in the browser.</p><p>4. APIM reaches three source-restricted private AKS origins: research, creator, and podcaster.</p><p>5. The agents use the APIM <em>/openai</em> gateway for Foundry model and TTS access.</p><p>6. Research delegates isolated retrieval to the Sandbox Broker, which creates policy-controlled ACA Sandboxes.</p><p>7. Podcaster persists audio through a private Blob endpoint, while workloads and APIM export correlated telemetry to Application Insights.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jwZMuWpMWldrQNSQi8cNNA.png" /><figcaption>Network Design</figcaption></figure><h3>Project Status and Deployment Boundary</h3><p>This is a working baseline, not a proof of concept: AKS, APIM, Foundry, ACA Sandboxes, private storage, identity, ingress, and observability are all deployed and validated together as a conventional Kubernetes stack, stood up end to end with the Azure Developer CLI and Bicep.</p><p>One caveat worth flagging for anyone adapting this: the demonstration AKS API endpoint is left public for operator convenience, while the application workloads and agent origins themselves stay private. A production deployment needs a private AKS control plane, trusted TLS, production-grade identity governance, and organization-approved network controls layered on top of everything described above.</p><h3>Summary</h3><p>The interesting decision in this lab is not “agents can collaborate” — that part is easy to demo and easy to fake. It is that the one operation with real blast radius, unrestricted web retrieval by an LLM-driven agent, is fully contained behind default-deny, per-task sandboxes, enforced at two independent layers of the network, while the rest of the system stays governed by Entra ID, APIM, and Foundry end to end.</p><h3>Resources and documentation</h3><p>● Repository: <a href="https://github.com/lyantovski/aca-sandbox-foundry">lyantovski/aca-sandbox-foundry</a></p><p>● <a href="https://techcommunity.microsoft.com/blog/appsonazureblog/azure-container-apps-sandboxes-now-generally-available/4559125">Azure Container Apps Sandboxes, Now Generally Available | Microsoft Community Hub</a></p><p>● <a href="https://sandboxes.azure.com/docs/sandboxes/">ACA Sandboxes | ACA Sandboxes Portal Docs</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=663a16dd2b7c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/building-a-governed-multi-agent-content-factory-on-azure-663a16dd2b7c">Building a Governed Multi-Agent Content Factory on Azure</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sharing a few tips and tricks about AI on Azure, that Claude itself isn’t always aware of!]]></title>
            <link>https://medium.com/microsoft/sharing-a-few-tips-and-tricks-about-ai-on-azure-that-claude-itself-isnt-always-aware-of-c44faeb6b7cd?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/c44faeb6b7cd</guid>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Stephane Eyskens]]></dc:creator>
            <pubDate>Mon, 05 Oct 2026 23:42:11 GMT</pubDate>
            <atom:updated>2026-10-05T23:42:10.096Z</atom:updated>
            <content:encoded><![CDATA[<p>I was recently part of an AI project where I learnt a few things and realized that I was not the only one to learn, so I thought it might be a good idea to share the findings with a broader audience.</p><h4>Tip #1 — Foundry-hosted agents and outbound traffic</h4><p>When hosting your agents in Foundry, they are running into a container app environment managed by Microsoft behind the scenes. While Foundry supports the BYO subnet feature, you must make sure to:</p><ul><li>Use a standard setup (not basic)</li><li>Bring not only your own subnet but also your own resources, meaning Cosmos DB, AI Search, Storage, etc.</li></ul><p>Only bringing your own subnet will not throw any error but the agents will still fail to resolve private link resources and go to the public endpoints of any resource they try to reach.</p><p>Here is a useful repo to get started <a href="https://github.com/microsoft-foundry/foundry-samples/">https://github.com/microsoft-foundry/foundry-samples/</a></p><h4>Tip #2 — Using private link with Sandbox Groups</h4><p>Azure Sandbox Groups are a great way to execute LLM-generated code safely. They are backed by MicroVMs, ensuring strict isolation as well as lightning speed. However, in the enterprise environment, we tend to isolate things from internet as much as possible, and here comes the tip! Sandbox Groups themselves have no private endpoints. The best you can achieve is to add network ACLs on their public endpoints. However, you can attach them to Container Apps Environments of type ‘Express’. Once attached, the Sandbox Groups can be connected using private endpoints. You can read more about this here <a href="https://sandboxes.azure.com/docs/sandboxes/private-endpoints">https://sandboxes.azure.com/docs/sandboxes/private-endpoints</a></p><h4>Tip #3 — Dealing with MCP authentication (DCR and/or CIMD)</h4><p>I shared a few options to deal with MCP within the Azure and Microsoft ecosystem. You can read this here to get all the details <a href="https://medium.com/@stephaneeyskens/i-built-my-own-wine-companion-mcp-server-on-azure-and-net-it-wasnt-that-straightforward-f8e74fab8722">https://medium.com/@stephaneeyskens/i-built-my-own-wine-companion-mcp-server-on-azure-and-net-it-wasnt-that-straightforward-f8e74fab8722</a> .</p><p>However, I recently discovered an option I wasn’t aware of, consisting of using FastMCP, a Python library. In a nutshell, FastMCP is a library that is part of your MCP server and acts as a proxy between the MCP client and your MCP server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/862/1*_2LXsudQGBvDpsx5BIOvLQ.png" /><figcaption>FastMCP library</figcaption></figure><p>It supports both Dynamic Client Registration (DCR) and Client ID Metadata Documents (CIMD). During the handshake, the client communicates with FastMCP rather than directly with the identity provider for registration and token acquisition. The client interacts with Entra ID only to authenticate the user.</p><p>FastMCP acquires tokens transparently and passes them to the MCP server, which can then use an on-behalf-of (OBO) flow to obtain tokens for downstream APIs. FastMCP also provides a state API that makes it easier to persist state in Redis, using a computed JTI key to link that state to upstream tokens.</p><p>Below is the sequence diagram showing the flow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cADAMov0Nb5Sntu1eKFbUg.png" /><figcaption>MCP Client-Server interaction with Entra ID as the IDP</figcaption></figure><p>Here is a pointer to the official FastMCP web site <a href="https://gofastmcp.com/v2/servers/auth/oauth-proxy">https://gofastmcp.com/v2/servers/auth/oauth-proxy</a></p><h4>Tip #4 — Dealing with MCP integration with SaaS platforms</h4><p>Although Satya Nadella suggested that SaaS platforms would die with the rise of AI, we still see many SaaS platforms whose the core business <strong>is</strong> AI. Platforms such as Perplexity and Claude code are just examples of these. I see two ways to expose your enterprise MCP servers to the outside world:</p><ul><li>Use Private Link Service (PLS) whenever possible if the SaaS provider supports it.</li><li>Expose your MCP server to Internet. In this case, you’d go for Front Door/Application Gateway ==&gt; API Management ==&gt; MCP server with or without FastMCP.</li></ul><p>However, at this stage, using PLS remains not that easy because:</p><ul><li>As of 09/2026, PLS Direct Connect is still in preview and only available in a few regions (e.g. only Spain Central for Europe).</li><li>The destination of a PLS cannot be a private endpoint, although it technically works (I tested it).</li></ul><p>The Citadel approach consists in using an AI gateway (API Management) as a central policy enforcement point. However, as of 09/2026, we cannot use Private Link Service (PLS) with API Management’s load balancer because PLS does not recognize it as an Azure Load Balancer. A private endpoint for APIM might seem like an alternative, but that scenario is not supported either. PLS requires a standard Azure Load Balancer, such as the one used by AKS.</p><p>For now, the only supported approach appears to be proxying the AI gateway through a gateway API hosted on AKS, although that feels like a dirty workaround. Hopefully, Microsoft will make PLS Direct Connect generally available across all regions soon and support private endpoints as destinations, enabling a wider range of integration scenarios.</p><h4>Tip #5 — Writing stateful agents</h4><p>There are many ways to orchestrate agents, but fewer options for running stateful orchestrations. As of 09/2026, I consider Dapr workflows to be one of the best options. Additionally, Dapr makes it easy to run ambient agents, which are nothing else but agents responding to events/commands, and Dapr particularly excels in building distributed systems.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c44faeb6b7cd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/sharing-a-few-tips-and-tricks-about-ai-on-azure-that-claude-itself-isnt-always-aware-of-c44faeb6b7cd">Sharing a few tips and tricks about AI on Azure, that Claude itself isn’t always aware of!</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deploy and use the Foundry Model Router using Azure Bicep]]></title>
            <link>https://medium.com/microsoft/deploy-and-use-the-foundry-model-router-using-azure-bicep-0339eddaaffa?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/0339eddaaffa</guid>
            <category><![CDATA[microsoft]]></category>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Brian Veldman]]></dc:creator>
            <pubDate>Mon, 31 Aug 2026 14:49:53 GMT</pubDate>
            <atom:updated>2026-08-31T14:49:51.865Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZCVhvncuNr-DQrlhnqv63w.png" /></figure><p>As organizations build more AI powered applications, choosing the right model for every request becomes increasingly important. Not every prompt requires a large and expensive reasoning model, while more complex workloads often benefit from advanced models with higher capabilities. Microsoft Foundry Model Router addresses this challenge by automatically selecting the most appropriate model for each request based on factors such as complexity, quality requirements, latency, and cost. Instead of maintaining custom routing logic, developers can interact with a single endpoint while the routing decisions happen behind the scenes..</p><p>In this article, we will deploy Microsoft Foundry Model Router using Bicep and explore how Infrastructure as Code can simplify the deployment and management of intelligent AI routing workloads on Azure.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CiUX1IASkakKJWvzC-12Yw.gif" /><figcaption>This diagram costed me $3 dollars on AI credits…</figcaption></figure><p>The main.bicepparam template defines our input parameters, such as the Landing Zone, Environment, Location.</p><pre>using &#39;main.bicep&#39;<br><br>//////////////////////////////////<br>param parAlzName = &#39;sln&#39;<br>param parEnvironment = &#39;prod&#39;<br>param parLocation = &#39;swedencentral&#39;<br>/////////////////////////////////</pre><p>The main.bicep template serves as the entry point for our Bicep configuration and defines the parameters and variables used across the Azure AI Foundry module.</p><pre>metadata name = &#39;Cognitive Services Account with Model Router&#39;<br>metadata description = &#39;This is a Bicep template to deploy a Cognitive Services Account with the Model Router deployment.&#39;<br>metadata owner = &#39;Brian Veldman&#39;<br>metadata version = &#39;1.0.0&#39;<br><br>/* TARGET SCOPE */<br>targetScope = &#39;subscription&#39;<br><br>@description(&#39;Defining our input parameters&#39;)<br>param parEnvironment string<br>@allowed([<br>  &#39;swedencentral&#39;<br>])<br><br>param parLocation string <br>param parAlzName string<br><br>@description(&#39;Defining our variables&#39;)<br>var shortEnvironmentMapping = {<br>  swedencentral: &#39;sdc&#39; // Supported for Model Router in Foundry<br>}<br><br>var shortEnvironment = shortEnvironmentMapping[parLocation] ?? parLocation<br>var resourceGroupName = &#39;rg-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br><br>@description(&#39;Deploying our resourcegroup&#39;)<br>resource rg &#39;Microsoft.Resources/resourceGroups@2025-04-01&#39; = {<br>  name: resourceGroupName<br>  location: parLocation<br>}<br><br>@description(&#39;Deploying our AI module&#39;)<br>module ai &#39;modules/ai.bicep&#39; = {<br>  params: {<br>    parAlzName: parAlzName<br>    parEnvironment: parEnvironment<br>    parLocation: parLocation<br>    shortEnvironment: shortEnvironment<br>  }<br>  scope: rg<br>}</pre><p>The ai.bicep module automates the deployment of an Azure AI Foundry environment by provisioning an Azure AI Services account, an AI Foundry project, and a Model Router deployment. It creates a centrally managed AI platform with project management capabilities enabled, configures a dedicated AI Foundry project with a system assigned managed identity for secure resource access, and deploys a Model Router endpoint using the Global Standard SKU. The module also applies a consistent naming convention based on the landing zone, environment, and location parameters, enabling standardized and scalable AI service deployments across Azure environments. ❤️</p><pre>param parEnvironment string<br>param parLocation string <br>param parAlzName string<br>param shortEnvironment string<br><br>@description(&#39;Deployment of Foundry&#39;)<br>resource aif &#39;Microsoft.CognitiveServices/accounts@2025-12-01&#39; = {<br>  name: &#39;aif-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br>  location: parLocation<br>  kind: &#39;AIServices&#39;<br>  sku: {<br>    name: &#39;S0&#39;<br>  }<br>  properties: {<br>    allowProjectManagement: true<br>    customSubDomainName: &#39;sub-aif-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br>  }<br>}<br><br>@description(&#39;Deployment of Foundry Project&#39;)<br>resource aip &#39;Microsoft.CognitiveServices/accounts/projects@2025-12-01&#39; = {<br>  name: &#39;proj-aif-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br>  parent: aif<br>  location: parLocation<br>  identity: {<br>    type: &#39;SystemAssigned&#39;<br>  }<br>}<br><br>@description(&#39;Deployment of Foundry Model Router&#39;)<br>resource router &#39;Microsoft.CognitiveServices/accounts/deployments@2025-12-01&#39; = {<br>  name: &#39;model-router&#39;<br>  parent: aif<br>  sku: {<br>    name:  &#39;GlobalStandard&#39;<br>    capacity: 499 //1 capacity unit = 1000 tokens per minute, took the standard capacity<br>  }<br>  properties: {<br>    model: {<br>      name: &#39;model-router&#39;<br>      format: &#39;OpenAI&#39;<br>      version: &#39;2025-11-18&#39; // Last supported version<br>      <br>    }<br>    routing: {<br>      mode: &#39;balanced&#39;<br>    }<br>  }<br>}</pre><p>Customize the code to align with your specific requirements, then execute it. After a few minutes, all the necessary resources will be deployed. This can be done using: New-AzSubscriptionDeployment -Name deploymentName -TemplateFile .\main.bicep -TemplateParameterFile .\main.bicepparam -Location swedencentral.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*d0r7Q-wDm6h1nDA4-LRm4Q@2x.png" /></figure><p>After deployment, you can use the standard Chat Completions API against the Model Router endpoint. Simple prompts are typically routed to smaller and cheaper models, while more complex reasoning tasks are automatically directed to larger models. This allows organizations to optimize both cost and performance without modifying application logic.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9ThjMg7T75dl09fybFOEhQ@2x.png" /></figure><p>Within the Microsoft Foundry portal, navigate to Deployments and verify that the Model Router deployment is present and successfully provisioned.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*88I5xycDtnQsN0Y_8y3iFw@2x.png" /></figure><p>We can see the model-router deployment. For this demo, I will use the Open in Playground option, which allows us to test the Model Router directly from within the browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZzoRjh-TTTOxTCcv8J4nUQ@2x.png" /></figure><p>When I ask <em>“What is Azure?”</em>, the request is routed to the GPT-5 Mini model by the Model Router, which selects the most appropriate model based on the prompt and routing configuration. For this query, the GPT-5 Mini model is chosen to generate the response.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Z_BQiddxoEBRZGIPhubZ-Q@2x.png" /></figure><p>However, when I ask, <em>“Can you write an Azure Bicep template based on Azure Verified Modules to deploy an Azure Virtual Machine?”</em>, the Model Router selects the GPT-5.3 Chat model. This is because the request involves more advanced reasoning, code generation, and knowledge of Azure infrastructure patterns, making GPT-5.3 Chat the more suitable model for generating an accurate and comprehensive response.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eYDI5YCQLWT_YjanMmSOfw@2x.png" /></figure><p>We can also use the Completions API to call the Model Router endpoint directly. With this approach, every request is automatically routed to the most suitable model based on the complexity and requirements of the prompt. This allows applications to benefit from optimized model selection without having to manually choose between different models, helping to balance performance, capability, and cost.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ytZTFZngycyZjqvAPhrV1g@2x.png" /></figure><p>That’s all for today. As you could see, Azure AI Foundry Model Router simplifies the way applications interact with AI models by automatically selecting the most suitable model for each request. Whether you are handling simple questions, complex reasoning tasks, or code generation scenarios, Model Router helps optimize performance, capability, and cost without requiring manual model selection. This makes it easier to build intelligent applications that can adapt to a wide range of user requests while taking full advantage of the latest AI models available in Azure AI Foundry. The Azure Bicep code is available in my GitHub repository: <a href="https://github.com/brianveldman/azure-bicep">GitHub — brianveldman/azure-bicep</a>. Feel free to star the repository and clap for this story and let me know if you have any feedback. I look forward to seeing you in the next session. Until then, take care! ❤️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0339eddaaffa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/deploy-and-use-the-foundry-model-router-using-azure-bicep-0339eddaaffa">Deploy and use the Foundry Model Router using Azure Bicep</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Configuring Microsoft Entra ID Authentication for Azure SQL Database using Azure Bicep]]></title>
            <link>https://medium.com/microsoft/configuring-microsoft-entra-id-authentication-for-azure-sql-database-using-azure-bicep-d0bc5d01875d?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/d0bc5d01875d</guid>
            <category><![CDATA[infrastructure-as-code]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[sql-server]]></category>
            <category><![CDATA[microsoft]]></category>
            <category><![CDATA[azure]]></category>
            <dc:creator><![CDATA[Brian Veldman]]></dc:creator>
            <pubDate>Mon, 17 Aug 2026 17:44:18 GMT</pubDate>
            <atom:updated>2026-08-17T17:44:17.697Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s3NUB9uegxX0HJbX0Mmqxg.png" /></figure><p>When I started blogging, one of my first articles was about configuring Microsoft Entra ID authentication for Azure SQL Database. At that time, everything was done manually through the Azure portal using ClickOps. While that worked perfectly fine, it wasn’t exactly the Infrastructure as Code approach that I prefer. Recently, I was asked how to deploy Azure SQL with Microsoft Entra ID authentication entirely using Azure Bicep. That question inspired me to revisit the topic and build a fully automated solution that not only deploys the Azure SQL Server and database, but also creates the required Microsoft Entra groups and configures the Entra administrator. In this blog, I’ll walk you through building a complete end to end Azure Bicep solution, allowing you to provision Azure SQL with Microsoft Entra ID authentication from scratch without relying on manual configuration in the Azure portal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aEbobZJZzc89VxDBmeh8jA.gif" /><figcaption>Diagram of the solution that we are going to deploy ❤</figcaption></figure><p>The main.biceptemplate serves as the entry point for our Infrastructure as Code solution. It defines the parameters, variables, and module orchestration required to deploy the complete Azure SQL environment.</p><pre>metadata name = &#39;Deploy Azure SQL Server with Entra ID Authentication&#39;<br>metadata description = &#39;Deploys an Azure SQL Server with Entra ID authentication enabled for administrators.&#39;<br>metadata owner = &#39;Brian Veldman&#39;<br>metadata version = &#39;1.0.0&#39;<br><br>/* TARGET SCOPE */<br>targetScope = &#39;subscription&#39;<br>/* EXTENSIONS */<br>extension &#39;br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.8-preview&#39;<br><br>@description(&#39;Defining our input parameters&#39;)<br>@allowed([<br>  &#39;canadacentral&#39;<br>  &#39;westus&#39;<br>  &#39;westus2&#39;<br>  &#39;eastus&#39;<br>  &#39;eastus2&#39;<br>  &#39;westeurope&#39;<br>])<br>param parLocation string = &#39;canadacentral&#39;<br>param parEnvironment string<br>param parAlzName string<br><br>@description(&#39;Defining our variables&#39;)<br>var varShortEnvironmentMapping = {<br>  canadacentral: &#39;cnc&#39;<br>  westus: &#39;wus&#39;<br>  westus2: &#39;wus2&#39;<br>  eastus: &#39;eus&#39;<br>  eastus2: &#39;eus2&#39;<br>  westeurope: &#39;weu&#39;<br>}<br><br>var shortEnvironment = varShortEnvironmentMapping[parLocation] ?? parLocation<br>var resourceGroupName = &#39;rg-sql-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br>var sqlServerName = &#39;sql-entra-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br>var sqlDatabaseName = &#39;sqldb-test-${parAlzName}-${parEnvironment}-${shortEnvironment}-001&#39;<br><br>@description(&#39;Resource Group Deployment&#39;)<br>resource sqlResourceGroup &#39;Microsoft.Resources/resourceGroups@2023-07-01&#39; = {<br>  name: resourceGroupName<br>  location: parLocation<br>}<br><br>@description(&#39;Creating Entra ID Group for SQL Administrators&#39;)<br>resource groupAdministrators &#39;Microsoft.Graph/groups@v1.0&#39; = {<br>  displayName: &#39;SQL Administrators&#39;<br>  mailEnabled: false<br>  mailNickname: &#39;sqladministrators&#39;<br>  securityEnabled: true<br>  uniqueName: &#39;sqladministrators&#39;<br>  members: [<br>    deployer().objectId<br>  ]<br>}<br><br>@description(&#39;Creating Entra ID Group for SQL Readers&#39;)<br>resource groupReaders &#39;Microsoft.Graph/groups@v1.0&#39; = {<br>  displayName: &#39;SQL Readers&#39;<br>  mailEnabled: false<br>  mailNickname: &#39;sqlreaders&#39;<br>  securityEnabled: true<br>  uniqueName: &#39;sqlreaders&#39;<br>}<br><br>@description(&#39;Deploying Azure SQL Server with Entra ID Authentication&#39;)<br>module server &#39;br/public:avm/res/sql/server:0.21.3&#39; = {<br>  params: {<br>    name: sqlServerName<br>    administrators: {<br>      azureADOnlyAuthentication: true<br>      login: groupAdministrators.displayName<br>      principalType: &#39;Group&#39;<br>      sid: groupAdministrators.id<br>    }<br>    location: parLocation<br>    databases: [<br>      {<br>        name: sqlDatabaseName<br>        availabilityZone: -1<br>        collation: &#39;SQL_Latin1_General_CP1_CI_AS&#39;<br>        maxSizeBytes: 53687091200<br>        zoneRedundant: false<br>        sku: {<br>          name: &#39;S1&#39;<br>          tier: &#39;Standard&#39;<br>        }<br>      }<br>    ]<br>  }<br>  scope: resourceGroup(resourceGroupName)<br>  dependsOn: [<br>    sqlResourceGroup<br>  ]<br>}<br><br></pre><p>Customize the code to align with your specific requirements, then execute it. You can do this by using: New-AzSubscriptionDeployment -Name deploymentName -Location canadacentral -TemplateFile .\main.bicep. After a few minutes, all the necessary resources will be deployed. We can see the Azure resources deployed within the resource group.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YlpT1f_5fBc5HsmeFHgR0A.png" /></figure><p>When checking Microsoft Entra ID, we can see that the SQL Administrators group has been created successfully, with me added as a member through the deployer().objectId function, which is exactly the expected behavior.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LxJ-jjEb75aqGbAy1Z1cnw.png" /></figure><p>We can also see that the SQL Administrators Microsoft Entra ID group has been configured as the Microsoft Entra administrator for the Azure SQL Server. 😍</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TImu1I6alhaL6DGY8COVqQ.png" /></figure><p>Now using the Query Editor we can access the Azure SQL Database since we have succesfully access to it using the Microsoft Entra Authentication.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W9_esc1TvlHpKCQ1ATjZIg.png" /></figure><p>What about the SQL Readers group? For this group, I chose to create the Microsoft Entra ID group as part of the Bicep deployment. However, I grant it access manually by using the following T-SQL statement to create the contained database user: CREATE USER [SQL Readers] FROM EXTERNAL PROVIDER;</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_HdE4TQatjAkRJhH5pl_oQ.png" /></figure><p>We can also grant the SQL Readers group the db_datareader database role by using the following T-SQL statement: ALTER ROLE db_datareader ADD MEMBER [SQL READERS];. From now on, any user who is added to the Microsoft Entra ID group SQL Readers can connect to the Azure SQL Database using SQL Server Management Studio, provided they have network access to the database. Because the group is a member of the db_datareader role, its members can read data from all user tables and views in the database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h0LewX4JRwD1ACrB15Bx1Q.png" /></figure><p>From this point, you know how to deploy an Azure SQL environment by using Azure Bicep and securely grant users access to Azure SQL databases through Microsoft Entra ID authentication. By adopting Infrastructure as Code, you can build repeatable, scalable, and consistent Azure SQL environments while minimizing manual configuration, reducing deployment time, and improving security. The Azure Bicep code is available in my GitHub repository: <a href="https://github.com/brianveldman/azure-bicep/tree/main/04-Compute-Containers/Azure%20Virtual%20Desktop/Session%20Host%20Config">GitHub — brianveldman/azure-bicep</a>. Feel free to star the repository and clap for this story. That’s all for today. I’d love to hear your feedback, and I look forward to our next session. Until then, take care. ⭐️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d0bc5d01875d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/configuring-microsoft-entra-id-authentication-for-azure-sql-database-using-azure-bicep-d0bc5d01875d">Configuring Microsoft Entra ID Authentication for Azure SQL Database using Azure Bicep</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Live Signals to Smarter Risk: Building a Company Brain on Microsoft Fabric]]></title>
            <link>https://medium.com/microsoft/from-live-signals-to-smarter-risk-building-a-company-brain-on-microsoft-fabric-c5eb14548790?source=rss----8bec1183ada9---4</link>
            <guid isPermaLink="false">https://medium.com/p/c5eb14548790</guid>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[microsoft-fabric]]></category>
            <category><![CDATA[realtime-analytics]]></category>
            <category><![CDATA[ontology]]></category>
            <category><![CDATA[ai-agent]]></category>
            <dc:creator><![CDATA[Dori Rabin]]></dc:creator>
            <pubDate>Thu, 16 Jul 2026 15:18:31 GMT</pubDate>
            <atom:updated>2026-07-16T15:18:30.589Z</atom:updated>
            <content:encoded><![CDATA[<h4>How Microsoft Fabric turns live maritime signals into agentic risk intelligence for real-time decisions.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W0HcU1YoB5LCGSgMIt4Itw.png" /></figure><p>A vessel is crossing the Strait of Hormuz. Its AIS signal updates in real time. A few minutes later, the vessel enters a high-risk zone.</p><p>For a maritime insurer, that single movement can trigger a chain of business questions:</p><p>Is the vessel still covered?<br>Does the policy include a war-risk exclusion?<br>Is there a warranty breach?<br>Which company operates the vessel?<br>What cargo is onboard?<br>What is the financial exposure right now?</p><p>Traditionally, answering those questions requires people to stitch together multiple systems: live vessel telemetry, geospatial boundaries, policy contracts, customer and cargo data, dashboards, and operational alerts.</p><p>This is exactly the kind of problem where enterprise AI needs more than retrieval. It needs business context.</p><p>In this post, I’ll show how I built a maritime risk “Company Brain” on Microsoft Fabric: an ontology-driven AI experience that connects live operational signals, semantic business relationships, and trusted policy documents so a <a href="https://learn.microsoft.com/en-us/fabric/data-science/concept-data-agent">Fabric Data Agent</a> can answer risk questions in real time.</p><h3>The disconnect between dynamic risk and static rules</h3><p>Maritime insurance risk is constantly changing. A vessel’s location, speed, route, AIS status, nearby weather, cargo, and geopolitical exposure can all shift while the policy language governing that risk remains locked inside static PDF or Word documents.</p><p>That creates a practical gap for underwriters and risk analysts.</p><p>They may have the data, but the meaning is scattered across different systems:</p><p>- live AIS telemetry<br>- geospatial risk boundaries<br>- policy contracts and warranty clauses<br>- vessel, company, cargo, and policy master data<br>- analytical dashboards and operational alerts</p><p>Each system can answer part of the question. None of them, on its own, can answer the business question that matters:</p><p>Is this vessel exposed right now, what does the policy say, and what should we do about it?</p><p>That is the gap this demo is designed to close.</p><h3>The solution: a geospatial Company Brain</h3><p>The core idea behind the demo is simple: the AI agent should not guess, and it should not treat every question as a document search problem.</p><p>Instead, it should understand which source of knowledge is responsible for each part of the answer.</p><p>In this solution, Microsoft Fabric becomes the foundation for a maritime risk Company Brain: a connected intelligence layer that combines live vessel state, geospatial risk context, business relationships, and trusted policy language.</p><p>The result is an AI experience that can answer business questions such as:</p><p>- Where is this vessel right now?<br>- Is it inside a high-risk zone?<br>- Which company operates it?<br>- What cargo is it carrying?<br>- Which policy applies?<br>- What does the policy say about this situation?<br>- Is there a coverage issue or warranty breach?<br>- What action should the risk team take?</p><p>That is the shift: from a dashboard that shows live data to an agent that explains what live data means for the business.</p><h3>High-level architecture</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zognBoiycm5jAGZrwWgUDw.jpeg" /><figcaption>End-to-end architecture for the maritime risk Company Brain, built with Microsoft Fabric and Azure AI Search.</figcaption></figure><p>End-to-end architecture for the maritime risk Company Brain, built with Microsoft Fabric and Azure AI Search.</p><p>The architecture is centered on Microsoft Fabric, with Azure AI Search connected as the external knowledge retrieval layer for policy documents.</p><p>At a high level, the flow works like this:</p><p>1. Live AIS telemetry is received from <a href="https://aisstream.io/">AisStream.io</a> and published into <a href="https://learn.microsoft.com/en-us/fabric/real-time-intelligence/event-streams/overview">Fabric Eventstream</a>.<br>2. <a href="https://learn.microsoft.com/en-us/fabric/real-time-intelligence/eventhouse">Eventhouse</a> stores the real-time vessel stream and uses KQL functions, <a href="https://learn.microsoft.com/en-us/kusto/management/update-policy?view=microsoft-fabric">update policies</a>, and <a href="https://learn.microsoft.com/en-us/kusto/management/materialized-views/materialized-view-overview?view=microsoft-fabric">materialized views</a> to enrich each position with geospatial risk context.<br>3. <a href="https://learn.microsoft.com/en-us/fabric/data-engineering/lakehouse-overview">Lakehouse</a> stores the business master data: vessels, companies, cargo, policies, and maritime risk zones.<br>4. GeoJSON risk-zone files define polygons such as Red Zone and Yellow Zone boundaries. These are transformed into structured tables that Eventhouse can use for geospatial enrichment.<br>5. Power BI semantic model and <a href="https://learn.microsoft.com/en-us/fabric/iq/ontology/overview">Fabric ontology</a> describe the business relationships between vessels, operators, cargo, policies, and live vessel position.<br>6. Azure AI Search indexes policy documents so the agent can retrieve coverage, exclusion, warranty, deductible, and war-risk language.<br>7. <a href="https://learn.microsoft.com/en-us/fabric/data-science/concept-data-agent">Fabric Data Agent</a> orchestrates the answer by routing each part of the question to the right source.<br>8. <a href="https://learn.microsoft.com/en-us/fabric/real-time-intelligence/map/create-map">Fabric Map</a> and Activator provide the visual and operational layers: live geospatial monitoring and alerting when a vessel enters a critical zone.</p><p>The key design choice is the vessel entity. The vessel, identified by MMSI, becomes the bridge between the operational world and the business world. Its latest position comes from Eventhouse, while its operator, cargo, policy, and coverage context come from the semantic model and ontology.</p><p>That lets the Data Agent answer questions that require both real-time state and business meaning.</p><h3>How the solution works</h3><p>After the architecture is in place, the implementation follows a simple flow: ingest live vessel telemetry, enrich it with geospatial risk context, connect it to business entities, and ground policy answers in indexed documents.</p><p>The live vessel data comes from AisStream.io, which provides a high-frequency feed of real-world AIS telemetry broadcast by ships across the world’s oceans. Each vessel is identified by an MMSI, or Maritime Mobile Service Identity: a unique 9-digit identifier that works like a digital license plate for a ship. In this solution, MMSI is the key that connects live telemetry in Eventhouse to the vessel entity in the ontology.</p><p>A Fabric notebook connects via WebSocket to AisStream.io and publishes live AIS events into Fabric Eventstream. Eventstream lands the telemetry in Eventhouse, where the raw maritimeAIS table stores vessel position, speed, heading, MMSI, ship name, and ingestion time.</p><p>Inside Eventhouse, KQL functions enrich each incoming position with geospatial risk context. The solution evaluates whether a vessel is inside a defined polygon using geo_point_in_polygon, then adds fields such as IsInZone, ZoneName, RiskLevel, and PremiumMultiplier. The enriched stream is stored in maritimeAIS_Enriched.</p><p>To make current-state questions fast and reliable, the solution creates the LatestShipPositionsEnriched materialized view. This view keeps only the most recent enriched position for each vessel, keyed by MMSI.</p><p>In parallel, the Lakehouse stores the business master data: vessels, companies, cargo, policies, and maritime risk zones. The risk zones are defined as GeoJSON files, transformed into a structured maritimezones table, and used by Eventhouse for geospatial enrichment.</p><p>The semantic model and Fabric ontology then connect the vessel to the business context around it: operator, cargo, policy, and latest live position.</p><p>Finally, policy documents are indexed in Azure AI Search so the Data Agent can retrieve relevant language about coverage, exclusions, warranties, deductibles, and war-risk conditions.</p><p>Together, these components allow the agent to move from a live vessel position to a business-level risk answer: where the vessel is, whether it is inside a risk zone, who operates it, what cargo it carries, which policy applies, and what the policy says about the current situation.</p><h3>Why Ontology matters</h3><p>One of the most important lessons from this demo is that enterprise AI needs more than retrieval.</p><p>RAG is powerful, but document search alone does not know which document matters. A policy clause may look relevant, but the agent still needs to know which policy belongs to which vessel, who operates that vessel, what cargo it carries, and whether the vessel is currently inside a risk zone.</p><p>That is where the ontology becomes critical.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yZPglL2kq6UbagD7Bs_DHw.png" /><figcaption>Fabric ontology overview, with the vessel as the central entity connecting companies, cargo, policies, and live operational context.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BTNnrgo3mGWTYH3acCkqHg.png" /><figcaption>A focused ontology graph showing how simulated vessels are linked to their operator, cargo, and insurance policy.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MhmUSq-NabtePY-iVZyTwQ.png" /><figcaption>Expanded ontology graph across the fleet, making entity relationships visible and queryable through the Fabric ontology experience.</figcaption></figure><p>In this architecture, the vessel is the central business entity. It connects the real-time operational world to the business and contractual world:</p><p>- latest vessel position from Eventhouse<br>- operator from the company master data<br>- cargo from the cargo master data<br>- policy from the insurance master data<br>- risk-zone status from geospatial enrichment</p><p>For example, if a user asks:</p><p>Is EAST_SIMULATED_TANKER_02 covered while crossing the Red Zone?</p><p>The agent should not start by searching all documents. It should reason through the business graph:</p><p>1. Find the vessel’s latest location in Eventhouse.<br>2. Check whether IsInZone is true and identify the RiskLevel.<br>3. Resolve the vessel to its company, cargo, and policy through the ontology.<br>4. Retrieve the relevant policy clauses from Azure AI Search.<br>5. Synthesize a grounded answer.</p><p>This is the difference between an AI assistant that can search and an AI agent that can understand context.</p><h3>Building the Data Agent</h3><p>The Fabric Data Agent is the orchestration layer for the experience.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BtnDA7O8IKdEfz92sc1gSg.png" /><figcaption>Fabric Data Agent answering a coverage question by combining Eventhouse telemetry, ontology context, and policy clauses from Azure AI Search.</figcaption></figure><p>In this demo, the agent is grounded in three complementary sources of knowledge:<br>- Eventhouse answers live operational questions: where the vessel is, whether it is inside a risk zone, and what the current risk state is.<br>- Fabric ontology and semantic model answer business-context questions: who operates the vessel, what cargo it carries, and which policy applies.<br>- Azure AI Search answers policy-evidence questions: what the insurance documents say about coverage, exclusions, warranties, deductibles, and war-risk conditions.</p><p>If the user asks where a vessel is, the agent should use Eventhouse.<br>If the user asks who owns a vessel or which policy applies, it should use the ontology.<br>If the user asks whether something is covered, excluded, or subject to a deductible, it should use Azure AI Search.<br>For complex questions, it should chain all three.</p><p>For example:</p><p>Is MV Ocean Voyager covered for a drone strike right now?</p><p>The expected reasoning path is:</p><p>1. Query LatestShipPositionsEnriched to find the vessel’s current location and zone status.<br>2. Use the ontology to find the policy linked to that vessel.<br>3. Search the policy index for drone strike, war exclusion, and high-risk zone language.<br>4. Return a grounded answer that cites both the live zone and the relevant contract clause.</p><p>The important point is that the agent is not inventing geospatial boundaries or legal interpretations. It is grounded in the live KQL materialized view, the ontology, and the indexed policy documents.</p><h3>Real-time risk in action</h3><p>For the demo, I used both real AIS data and simulated vessels.</p><p>The real AIS stream demonstrates live ingestion into Eventhouse. The simulation notebooks create controlled vessel itineraries that intentionally cross defined risk zones, which makes it easier to demonstrate how the risk state changes over time.</p><p>Two simulated vessels are included:</p><p>- SIMULATED_TANKER_01<br>- EAST_SIMULATED_TANKER_02</p><p>Each simulated vessel is enriched with demo insurance context such as company, cargo, hull value, and policy ID. One vessel is associated with a Hull &amp; Machinery policy, while another is associated with a War Risk policy.</p><p>As the simulated vessels move through the Strait of Hormuz scenario, the Data Agent’s answers change.</p><p>When a vessel is outside a risk zone, the agent can report standard coverage and no active breach. When the same vessel enters the Red Zone, the agent can flag elevated exposure, identify the critical zone, apply the premium multiplier, retrieve the relevant policy language, and explain the potential warranty or coverage impact.</p><p>This is the key experience: the same business question receives a different grounded answer because the operational reality changed in real time.</p><p>That is what makes the pattern powerful. The agent is not just answering from static knowledge; it is reasoning over the current state of the business.</p><h3>Visualizing live maritime risk with the Fabric Map item</h3><p>The Fabric Map item provides the spatial operating picture for the demo.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K_1Wh78kARgsN7_zc9HYQA.png" /><figcaption>Fabric Map visualizing vessel positions and maritime risk-zone polygons over a satellite-style map of the Strait of Hormuz.</figcaption></figure><p>While the Data Agent answers natural-language business questions, the map lets users see the same risk scenario visually: live vessels moving across geographic boundaries, risk-zone polygons, and changing exposure over time.</p><p>In this solution, the map brings together three layers:</p><p>- live vessel positions from Eventhouse<br>- maritime risk-zone polygons from Lakehouse GeoJSON data<br>- business context from the ontology, where each vessel connects to operator, cargo, policy, and current location</p><p>This is especially useful for real-time risk monitoring. An underwriter can inspect vessels approaching a high-risk polygon, filter by operator or cargo type, zoom into a corridor such as the Strait of Hormuz, and then ask the Data Agent a grounded follow-up question about coverage, warranties, or financial exposure.</p><p>In other words, Fabric Map provides the spatial view, while the ontology and Data Agent provide the business meaning behind what the user sees.</p><h3>From dashboard to decision</h3><p>Dashboards are still valuable. They show what is happening.</p><p>But the Data Agent adds a different interaction model. It helps users ask what the situation means.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sbHOpJ37MwunafcTi06CBA.png" /><figcaption>Natural-language risk analysis and geospatial exploration side by side: the Data Agent explains the risk while the Map shows where it happens.</figcaption></figure><p>A dashboard can show that a vessel is in a particular location. An ontology-driven agent can connect that location to the vessel’s operator, cargo, policy, current risk zone, and relevant contractual language.</p><p>That distinction matters for underwriters and risk teams. They do not only need to see that something happened; they need to understand whether it creates a coverage issue, whether the cargo changes the severity, whether the operator has a high-risk profile, and whether the policy includes a relevant exclusion or warranty condition.</p><p>With the ontology-driven approach, the agent can connect those dots and turn live telemetry into a business-level risk explanation.</p><h3>Adding operational alerts with Activator</h3><p>The solution also includes an Activator scenario, represented in the repository as a Reflex item called RedAlertActivator.</p><p>This adds an operational layer to the pattern. Once live data is enriched with geospatial and business meaning, it can trigger action rather than only support analysis.</p><p>For example, when a vessel enters a critical zone, the system can move beyond passive monitoring and activate an alerting flow.</p><p>That creates a practical enterprise pattern:</p><p>1. Eventstream ingests live data.<br>2. Eventhouse enriches the data in real time.<br>3. Materialized views expose the latest risk state.<br>4. Power BI and Fabric Map visualize the fleet.<br>5. Data Agent explains the business meaning.<br>6. Activator triggers operational action.</p><p>This is where the Company Brain becomes more than a question-answering interface. It becomes part of the operating model.</p><h3>Example questions for the Data Agent</h3><p>Here are some of the prompts I used to demonstrate the solution:</p><blockquote>Where is SIMULATED_TANKER_01 right now, and is it in a risk zone?</blockquote><blockquote>What is the total risk assessment for EAST_SIMULATED_TANKER_02?</blockquote><blockquote>Is this vessel in breach of any policy warranty?</blockquote><blockquote>Which of my ships are currently inside a high-risk zone?</blockquote><blockquote>What does the policy say about war-risk zones for this vessel?</blockquote><blockquote>Who operates this vessel, what cargo is it carrying, and which policy applies?</blockquote><p>These prompts demonstrate different agent behaviors.</p><p>Some are pure live telemetry questions. Some are ontology traversal questions. Some are policy retrieval questions. The most interesting ones require the agent to orchestrate all three.</p><h3>What I learned building it</h3><p>The biggest learning from this demo is that the value of enterprise AI does not come only from the model.</p><p>It comes from grounding the model in the right architecture.</p><p>A strong enterprise AI architecture needs:</p><h3>Live operational context</h3><p>For this demo, that context came from AIS telemetry streaming through Eventstream into Eventhouse.</p><h3>Semantic business context</h3><p>The ontology gave the AI a way to understand relationships between vessels, companies, cargo, policies, and zones.</p><h3>Trusted knowledge</h3><p>Azure AI Search gave the agent access to policy documents and legal clauses without forcing the user to manually read through dense contracts.</p><h3>Clear routing and orchestration</h3><p>The Data Agent needed explicit instructions about when to use Eventhouse, when to use the ontology, and when to use AI Search.</p><h3>Real-time state</h3><p>The LatestShipPositionsEnriched materialized view was essential because risk assessment depends on the latest vessel position, not historical telemetry.</p><p>Together, these pieces create something much more useful than a generic chatbot.</p><p>They create an AI agent that can reason over the business.</p><h3>Why this pattern matters</h3><p>Although this demo focuses on maritime insurance, the pattern applies to many industries.</p><p>In manufacturing, the central entity might be a machine or production line.</p><p>In healthcare, it might be a patient journey.</p><p>In retail, it might be a product, supplier, or store.</p><p>In energy, it might be an asset, grid node, or maintenance event.</p><p>The common pattern is the same:</p><ol><li>Capture live operational signals.</li><li>Enrich them with domain-specific logic.</li><li>Model business entities and relationships.</li><li>Ground the AI in trusted documents.</li><li>Expose the experience through a natural-language agent.</li><li>Trigger alerts or actions when business conditions change.</li></ol><p>That is the essence of the Company Brain.</p><p>It is not just an AI assistant connected to data. It is an AI layer that understands how the business is connected.</p><h3>Final thoughts</h3><p>The Fabric IQ demo at the Microsoft AI Tour was a great opportunity for me to show how Microsoft Fabric can bring together real-time analytics, semantic modeling, ontology, document intelligence, and AI agents into a single architecture.</p><p>The most exciting part is the shift in user experience.</p><p>Instead of asking analysts to manually inspect telemetry, dashboards, policies, and risk rules, we can let them ask business questions directly:</p><blockquote>“Which vessels are exposed right now, why, and what does the policy say?”</blockquote><p>That is the promise of ontology-driven AI.</p><p>When your data platform understands your business entities, your AI agent can do more than retrieve information. It can reason across live events, business relationships, and trusted knowledge.</p><p>That is how we start building the Company Brain.</p><h3>Resources</h3><p>The full demo repository is available here:</p><p><a href="https://github.com/dorirabin/fabricdemo/tree/fabriciq-maritimedemo">https://github.com/dorirabin/fabricdemo/tree/fabriciq-maritimedemo</a></p><p>The repo can be automatically deployed in your Fabric Workspace using the <a href="https://microsoft.github.io/fabric-cicd/latest/">fabric cicd </a>python library <br>The full demo repository includes the Fabric assets, notebooks, ontology, Data Agent configuration, sample policy documents, reports, maps, and Activator scenario used to build the solution.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c5eb14548790" width="1" height="1" alt=""><hr><p><a href="https://medium.com/microsoft/from-live-signals-to-smarter-risk-building-a-company-brain-on-microsoft-fabric-c5eb14548790">From Live Signals to Smarter Risk: Building a Company Brain on Microsoft Fabric</a> was originally published in <a href="https://medium.com/microsoft">Microsoft</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>