Microsoft Entra ID under the hood

Recently I saw a Microsoft Entra ID architecture poster on LinkedIn, put together by Srawon Kumar Reddy Mula, and it is a good one: six numbered steps across the top, an architecture panel underneath, security and governance columns down the side. User signs in, request goes to Entra ID, authenticate, evaluate access, issue token, access application. A dashed arrow loops back from the last box to the third and carries the label “token renewal / continuous access evaluation”.

(Source: LinkedIn post by Srawon Kumar Reddy Mula)

And I was interested in how that actually works, because the diagram does not really show it. One dashed arrow covers a lot of ground. How long does that token live? What happens to it when you revoke someone? Does the loop do anything at all if the client never asked for it?

So I went and found out. I built the flow against a real tenant, one working sample per box, and measured the parts the poster fits into an icon. Nothing on the poster is wrong, and none of what follows is a correction. It is the answer to a question a picture that size cannot hold.

Two comments under the original post were asking better versions of the same question. Ernie Prescott pointed out that “Hybrid Identity with Active Directory” was sitting quietly in the highlights list. It was doing far more work than it looked like. Sajeed Mullaji wrote that PIM only solves the activation window, not the payload. Both of them named a boundary the picture compresses, so both of them got a sample.

Six samples, one per box

The repository is at github.com/steefjan1/entra-id-end-to-end. Six samples, one per box, every one of them run against a real tenant.

The dashed arrow is doing the most work on the whole poster

Continuous access evaluation gets one dashed line and one label. In practice it is a narrow feature with a wide reputation.

It covers five critical events: the account is deleted or disabled, the password changes, MFA is enabled for the user, an administrator revokes all refresh tokens, or Identity Protection detects high user risk. It reaches Exchange Online, SharePoint Online, Teams and Microsoft Graph, and even that list needs footnotes. The client matrix marks Teams partially supported in every row. SharePoint Online does not support the user risk event. Azure Resource Manager is not on the list at all. And none of it reaches a client that did not ask for it. Asking means declaring the cp1 capability, so that Entra ID treats the session as CAE aware.

Two clients, one line of configuration apart

That last part is a single line of MSAL configuration, and everything hangs off it. So the first sample in the repository measures the difference rather than describing it. It signs the same user in twice against Graph, once with clientCapabilities: ['cp1'] and once without. Then it revokes the sessions and polls with both tokens until each one stops working.

Here is what that returned on my own tenant this morning. Client A, the one declaring cp1, came back with a token good for 1439 minutes. Client B, identical except for that one line, came back with 65 minutes. Both sit exactly where the documentation says they should: 20 to 28 hours for a continuous access evaluation session, and the randomized 60 to 90 minute band for an ordinary one.

Then I revoked the user’s sessions.

Client A stopped working four seconds later. I know it was four seconds rather than roughly, because the claims challenge carries the timestamp: the nbf value Entra ID sent back decodes to 07:57:38Z and Graph turned the token away at 07:57:42.

Client B kept answering 200 for the rest of its 65 minutes. Revoking sessions invalidates refresh tokens and browser cookies. Microsoft’s own guidance on removing a user’s access says the rest: for applications using access tokens, the user loses access when the access token expires. Nothing in the tenant shortens that window.

The challenge is a timestamp, not a capability

The challenge itself is worth a look, because it is not the one most articles show:

{"access_token":{"nbf":{"essential":true,"value":"1787731058"}}}

Not a capability negotiation. A timestamp. The resource is saying: your token predates the revocation instant, bring me one issued after it. essential: true means the client does not get to negotiate.

Which is the whole of that dashed arrow, drawn out. Notice what the client does with the challenge: it clears its cache, asks again with the claims parameter, and gets a fresh token. The user sees none of it. That is the part worth implementing. It is also why declaring cp1 without handling the challenge is worse than not declaring it at all: the client will loop, retrying a token the resource has already told it to replace.

Run that once against your own tenant and the dashed arrow stops looking like a safety net.

“Evaluate access” is not continuous, and that is the part people get wrong

One passage from Microsoft’s own documentation deserves a place on that poster more than anything already on it: policies targeting roles or groups are evaluated only when a token is issued, and if a user already has a valid token before being added to the role or group, the policy does not apply retroactively.

Read that against how offboarding usually works. You remove someone from a group. The group was the thing granting access. You now believe access is gone. It is not, because the token in the client’s cache does not care about your group change, and continuous access evaluation does not cover group membership. Microsoft documents that replication as taking up to one day. There is an optimization that brings it down to two hours. It applies to policy updates rather than to group membership, and the documentation says plainly that it does not cover all scenarios yet. Their own recommended workaround is to revoke the user’s sessions by hand.

The same applies to a new Conditional Access policy and to a role assignment. Which is a strange thing to discover during an incident.

Where authority actually lives

Ernie’s point deserves the space. In a hybrid tenant, the box marked “authenticate” is not where the answer comes from. It comes from wherever the account state currently lives, and the delay between the two directions is not symmetric.

With password hash synchronization, disabling an account in Active Directory does not immediately end cloud access. Microsoft puts the window at up to 30 minutes, and adds that sync never carries password expiry or account lockout state to Entra ID at all. Pass through authentication and federation enforce those states at sign in, immediately. That is a real architectural difference hiding behind a single line item in a highlights list.

The other direction has no delay because it has no mechanism. Neither Connect Sync nor Cloud Sync provisions a user disable back to Active Directory. Disable the cloud account and Kerberos, NTLM and LDAP access on premises continues exactly as before. Conditional Access does not sit in that path natively. Entra Private Access for domain controllers went GA in January 2026. It is the way to put policy in front of Kerberos, and it works by putting sensors on the domain controllers rather than by extending the token flow.

There is a clock on Connect Sync now

There is a clock attached to this now. Microsoft published a phased transition plan from Connect Sync to Cloud Sync. From July 2026 it starts telling tenants their individual transition windows through the Message Center, Connect Health and targeted email. The tenants where Cloud Sync already covers everything go first. Source of authority conversion for individual users went GA in January 2026. The Active Directory to Entra ID boundary is moving under people while they are still drawing it as one arrow.

Sample 05 in the repository does not fix any of this. It measures it. The report lists every principal by where its authority lives, and flags synced objects whose last sync is stale. It also names the case that should worry you most: a principal whose authority is on premises but whose privilege is in the cloud.

It also ships a coverage matrix that contacts nothing and takes a second to read. Six controls down the side, five surfaces across the top, and the column for on-premises Kerberos, NTLM and LDAP reads no from top to bottom. Conditional Access, MFA, continuous access evaluation, sign-in risk, device compliance, sign-in logs. Not one of them reaches that surface natively. That column is the highlights-list bullet, drawn honestly.

PIM controls the window, not the payload

Sajeed’s comment is the best one line summary of privileged access I have read this year, so I built a script around it.

PIM answers when a role is active. It says nothing about what the role can do while it is. An eligible assignment with a one hour activation limit, MFA on activation and an approval step looks like a strong control on a dashboard. If the role behind it grants four hundred actions including application credential management, you have not reduced the blast radius. You have scheduled it.

So sample 04 prints both numbers on the same row. Per assignment it prints the activation maximum duration, and whether the role requires MFA and approval. Next to that sits the number of resource actions the role definition actually grants. Then a payload risk verdict that flags wildcards, and a list of high impact actions.

What the report found on my own tenant

I ran it on my own lab tenant expecting to demonstrate a point. It found something instead. Two service principals, one of them a monitoring integration, both holding Directory Writers. That role carries microsoft.directory/servicePrincipals/appRoleAssignedTo/update, which grants application permissions to any application in the tenant. It also carries microsoft.directory/groups/members/update. Neither assignment expires. Nobody would think to look at a monitoring identity when auditing privilege. No PIM dashboard would show it as a problem either, because PIM is not involved at all.

That tenant has no P2, which turned out to matter in an instructive way. The report falls back to the plain role assignment endpoint and prints the window columns as unavailable. The action counts do not change, because a role definition grants what it grants however the assignment happened. A tenant with no PIM is the more alarming case, not the less: every assignment is standing, permanent, with no window to shorten.

Writing that fallback also caught a bug in my own scoring. Entra ID writes its largest wildcard in words rather than asterisks. Global Administrator’s payload includes microsoft.directory/allEntities/allProperties/allTasks, and matching wildcards on * alone ranked Global Administrator below a billing role on a naive action count. In a script whose entire purpose is measuring the payload, the measurement inverted the ranking at exactly the row that mattered most.

Sajeed made the point about ERP duties, and he is right that Entra ID cannot see inside the application. That is the honest boundary of this work. Sample 04 reports Entra’s half. The duty separation inside your finance system is a separate project with separate tooling. Pretending PIM covers it is how audits get closed without anything getting safer.

Step one says “user signs in”

In a real Azure tenant, most sign-ins are not users. They are service principals, managed identities, and pipelines.

Almost none of the controls in the diagram apply to them the way people assume. Conditional Access for workload identities covers single tenant service principals registered in your tenant. It does not cover managed identities at all. It does not cover multitenant or Microsoft applications. Its conditions cover location, service principal risk and authentication context, and block is the only grant control it offers. If your plan was to require MFA for a pipeline, there is no such thing. Continuous access evaluation for workload identities reaches Microsoft Graph and nothing else.

What you can do is remove the secret. Sample 06 deploys workload identity federation, so a GitHub Actions workflow reaches Azure with no stored credential at all. It also ships an inventory script. That script ranks every workload identity in the tenant by credential type, days to expiry and the application permissions it holds on Graph.

What 348 service principals look like

Run against my lab tenant it found 348 service principals, 246 of them Microsoft first party, leaving 102 worth inspecting. Twenty seven of those are managed identities. That is the number behind the claim at the top of this section. One person, one lab, and a hundred non human principals authenticating, without anybody ever having thought about them as sign-ins.

The report was also wrong twice, and both mistakes are instructive. Graph returns signInAudience as null for a managed identity, and null !== undefined, so my check labelled all twenty seven of them multitenant. And Azure rotates a managed identity’s certificate itself, leaving stale entries on the service principal, so the report announced five certificates that expired two thousand days ago as high risk. Two categories of confident nonsense, both crowding out the one row in that tenant that genuinely mattered: a multitenant application holding Directory.ReadWrite.All and User.ReadWrite.All.

A managed identity has no credential you rotate and no Conditional Access at all. Its entire blast radius is its permissions and its Azure RBAC scope. I made the same argument from the other end when I looked at Logic Apps agent loop security: once the identity is managed for you, the permissions are the only surface left to get wrong. Which is the argument for reading the permissions column rather than the expiry column. I had to be wrong in public on my own tenant to see it.

Conditional Access deserves the same pipeline as your code

The shield in the middle of the poster is a set of policies someone clicked together in a portal. Conditional Access is the tenant wide half of authorization. The per API half I covered separately in securing AI APIs with authentication and authorization in Azure API Management. Neither one substitutes for the other. No history, no review, no test, and no way to answer “what breaks if I enable this” other than enabling it.

Sample 03 treats it as code. Policies are JSON files with placeholders instead of hard coded object IDs. A guard refuses to ship a policy that targets users, carries a grant control and does not exclude the break glass group, because Conditional Access has no “except the person who wrote it” fallback. Everything deploys in report only unless you opt in twice.

The part I would steal even if you ignore the rest is the test runner. Graph exposes the What If evaluation as an API, so every pull request can check a set of hypothetical sign-ins against your live policies. The most valuable assertion in the file is the one that says no policy of your own catches the break glass account. Run it forever.

Where this is the wrong answer

Not all of this is worth doing.

If you run a small tenant with a handful of policies and one administrator, the portal is fine. A Conditional Access pipeline is machinery you will maintain instead of using. Without Entra ID P2, PIM and access reviews are not a decision you get to make, and standing assignments with a tight scope plus an honest quarterly review beat pretending otherwise. If you are already on Cloud Sync and everything works, the 2026 migration is not a project you need to start this quarter.

And the measurement scripts come with a warning I will repeat here. The revocation stopwatch revokes a real person’s sessions. The propagation watcher requires you to disable a real account. Tell the person first.

This is the same conclusion I keep arriving at, and I wrote up how I got here in my Azure security journey. The one thing I would not skip, whatever size you are, is running the two read only reports. The entitlement report in sample 04 and the sync report in sample 05 need no licence beyond what you already have. They change nothing. And they answer questions about your own tenant that the diagram cannot.

The Microsoft Entra ID samples repository

Everything above ships as working code at github.com/steefjan1/entra-id-end-to-end, MIT licensed. Six samples, each with its own README, its own permissions list, and its own section on where it is the wrong answer. Every write supports a dry run. Nothing deletes anything.

I deployed sample 01 and signed into it. Sample 06 compiles clean and I have not deployed it yet, which is exactly the kind of thing a diagram would let me leave out and a README should not.

Five defects that only showed up on deployment

Deploying it for real was worth more than writing it. Five defects turned up that no amount of template validation would have caught. The repository documents every one, with the actual error text:

  • Entra ID validates preAuthorizedApplications against the scopes that already exist on an app, not the ones you create in the same request. Exposing a scope and pre-authorizing a client for it has to be two calls with a wait in between.
  • az writes to stderr for entirely ordinary things. Windows PowerShell under ErrorActionPreference = 'Stop' treats that as fatal, which killed the same script three times for three unrelated reasons.
  • The execution policy on a normally configured Windows machine blocks an unsigned .ps1 azd hook. I stopped using PowerShell for hooks at all.
  • Node 20 reached end of life in April 2026. App Service quietly substitutes the nearest LTS it actually has in your region, rather than failing.
  • And one of my own. I read an azd timeout warning as a deployment failure, and changed a working deployment strategy on the strength of it. The app had already started successfully two minutes after azd stopped watching. That sample’s README writes it up under a heading telling you not to make it.

The number on my screen while writing this

The token in front of me as I write this carries 75 minutes, the exact average Microsoft documents for the randomized 60 to 90 minute window. It is a small thing to see the number rather than read it. It is also the entire argument of this post in one line.

Run any of it against your own tenant. If it does not behave the way I have described, that is worth an issue rather than a comment thread. The whole point of putting code behind an architecture picture is that the picture can then be wrong out loud.

Azure Governance and Identity for Integration Architects

In the Azure PaaS map post, governance and identity got a paragraph and a firm line: managed identity everywhere, secrets in Key Vault, posture visible. That paragraph is the one most PaaS write-ups skip; they cover compute, messaging, and data, then wave at security on the way out.

For an integration platform in a regulated industry, though, this layer isn’t the afterthought. It’s the part that decides whether the thing can run at all. So this post gives it the space the map couldn’t. Azure governance and identity are where a proof of concept becomes something an auditor will sign off on, and where many otherwise good architectures quietly fail their first compliance review.

The two jobs this layer does

Governance and identity sounds like one concern. In practice it’s two, and keeping them apart makes the design clearer.

Identity answers who. Who is this caller? What are they allowed to reach? This is Entra ID, managed identities, and role assignments the machinery that authenticates and authorizes every hop in the platform.

Governance answers whether and how. Is this configuration allowed? Can we prove it stayed allowed? This is Azure Policy, Defender for Cloud, and the audit trail the machinery that constrains what the platform can be and evidences it after the fact.

An integration platform needs both. Identity without governance gives you a secure platform you can’t prove is secure. Governance without identity gives you a well-documented platform anyone can walk into. So let’s take each in turn, then the compliance frame that ties them together.

Identity: managed identity as the default

Here’s the single most important rule in this layer. Every service authenticates as itself, with no secret in configuration. That’s what managed identity gives you, and it’s the foundation everything else sits on.

Without it, you’re back to connection strings and API keys scattered across app settings and config files. Each one is a secret that can leak, expire, or get committed to a repo by accident. With managed identity, the platform issues each service an identity in Entra ID, and that identity authenticates directly to Key Vault, SQL, Service Bus, and Storage. No secret changes hands. Nothing to rotate, nothing to leak.

A few design points that matter for integration specifically:

  • System-assigned or user-assigned? A system-assigned identity is tied to one resource and dies with it. A user-assigned identity is a standalone resource you attach to many. For an integration platform with several services that need the same access, a user-assigned identity is usually cleaner: you grant permissions once and attach it everywhere, rather than managing a dozen separate grants.
  • Least privilege, per service. Managed identity makes authentication clean, but authorisation is still on you. Each service should hold exactly the roles it needs and nothing more. The integration service that reads from Service Bus doesn’t need write access to the whole storage account. So scope the role assignments tightly, because a broad grant is a standing risk long after anyone remembers making it.
  • RBAC over access policies. Where a service supports Azure RBAC for its data plane, Key Vault now prefers it over the older per-resource access policy model. RBAC gives you one consistent permission model across the platform, which is far easier to audit than a patchwork of resource-specific policies.

Identity: Entra ID and the human side

Managed identity handles service-to-service. Entra ID also governs the human and external edges of the platform.

For inbound calls, Entra ID handles authentication and authorisation validating tokens, enforcing scopes, and applying conditional access where the risk warrants it. App Service and API Management both integrate with it directly, so the platform can offload the whole OIDC flow rather than hand-rolling token validation. That’s less code and, more to the point, less code to get wrong.

The honest note: Entra ID’s built-in flows are excellent for standard cases and awkward for unusual ones. If your authorization logic is genuinely complex, with per-tenant rules, dynamic scopes, and fine-grained resource permissions, understand where the platform’s built-in handling stops and your own logic has to begin. Leaning on Easy Auth for something it wasn’t designed for is a common way to end up with authorization gaps you don’t discover until an audit.

Secrets: Key Vault for what can’t be an identity

Managed identity removes most secrets. It doesn’t remove all of them. Third-party API keys, certificates, signing keys these can’t be a managed identity, so they need somewhere safe to live. That’s Key Vault.

The pattern is straightforward. Secrets live in Key Vault, and services read them at runtime using their managed identity. No secret sits in configuration; the app holds a reference, and the platform resolves it. As a result, rotating a secret is a Key Vault operation, not a redeploy.

For a regulated integration platform, Key Vault also stores the certificates that underpin private connectivity and signing, and it provides an access log of every secret read, which matters more than it sounds, because “who accessed this key and when” is a question auditors actually ask.

Governance: policy, posture, and the audit trail

Identity secures the platform. Governance proves it and constrains it so it can’t drift out of compliance.

Azure Policy enforces the guardrails. Policy lets you assert rules across the platform and block or flag anything that violates them. No public endpoints. Encryption required. Approved regions only, which matters directly under data-residency rules. Policy is what stops a well-meaning change from quietly breaking a compliance requirement, because it refuses the change rather than trusting everyone to remember the rule.

Defender for Cloud gives you posture. It surfaces misconfigurations, missing controls, and active threats across the platform, and scores you against benchmarks. For an integration platform touching regulated data, that continuous posture view is the difference between finding a gap yourself and having an auditor find it for you.

The audit trail evidences everything. Azure Monitor and the activity log record what changed, who changed it, and when. Under most compliance regimes, you don’t just have to be secure; you have to prove you were secure, continuously, over time. The audit trail is that proof. So wire it in from the start, because you can’t reconstruct an audit trail you didn’t capture.

The compliance frame: why this layer is non-negotiable

Everything above applies to any serious platform. In a regulated Dutch healthcare context, though, the frame is sharper, and it’s worth naming the constraints that turn best practices into requirements.

Under the AVG, the Dutch implementation of the GDPR imposes strict obligations on the handling, minimization, and residency of personal data. Approved-region policy, tight role scoping, and the access trail stop being good hygiene and become the evidence you’re meeting those obligations. NEN 7510, the Dutch standard for information security in healthcare, adds specific controls around access management and traceability that map almost directly onto Entra ID role assignments and the audit trail. DORA brings operational-resilience and third-party-risk requirements that lean on the same posture and logging foundations. And the EU AI Act, where the platform touches AI workloads, layers on transparency and oversight duties that again rest on identity and audit.

The through-line: these regimes don’t ask for exotic new machinery. They ask you to apply identity, policy, and audit rigorously, and to prove it. So the governance layer isn’t compliance overhead bolted onto the architecture. Done right, it is the compliance posture, expressed as configuration.

Where this layer gets over-applied

Consistent with the series the honesty section. Rigour in this layer is right; ceremony for its own sake is not.

  • Not every secret needs a Key Vault reference if it isn’t a secret. A non-sensitive configuration value doesn’t belong in Key Vault just because everything else does. Reserve it for things that actually need protecting, or you bury the real secrets in noise.
  • Not every workload needs the strictest tier of every control. A platform handling public reference data doesn’t need the same isolation and scrutiny as one touching patient records. Match the rigour to the data classification, rather than applying maximum controls uniformly and paying for it in friction everywhere.
  • Policy that only flags is policy that gets ignored. A pile of advisory policies nobody acts on is worse than a few enforced ones, because it creates the appearance of governance without the substance. Enforce what matters; don’t drown the signal.

The shape of it

For an integration architect, Azure governance and identity are the layer that decides whether the platform is allowed to exist, not just whether it works. Managed identity removes the secrets. Entra ID governs who gets in. Key Vault holds what’s left. Policy constrains the platform, Defender watches it, and the audit trail proves it continuously, the way every regime from AVG to the EU AI Act actually demands. Get this layer right, and compliance stops being a gate you dread. It becomes a property the architecture already has.

Want the layer this sits inside? The Azure PaaS map puts governance in context against compute, integration, and data, and walks the five-question framework across all of them.

Registering a Citadel Agent in the AI Registry: Azure API Center

This is the final post in a five-part series on the Microsoft Foundry Citadel Platform about Azure API Center AI agent registration. If you landed here first, a quick catch-up helps.

In Part 1, I deployed the Citadel Governance Hub (APIM, Azure OpenAI, Content Safety, Cosmos DB, Logic App) and an Agent Spoke (AI Foundry, Cosmos DB, Key Vault, App Config) in Sweden Central. Next in Part 2, I connected a tool-calling agent through APIM using the standard OpenAI SDK, with the Open-Meteo weather API as its only tool. In Part 3, the agent started writing every run to a conversations container in the spoke’s Cosmos DB. In Part 4, I built a three-layer kill switch so the platform team could shut an agent down fast when something went wrong.

By the end of Part 4, I had a working agent with a gateway, an audit trail, and an off switch. But I still had a gap that only shows up once you stop thinking about one agent and start thinking about a fleet of them: nobody outside my own head knew this agent existed.

That’s the problem this post solves.

Why register agents at all

One agent is easy to track. You know its name, its resource group, and roughly what it does, because you built it last week. Ten agents get harder. A hundred agents, spread across teams, spokes, and environments, becomes a governance problem rather than an inconvenience.

Platform teams eventually ask the same three questions about every agent in the estate: what does it do, what data does it touch, and who owns it. Without a registry, the answer lives in Slack threads, README files, and the memory of whoever built the thing. That doesn’t scale, and it definitely doesn’t survive an audit.

Azure API Center gives the Citadel Platform a place to answer those questions consistently, for every agent, in a format a platform team (or a compliance officer) can actually query. And as you’ll see later in this post, that same discipline lines up neatly with a transparency obligation that’s no longer theoretical: Article 49 of the EU AI Act.

Azure API Center 101

API Center and Azure API Management solve different problems, even though they sound similar and live in the same governance hub.

APIM is a runtime gateway. It sits in the request path, enforces policies, and does the actual work of routing, throttling, and inspecting traffic. That’s where the kill switch from Part 4 lives, and it’s where the five governance policies from earlier in the series get enforced.

API Center is a catalog. It doesn’t sit in the request path at all. Its job is to hold structured metadata about APIs and, in our case, agents: what they are, what version they’re on, who owns them, and what environment they run in. Think of APIM as the checkpoint and API Center as the registry office.

In the hub resource group rg-ai-hub-gateway-dev, the API Center instance is already deployed as apic-wpvlimv4ngkns, alongside APIM. That placement matters. The registry belongs in the hub because it needs to see across every spoke, not just the one running our weather agent.

The AI Publish Contract pattern

A generic API definition (an OpenAPI spec, for example) tells you the shape of the requests and responses. It doesn’t tell you whether the thing behind that shape is calling a third-party model, touching personal data, or making decisions that affect a person’s rights.

For agents, that gap matters more than it does for a plain REST API. So the Citadel Platform defines a small, structured metadata contract that every agent has to carry before it gets registered. I call it the AI Publish Contract. It rides alongside the API definition in API Center as custom metadata, rather than replacing the API definition entirely.

Here’s what it looks like for the weather agent:

{
"contractVersion": "1.0",
"agent": {
"id": "citadel-weather-agent-v1",
"displayName": "Citadel Weather Agent",
"description": "Tool-calling agent that answers weather queries using Open-Meteo as its only external tool.",
"owner": "platform-team@example.com",
"environment": "dev",
"spokeResourceGroup": "rg-ai-spoke-dev",
"aiFoundryProject": "aifp-tggi2gmkw22w4"
},
"modelBacking": {
"provider": "azure-openai",
"routedThrough": "apim-wpvlimv4ngkns",
"model": "gpt-4o-mini"
},
"tools": [
{
"name": "open-meteo-forecast",
"type": "external-api",
"endpoint": "https://api.open-meteo.com/v1/forecast",
"dataClassification": "public"
}
],
"dataClassification": "public",
"personalDataProcessed": false,
"highRiskCategory": false,
"killSwitchLayer": "named-value-flip",
"transparencyDisclosure": {
"aiActArticle49Applicable": false,
"userFacingDisclosureRequired": true,
"disclosureText": "This response was generated by an AI system."
}
}

A few fields deserve a comment, because I went back and forth on them.

dataClassification and personalDataProcessed exist because a registry that doesn’t record data sensitivity is only half a registry. The weather agent is a deliberately boring example: no personal data, no high-risk category, public weather data in and out. That’s exactly why it’s a good agent to demonstrate the pattern on before you register something that touches real user data.

transparencyDisclosure maps directly to the Article 49 discussion later in this post. Even for a low-risk agent, I keep the field in the contract rather than making it conditional. Consistency across every agent in the registry is the whole point.

Step-by-step: registering the weather agent in Azure API Center

With the contract defined, registration itself is a handful of steps. I’ll show the CLI first, then the portal equivalent, because I use both depending on whether I’m scripting a pipeline or walking a colleague through it live.

Prerequisites

You need an existing API Center instance (ours is apic-wpvlimv4ngkns in rg-ai-hub-gateway-dev) and Contributor access to it. If you’re following along, confirm the instance is visible first:

az apic show --resource-group rg-ai-hub-gateway-dev \
--name apic-wpvlimv4ngkns \
--subscription dc0f4d72-3734-4b03-8884-ccfb9c2c4cc7

Step 1: define the custom metadata schema

API Center lets you extend its metadata model with custom fields. Before registering any agent, define a schema for the AI Publish Contract so every future registration validates against the same structure.

az apic metadata create --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--metadata-name aiPublishContract \
--schema "{\"type\":\"string\",\"title\":\"AI Publish Contract\"}"
--assignments "[{\"entity\":\"api\",\"required\":true}]"

I keep ai-publish-contract-schema.json as a JSON Schema file that mirrors the structure shown above. Doing this once means every agent registered afterward gets validated the same way, instead of drifting field by field as different teams register their own agents.

Step 2: register the agent as an API

az apic api create --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--api-id citadel-weather-agent-v1 \
--title "Citadel Weather Agent" \
--type rest \
--custom-properties @C:\Users\steef\ai-citadel-agent\citadel-agent\ai-publish-contract.json

The --type rest flag looks like a mismatch at first, since this is an agent, not a REST API in the traditional sense. API Center doesn’t yet have a native “agent” type, so I register it as a REST API and let the AI Publish Contract metadata carry the agent-specific detail. That’s a workaround, not a limitation I’d defend as elegant, and I expect this to get cleaner as Microsoft extends API Center for agentic workloads.

Step 3: create the API version

az apic api version create --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--api-id citadel-weather-agent-v1 \
--version-id v1-0 \
--title "v1" \
--lifecycle-stage development

Step 4: attach the definition

az apic api definition create --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--api-id citadel-weather-agent-v1 \
--version-id v1-0 \
--definition-id openapi \
--title "OpenAPI"

Then import the actual spec, which describes the agent’s HTTP-facing invocation contract (the endpoint APIM exposes, not the internal tool-calling logic):

az apic api definition import-specification --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--api-id citadel-weather-agent-v1 \
--version-id v1-0 \
--definition-id openapi \
--format link
--value "https://apim-wpvlimv4ngkns.azure-api.net/weather-agent/openapi.json"
--specification "{\"name\":\"openapi\",\"version\":\"3.0.0\"}"

That failed immediately:

(ValidationError) Could not download the API specification from
the specified URL due to an error:
Could not reach the provided URL.
Please make sure that the specification file
is publicly available and that the link is valid and up-to-date.

My first assumption was a network or auth issue between API Center and APIM. It wasn’t. Curling the same URL directly returned a plain 404 from APIM itself:

{ "statusCode": 404, "message": "Resource not found" }

That 404 is the real signal. There is nothing published at that path, because there was never a dedicated API resource for the weather agent in APIM to begin with. Part 2 wired the agent up through APIM as a passthrough to Azure OpenAI, using the standard OpenAI SDK. That gets you routing, policy enforcement, and governance on every call, but it doesn’t give you a discoverable, documented API surface. A passthrough proxies requests. It doesn’t publish an OpenAPI spec describing them.

This is worth sitting with for a second, because it’s not just a command-line gotcha. It means the OpenAPI definition API Center wants isn’t something you can export from a running agent. You have to author it, the same way you’d write documentation, because the system underneath genuinely has nothing to hand you.

So the real fix has two parts. First, write a minimal OpenAPI spec that documents the agent’s actual invocation contract as it exists through the passthrough:

{
"openapi": "3.0.0",
"info": {
"title": "Citadel Weather Agent",
"version": "1.0.0",
"description": "Tool-calling agent invocation contract, routed through APIM to Azure OpenAI."
},
"servers": [
{ "url": "https://apim-wpvlimv4ngkns.azure-api.net" }
],
"paths": {
"/openai/deployments/{deploymentId}/chat/completions": {
"post": {
"summary": "Invoke the weather agent",
"parameters": [
{
"name": "deploymentId",
"in": "path",
"required": true,
"schema": { "type": "string" }
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"messages": { "type": "array" },
"tools": { "type": "array" }
},
"required": ["messages"]
}
}
}
},
"responses": {
"200": {
"description": "Chat completion response, including any tool calls the agent made.",
"content": {
"application/json": {
"schema": { "type": "object" }
}
}
}
}
}
}
}
}

Save that locally as openapi.json, then import it inline instead of by link, since there’s nothing at the other end of a link to point to:

az apic api definition import-specification --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--api-id citadel-weather-agent-v1 \
--version-id v1-0 \
--definition-id openapi \
--format inline \
--value @C:\Users\steef\ai-citadel-agent\citadel-agent\openapi.json \
--specification "{\"name\":\"openapi\",\"version\":\"3.0.0\"}

That worked. But notice what actually happened here. The spec isn’t extracted from the system, it’s a description you write and commit to keeping accurate. If you add a second tool to the agent, or change the invocation shape, this file goes stale unless you treat it as part of the change, not an afterthought to registration.

Step 5: register the environment and deployment

az apic environment create \
--resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--environment-id dev \
--title "Development" \
--type development
az apic api deployment create --resource-group rg-ai-hub-gateway-dev \
--service-name apic-wpvlimv4ngkns \
--api-id citadel-weather-agent-v1 \
--deployment-id dev-deployment \
--title "Dev deployment" \
--environment-id "/workspaces/default/environments/dev" \
--definition-id "/workspaces/default/apis/citadel-weather-agent-v1/versions/v1-0/definitions/openapi" \
--server "{\"runtimeUri\":[\"https://apim-wpvlimv4ngkns.azure-api.net/weather-agent\"]}"

The portal equivalent FOR Azure API Center

If you’d rather click through it, the same five steps map onto the portal like this. Open the API Center resource in the Azure portal, and go to APIs. Select “Register API,” fill in the title and type, and paste the AI Publish Contract JSON into the custom metadata panel that appears once the metadata schema is assigned. From there, add a version, upload or link the OpenAPI definition, and finish by adding an environment and a deployment under the API’s Deployments tab.

I default to the CLI for anything I’ll repeat across agents, and the portal when I’m registering something once or explaining the process to someone new to the platform.

What it looks like in the fleet dashboard

Once registration finishes, the weather agent shows up in the Azure API Center catalog alongside anything else registered in the hub. The catalog view lists each API (agent) with its title, type, and lifecycle stage, and clicking into it surfaces the custom metadata panel with the full AI Publish Contract attached.

This is where the payoff becomes visible. Instead of a spreadsheet someone updates twice a year, the platform team gets a live, filterable view. Filter by dataClassification: personal and you instantly see which agents touch sensitive data. In addition, filter by environment: production and you get the actual production fleet, not the fleet someone remembers deploying. And finally, filter by owner, and you know exactly who to page when an agent misbehaves.

For a single weather agent, this looks like overkill. It isn’t the weather agent that justifies the pattern. It’s the fortieth agent, registered by a team you’ve never met, that you’ll be glad has the same contract structure as this one.

Connecting to EU AI Act Article 49

Article 49 of the EU AI Act introduces a registration obligation, primarily for providers and deployers of high-risk AI systems, who must register certain information in an EU database before those systems go into service or are used. The exact scope depends on the system’s risk classification, and I’m not going to pretend to give legal advice here. Talk to your compliance team about whether a specific system falls under that obligation.

What I can talk about is the architecture. The AI Publish Contract pattern doesn’t make an agent compliant with Article 49. What it does is put your organization in a position to answer an Article 49-style question quickly: which AI systems exist, what do they do, what data do they process, and are any of them high-risk. That’s the same question a platform team asks internally for entirely operational reasons, and it turns out to be the same question a regulator asks for entirely different reasons.

Building the registry now, before any single agent forces the issue, means the answer already exists when someone asks. Retrofitting a registry across dozens of agents that were never designed with one in mind is a much worse project to inherit.

The weather agent in this post sets aiActArticle49Applicable: false, because it’s a low-risk, non-personal-data agent. The field exists precisely so the next agent, the one that does touch personal data or make consequential decisions, has a place to say true and trigger whatever process your organization builds around that flag.

Pitfalls

A few things caught me off guard while working through this. I’m flagging these as the kind of issues I’d expect to hit on a real rollout, so treat them as a checklist to verify against your own environment rather than a transcript of my exact errors.

  • A passthrough agent has no OpenAPI spec to export, even though it looks like it should. This is the one that actually got me. Importing by link failed with a vague reachability error from az apic, which pointed me toward a networking explanation. Curling the URL directly gave the real answer: a plain 404 Resource not found from APIM. There was nothing published at that path, because Part 2 wired the agent up as a passthrough to Azure OpenAI, not as a dedicated API resource with its own documented surface. Passthroughs proxy requests, they don’t publish specs. The fix is to hand-author a minimal OpenAPI spec describing the invocation contract and import it inline, and then treat that file as something you update deliberately when the agent’s interface changes, not something the platform keeps in sync for you.
  • Custom metadata schema assigned after APIs already exist. If you register an API before creating the metadata schema, the custom properties won’t validate retroactively. Define the schema first, every time, or you’ll end up patching APIs one by one afterward.
  • RBAC gaps across the hub-spoke boundary. The identity registering the agent needs Contributor (or a custom role with equivalent permissions) on the API Center instance in the hub, which is a different resource group than where the agent itself runs. Teams that only have access to their own spoke will hit a permissions wall here, and that’s worth deciding on deliberately rather than discovering it during a rollout.

Other Pitfalls

  • Confusing API Center registration with APIM configuration. These are separate systems that happen to sit in the same hub. Registering an agent in API Center doesn’t change how APIM routes or governs it. I’ve seen (and made) the assumption that registration alone would trigger some policy change in APIM. It doesn’t. They’re linked by convention and shared metadata, not by any automatic sync.
  • Definition drift. The OpenAPI definition describes the HTTP surface of the agent, but the AI Publish Contract describes its behavior and data handling. Nothing enforces that these stay in sync when the agent changes. Treat both as part of your deployment pipeline, updated together, rather than the contract as a one-time registration artifact.
  • Lifecycle stage mismatches. Marking an API’s --lifecycle-stage as development when it’s actually running in a shared dev/test environment used by multiple teams creates confusion during the fleet review. Match lifecycle stages to how the environment is actually used, not just what resource group it happens to sit in.

What’s next

This closes out the deployment arc of the series: hub and spoke, a working agent, persistence, a kill switch, governance policies, and now a registry entry. What I haven’t covered yet, and what comes next, is hardening this for anything beyond a single dev environment. That means promotion paths from dev to staging to production, cost controls that scale with a growing agent fleet, and staging environment parity so what you test actually matches what you ship.

If there’s interest, that hardening work is a natural follow-up series rather than a single post, given how much ground it covers.

Wrapping up the series

Five posts ago, this started as an empty resource group in Sweden Central. It’s now a governed platform: a hub that enforces policy and holds the registry, a spoke that runs the agent, a kill switch that can shut things down fast, and a contract-based registration pattern that keeps the fleet auditable as it grows.

The weather agent was never really the point. It was the simplest possible agent I could use to prove out every layer of the platform without the complexity of a real production workload getting in the way. The patterns here, hub-spoke isolation, layered kill switches, policy-driven governance, and contract-based registration, are the parts meant to outlast this specific agent.

Thanks for following along through the series. If you’re building something similar, I’d like to hear what broke for you.

Knowledge Base as a Service in Azure Logic Apps

The Logic Apps Agent Loop series published here between May and June 2026 documented the agentic capabilities of Azure Logic Apps in depth. Notably, one recurring limitation across that series was the complexity of knowledge retrieval: building a proper Retrieval-Augmented Generation pipeline required a separately configured Azure AI Search index, an indexer, a data source, and a significant setup overhead before the agent could answer a question from enterprise content.

Consequently, the Knowledge Base-as-a-Service (KBaaS) capability in Azure Logic Apps, announced at Integrate 2026 and now in preview, addresses this directly. This post, as a follow-up to the previous post on all the announcements, walks through what KBaaS is, how it works, and how to build a practical HR policy agent that answers employee questions from uploaded policy documents, with a complete sample available on GitHub.

What is Knowledge Base as a Service?

KBaaS is a Logic Apps-native RAG pipeline that sits in front of Azure Cosmos DB and Azure OpenAI. Instead of building and managing the ingestion and retrieval infrastructure yourself, you upload documents and KBaaS handles the rest.

Specifically, the service has two pipelines:

  • Ingestion pipeline — when you upload a document, KBaaS automatically parses, chunks, summarises, and vectorises the content using your Azure OpenAI embeddings model. The results are stored in four Cosmos DB containers that KBaaS creates and manages on your behalf.
  • Retrieval pipeline — when an agent loop queries the knowledge base, KBaaS rewrites the query if needed, generates a vector representation, performs a semantic search against Cosmos DB, and returns the most relevant chunks to the language model for response generation.

In contrast, the key difference from a manually configured RAG pipeline is the abstraction layer. You do not configure chunking strategies, embedding dimensions, index schemas, or retrieval parameters. Instead, you upload documents and add the knowledge base as a tool. The agent loop does the rest.

Knowledge Base as a Service: Deploying the sample

The complete sample is available at steefjan1/logic-apps-kbaas-sample. It includes the workflow definition, Bicep infrastructure template, sample HR policy documents, and connection configuration.

Step 1: Provision infrastructure with azd

cd C:\Dev\logic-apps-kbaas-sample
azd auth login
azd init
azd env new <your-env-name>
azd provision

Naming note: avoid hyphens in your environment name. The Bicep template appends store to the prefix for the storage account name, and hyphens are not valid in storage account names. Use a short alphanumeric name such as kbaas rather than kbaas-preview.

Specifically, azd provision creates the resource group, a Logic App Standard, a Cosmos DB with vector search enabled, a storage account, an App Service Plan, and RBAC role assignments.

Step 2: Set app settings

$cosmosKey = az cosmosdb keys list `
--name <cosmos-name> `
--resource-group <resource-group> `
--query primaryMasterKey -o tsv
$storageKey = az storage account keys list `
--account-name <storage-name> `
--resource-group <resource-group> `
--query "[0].value" -o tsv
az logicapp config appsettings set `
--name <logic-app-name> `
--resource-group <resource-group> `
--settings `
AzureWebJobsStorage="DefaultEndpointsProtocol=https;AccountName=<storage-name>;AccountKey=$storageKey;EndpointSuffix=core.windows.net" `
FUNCTIONS_EXTENSION_VERSION="~4" `
FUNCTIONS_WORKER_RUNTIME="dotnet" `
APP_KIND="workflowapp" `
OPENAI__endpoint="https://<your-aoai-resource>.openai.azure.com/" `
OPENAI__key="<your-aoai-key>" `
agent_openAIKey="<your-aoai-key>" `
agent_openAIEndpoint="https://<your-aoai-resource>.openai.azure.com/" `
COSMOS__endpoint="https://<cosmos-name>.documents.azure.com:443/" `
COSMOS__key="$cosmosKey"

Step 3: Deploy the workflow

Compress-Archive `
-Path connections.json, host.json, hr-policy-agent `
-DestinationPath ..\kbaas-deploy.zip `
-Force
az logicapp deployment source config-zip `
--name <logic-app-name> `
--resource-group <resource-group> `
--subscription "<your-subscription>" `
--src ..\kbaas-deploy.zip
az logicapp restart `
--name <logic-app-name> `
--resource-group <resource-group>

Verify the workflow deployed cleanly go to the Logic App Overview → Notifications tab and confirm no WorkflowProcessingFailed errors appear before proceeding.

Step 4: Create the knowledge base connection

In the Azure portal, open your Logic App. Under Agents in the left sidebar, select Knowledge base then + Set up.

Basics tab:

  • Display name: hr-knowledge-base
  • Authentication type: Key-based
  • Database: select your Cosmos DB account — URL endpoint and key auto-populate
  • Click Next

Model tab:

  • Authentication type: URL and key-based authentication
  • Azure OpenAI resource: select your Azure OpenAI resource
  • Completions model: enter your GPT-4o deployment name exactly as it appears in Azure OpenAI
  • Embeddings model: text-embedding-3-small
  • Click Create

Preview limitation: The knowledge base connection does not persist across page refreshes in the current preview — this is a known portal UI bug. Do not refresh the page after clicking Create. Proceed immediately to adding files.

Note: Furthermore, after creating the connection, you can only edit display names. Authentication type, endpoint, and model deployment names cannot be changed. Get these right before clicking Create.

Step 5: Upload knowledge sources

Immediately after creating the connection, without refreshing, click New → Add files.

  • Group name: type hrpolicies
  • Upload hr-leave-policy.md from the sample’s docs/ folder
  • Artifact name: hr-leave-policy
  • Click Add

Once the status changes to Completed, then repeat for hr-expense-policy.md.

Preview limitation: The portal uploads one file at a time. Wait for each file to reach Completed before uploading the next.

Preview limitation: The Add button may remain greyed out if the artifact name field does not register keyboard input. If this happens, try dragging and dropping the file onto the upload area rather than using the file picker, then type the artifact name. If the button remains inactive, try a private/incognito browser window.

KBaaS creates the following Cosmos DB containers during ingestion:

ContainerPurpose
KnowledgeHubsKnowledge base metadata
KnowledgeArtifactsSource metadata and document references
KnowledgeArtifactChunksFull-text document chunks
KnowledgeArtifactChunkSummariesSummarised chunks with vector embeddings

Step 6: Add the knowledge base as a tool

Open the hr-policy-agent workflow in the Logic Apps designer. Select the HR Policy Agent action and scroll to the Knowledge base section in the parameters pane. Select Create and choose hr-knowledge-base from the list. Save the workflow.

Testing the agent

Get the trigger URL from hr-policy-agent → Overview → Run trigger → copy the callback URL.

Test 1 — grounded answer from the leave policy:

In postman execute: https://kbass-preview-la.azurewebsites.net:443/api/hr-policy-agent/triggers/When_an_HTTP_request_is_received/invoke?api-version=2022-05-01&sp=%2Ftriggers%2FWhen_an_HTTP_request_is_received%2Frun&sv=1.0&sig=<sig>

With payload (application\json): {“question”: “How many days of annual leave am I entitled to?”}

Test 2 — grounded answer from the expense policy:

In postman execute: https://kbass-preview-la.azurewebsites.net:443/api/hr-policy-agent/triggers/When_an_HTTP_request_is_received/invoke?api-version=2022-05-01&sp=%2Ftriggers%2FWhen_an_HTTP_request_is_received%2Frun&sv=1.0&sig=<sig>

With payload: (application\json):{“question”: “What is the maximum I can claim per night for hotel accommodation?”}

Test 3 — correct fallback when knowledge base has no answer:

In postman execute: https://kbass-preview-la.azurewebsites.net:443/api/hr-policy-agent/triggers/When_an_HTTP_request_is_received/invoke?api-version=2022-05-01&sp=%2Ftriggers%2FWhen_an_HTTP_request_is_received%2Frun&sv=1.0&sig=<sig>

With payload: {“question”: “What is the company policy on remote working?”}

As a result, the agent answers the first two questions from the uploaded documents and correctly declines the third exactly as the system prompt instructs.

Knowledge Base as a Service:: Practitioner notes

Several things in the current preview are worth documenting for anyone following along:

Connection persistence bug — the knowledge base connection disappears on page refresh in the portal. The connection is correctly stored in connections.json on disk and the workflow runs correctly — but the portal UI does not display it after refresh. Work around this by not refreshing after creating the connection and uploading files in the same browser session.

knowledgeHubConnections format — the connections.json format for knowledge base connections is not publicly documented. If you include a knowledgeHubConnections block with incorrect structure in your deployed connections.json, the Logic App runtime will fail to start with The 'knowledgeHubConnections' property in connection.json has a value that cannot be parsed. The safest approach is to omit knowledgeHubConnections from your deployed connections.json entirely and let the portal write it after deployment.

Embeddings model region availability — text-embedding-3-small with Standard SKU is not available in all Azure regions. Use GlobalStandard SKU as shown in the prerequisites command above.

Managed Identity for agent connections — KBaaS supports Managed Identity authentication for the Cosmos DB connection. However, Managed Identity is not supported for the agent model connection when using the MicrosoftFoundry model type. If your workflows use Foundry Models, the agent connection must use Key authentication. Managed Identity remains the right choice for the Cosmos DB connection and all other backend services.

workflow.json schema — the Agent action requires a limit property at the action level (not inside inputs) and a modelConfigurations block with a referenceName pointing to the agent connection. The Microsoft Learn documentation does not show these as required, but the runtime rejects the workflow without them.

Preview limitations

  • Only uploaded files are supported as knowledge sources — live API connectors and SharePoint libraries are not yet available
  • Supported formats: DOC, DOCX, HTML, MD, PDF, PPT, PPTX, TXT, XLS, XLSX
  • Text-based content only — images within documents are not parsed
  • Default chunking only — custom chunk size and overlap configuration is not yet available
  • Azure portal only — VS Code is not yet supported for knowledge base configuration
  • One file upload at a time — wait for each file to reach Completed before uploading the next

GitHub sample

The complete sample workflow definition, Bicep infrastructure template, sample HR policy documents, and connection configuration are available on my GitHub.

What comes next

KBaaS is the most significant reduction in RAG pipeline complexity that Logic Apps has offered to date. For scenarios where uploaded documents are the primary knowledge source, it removes the Azure AI Search setup entirely and replaces it with a portal-native upload flow. The preview limitations, particularly the connection persistence bug and the lack of live source connectors, will be addressed in subsequent releases.

The natural next step is combining KBaaS with the multi-agent patterns from Part 5 of the Logic Apps Agent Loop series, an orchestrator agent that routes questions to specialist knowledge bases, each containing documents for a specific domain.

Azure Logic Apps at Integrate 2026: The Announcements

Integrate 2026 took place on June 8–9 and brought the Microsoft integration product group together with the community for the first time since the platform’s agentic capabilities became generally available. For Azure Logic Apps, the announcements from Divya Swarnkar and Wagner Silveira’s session “What’s New and What’s Next in Azure Logic Apps” signal something more than a feature release cycle. They signal a platform repositioning.

Historically, Logic Apps has always occupied the integration and workflow orchestration layer of the Azure stack. Consequently, it is also firmly in the AI orchestration layer, connecting systems, knowledge, and intelligent agents in ways that were not possible twelve months ago. As a result, this post unpacks the five announcements that matter most for integration architects and connects them to the work covered in the Logic Apps Agent Loop series published here over the past two months.

Azure Logic Apps Integrate: The announcements

1. Azure Logic Apps Automation

The headline announcement is Logic Apps Automation, a new managed offering that sits alongside Logic Apps Standard and Consumption. It introduces a dedicated automation portal, AI-assisted workflow authoring, and a fully managed infrastructure model that removes the App Service Plan configuration and management required by Standard.

For integration architects in particular, this is significant in two ways. First, AI-assisted authoring lowers the barrier to building workflows; natural language descriptions of what a workflow should do can generate a starting point for the designer. Second, the fully managed model means organizations can adopt Logic Apps at scale without dedicated infrastructure expertise for every deployment.

In practice, Logic Apps Automation targets the enterprise automation use case: the high-volume, repeatable processes that currently live in RPA tools, home-grown scripts, or overly complex BPMN platforms. Importantly, it retains Logic Apps’ governance and security capabilities while making the platform accessible to a broader audience within the organization.

2. Knowledge as a Service

Microsoft announced Knowledge as a Service for Logic Apps, a capability that simplifies how organizations prepare enterprise data for AI-driven scenarios. Rather than building and maintaining complex data ingestion, chunking, embedding, and retrieval pipelines, teams can upload content, and Logic Apps handles the orchestration required to make that data available to AI agents.

Notably, this is directly relevant to the agentic workflows covered in this series. In Post 4, the agent tool layer relied on Azure AI Search as the retrieval mechanism, which required a separately configured search index, an indexer, a data source, and a skill set. Instead, Knowledge as a Service abstracts that complexity into a Logic Apps-native capability, reducing the setup time for a retrieval-augmented generation pattern from hours to minutes.

Significantly, the capability will be available across both Logic Apps Automation and Logic Apps Standard.

3. Azure AI Foundry Agent Integration

Logic Apps now supports invoking Azure AI Foundry Agents directly from workflows. Organisations can build, evaluate, and govern agents within Azure AI Foundry and use Logic Apps to orchestrate those agents as part of broader business processes.

Crucially, this closes a gap that the agent loop series ran into directly. Moreover, in Post 4, the attempt to call a Logic Apps workflow as an OpenAPI tool from Foundry hit network restrictions between the two platforms. As a result, the native Foundry Agent Integration announced at Integrate 2026 addresses this at the platform level; the connection between Logic Apps and Foundry is a first-class integration, not a custom OpenAPI workaround.

In practice, for multi-agent architectures, this means the orchestrator-worker pattern from Post 5 can now span both platforms: a Foundry agent as the orchestrator, Logic Apps autonomous workflows as the workers, with native connectivity between them rather than the SAS token-based HTTP invocation used in the demo.

4. Logic Apps Standard SDK

Microsoft introduced the Logic Apps Standard SDK, enabling workflows to be authored directly in C#. Developers gain access to familiar .NET tooling, type safety, NuGet packaging, and proper source control practices, while continuing to use the existing Logic Apps runtime and operational infrastructure.

Of all the announcements, this is the most relevant to the DevOps content in Post 7 of this series. The JSON-on-disk deployment model covered there remains valid, but the SDK adds a code-first authoring path that developer teams will strongly prefer for complex workflows. Type-safe workflow definitions, unit testability, and IDE integration (Visual Studio, VS Code) address the most common developer friction points with the current designer-first model.

For integration architects evaluating Logic Apps for new projects, the SDK changes the “who builds this” conversation. Workflows no longer need to be designer-authored by integration specialists; they can be written by developers using the tools they already know.

5. Azure Connector Namespace

Microsoft unveiled Azure Connector Namespace, which decouples Logic Apps connectors from Logic Apps workflows. The connector ecosystem, which includes over 1,400 connectors covering Microsoft and third-party services, can now be used from custom applications, Azure Functions, Container Apps, and AI agent platforms without the workflow runtime.

Of the five, this is architecturally the most significant for the longer term. Previously, the three-layer tool model covered in Post 4 (built-in connectors, custom connectors, MCP servers) assumed that connectors lived inside Logic Apps workflows. Azure Connector Namespace removes that constraint. Now, an Azure Function or a Foundry agent can now consume a Logic Apps connector directly, accessing Office 365, Service Bus, SAP, or any of the other 1,400+ services without a workflow in between.

For enterprise AI architectures, this means the connectivity layer and the orchestration layer are now separable. An AI agent can reach any enterprise system through the connector ecosystem without Logic Apps being the runtime that executes the connection.

Azure Logic Apps Integrate: The direction of travel

Taken together, the five announcements describe a platform moving in a consistent direction: Logic Apps is becoming the connectivity and orchestration substrate for enterprise AI, not just enterprise integration.

The diagram below maps the five announcements against the platform layers they affect: authoring, orchestration, knowledge, connectivity, and developer experience.

The agent loop series documented the platform as it stood at the general availability of the agentic capabilities. Encouragingly, several of the limitations called out in that series the Foundry network restrictions, the complexity of knowledge retrieval setup, and the JSON-only authoring model are directly addressed by the Integrate 2026 announcements. That is a healthy sign: the platform team is hearing the practitioner feedback and moving quickly.

Azure Logic Apps Integrate: What this means for integration architects

Three practical implications for architects evaluating or already using Logic Apps:

  • First, revisit your hosting model decision. Logic Apps Automation changes the Standard-versus-Consumption decision for new projects. If the fully managed model meets your governance requirements, the App Service Plan overhead goes away.
  • Secondly, reconsider your knowledge retrieval approach. If you are building RAG patterns on Azure today using manually configured AI Search indexes, Knowledge as a Service is worth evaluating as a simpler path, particularly for projects where the data preparation pipeline is more complex than the agent itself.
  • Third, plan for SDK adoption. If your organization has strong .NET development capability, the Logic Apps Standard SDK should be on the evaluation list for any new workflow project. The designer-first model remains valid, but the code-first path will be preferred by development teams working in existing C# codebases.

Azure Logic Apps Integrate: Series connection

The Logic Apps Agent Loop series published here between May and June 2026 covered the agentic capabilities of Logic Apps in depth, from the anatomy of a single agent loop through to multi-agent patterns, security, and production operations. The Integrate 2026 announcements build directly on that foundation. Post 4’s MCP server pattern connects to the Azure Connector Namespace. Subsequently, Post 5’s orchestrator-worker pattern connects to the Foundry Agent Integration. Post 7’s DevOps section connects to the Standard SDK.

Microsoft Foundry Citadel Platform Azure: A Practitioner’s Deployment Guide

Microsoft Foundry Citadel Platform on Azure is a layered AI governance architecture that delivers production-ready agent deployments with unified governance, end-to-end observability, and centralized policy enforcement via Azure API Management. It is still in preview, and the documentation assumes a degree of familiarity with Azure infrastructure that not everyone has on day one. This post walks through what it actually takes to get a working hub-and-spoke running in Sweden Central, including the pitfalls, so you can decide whether it is a viable starting point for your own AI platform journey.

What Citadel Is (and Is Not)

Before touching the tooling, it helps to understand what Citadel actually deploys. The architecture has four layers:

The first layer — Governance Hub is the runtime enforcement plane: Azure API Management as a centralized AI gateway, Azure API Center as a model registry, and supporting services for content safety, PII detection, cost attribution, and usage telemetry.

Subsequent second layer 2 — AI Control Plane provides observability via the Foundry Control Plane: agent-level execution traces, AI evaluations in development and production, red-teaming, drift monitoring, and fleet dashboards.

The next third layer — Agent Identity transforms agents into managed enterprise assets via Microsoft Entra ID, with lifecycle management, sponsorship models for human accountability, and shadow AI discovery.

Finally, the last fourth layer, 4 — Security Fabric, weaves Defender, Purview, and Entra across the other three layers for real-time threat intelligence, data governance, and compliance automation.

For this guide, we deploy Layer 1 (the Governance Hub via the AI Hub Gateway Solution Accelerator) and a Layer 1/2 spoke (via the AI Landing Zone Bicep). Layers 3 and 4 reference existing Azure services (Entra ID, Defender, Purview) that you integrate separately.

Important: Citadel is currently in preview. The repos, parameter schemas, and CLI commands will change. Treat everything in this post as a starting point, not a stable reference.

Prerequisites

Before you start, make sure you have:

  • An Azure subscription with Azure OpenAI access approved (aka.ms/oaiapply)
  • Microsoft.Authorization/roleAssignments/write on the subscription (Owner or User Access Administrator role)
  • Azure CLI installed and authenticated (az login)
  • Azure Developer CLI (azd) installed
  • Node.js — use v20 LTS, not v24. Node 24 on Windows has a known issue where npm bundles are incomplete, causing MODULE_NOT_FOUND errors on npm-cli.js and npm-prefix.js when azd tries to package Logic App components

If you run into npm issues on Windows, the cleanest workaround is Azure Cloud Shell, where Node, npm, az, and azd are all pre-installed and healthy.

Part 1: Deploying the Microsoft Foundry Citadel Governance Hub

Clone the AI Hub Gateway Solution Accelerator:

git clone https://github.com/Azure-Samples/ai-hub-gateway-solution-accelerator.git
cd ai-hub-gateway-solution-accelerator

Create your azd environment:

azd auth login
azd env new ai-hub-gateway-dev
azd env set AZURE_LOCATION swedencentral

Create a parameters file at infra/main.parameters.json. The key decisions:

Model versions matter. At the time of writing, gpt-4o-mini versions 2024-07-18 and 2024-10-18 are retired. Use gpt-4o version 2024-11-20 with GlobalStandard SKU. Always verify current model availability at aka.ms/aoai-regions before deploying these changes frequently.

{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": { "value": "ai-hub-gateway-dev" },
"location": { "value": "swedencentral" },
"apimSku": { "value": "Developer" },
"openAiInstances": {
"value": {
"openAi1": {
"name": "openai1",
"location": "swedencentral",
"deployments": [
{
"name": "chat",
"model": { "format": "OpenAI", "name": "gpt-4o", "version": "2024-11-20" },
"sku": { "name": "GlobalStandard", "capacity": 20 }
},
{
"name": "embedding",
"model": { "format": "OpenAI", "name": "text-embedding-3-large", "version": "1" },
"sku": { "name": "Standard", "capacity": 20 }
}
]
}
}
},
"provisionFunctionApp": { "value": false },
"createAppInsightsDashboard": { "value": false },
"enableAIGatewayPiiRedaction": { "value": true },
"enableAIModelInference": { "value": true }
}
}

Deploy:

azd up

Expect 45–90 minutes. APIM Developer SKU is the slow component. If the deployment fails partway through, re-run azd up it is idempotent and will pick up where it left off.

Pitfall: Managed Identity Race Condition

You will likely see this error on first attempt:

BadRequest: The provided principal ID was not found in the AAD tenant(s)

This is a known race condition — the Managed Identity is created but has not yet propagated in Entra ID before the role assignment fires. Re-run azd up without any changes and it will succeed.

Validate the Hub

Once deployed, run:

azd env get-values | grep APIM

You will get your APIM gateway URL. Test it with a chat completion:

$headers = @{
"Content-Type" = "application/json"
"api-key" = "<YOUR_APIM_SUBSCRIPTION_KEY>"
}
$body = '{"messages":[{"role":"user","content":"Hello from the AI Hub Gateway!"}],"max_tokens":100}'
Invoke-RestMethod `
-Uri "https://<your-apim>.azure-api.net/openai/deployments/chat/chat/completions?api-version=2024-02-01" `
-Method POST -Headers $headers -Body $body

A successful response with content_filter_results and prompt_filter_results confirms Content Safety and PII redaction are active. Token usage in the response confirms Cosmos DB is logging for cost attribution.

Part 2: Deploying a Citadel Platform Agent Spoke on Azure

The spoke is deployed from the AI Landing Zone Bicep repo. Download it as a ZIP (no GitHub account required):

https://github.com/Azure/bicep-ptn-aiml-landing-zone/archive/refs/heads/main.zip

Extract and navigate to the folder. Create a resource group for the spoke:

az group create --name rg-ai-spoke-dev --location swedencentral

Create a spoke.parameters.json file. Several things to know upfront:

The parameter schema is not the same as the Citadel README suggests. The actual template parameters differ from the example file. Key differences discovered in practice: aiFoundryLocation does not exist as a separate parameter; deployMcp, greenFieldDeployment, deployPostgres, and useCMK are not in this version of the template; and solutionStorageAccountName is simply storageAccountName.

The modelDeploymentList uses nested objects, not flat properties:

"modelDeploymentList": {
"value": [
{
"name": "chat",
"model": { "format": "OpenAI", "name": "gpt-4o", "version": "2024-11-20" },
"sku": { "name": "GlobalStandard", "capacity": 20 },
"canonical_name": "CHAT_DEPLOYMENT_NAME",
"apiVersion": "2025-04-01-preview"
},
{
"name": "text-embedding",
"model": { "format": "OpenAI", "name": "text-embedding-3-large", "version": "1" },
"sku": { "name": "Standard", "capacity": 10 },
"canonical_name": "EMBEDDING_DEPLOYMENT_NAME",
"apiVersion": "2025-04-01-preview"
}
]
}

containerAppsList cannot be an empty array. The template references containerApps[0] internally and will fail validation if the array is empty. Pass at least one placeholder entry.

Deploy:

az deployment group create `
--resource-group rg-ai-spoke-dev `
--template-file main.bicep `
--parameters @spoke.parameters.json

Pitfalls in the Spoke Deployment

AI Search Standard SKU capacity exhaustion. Sweden Central frequently runs out of AI Search Standard SKU capacity. You will see ResourcesForSkuUnavailable. This affects both the standalone Search Service and the AI Foundry Agent Service’s internal Search instance. Disable both:

"deploySearchService": { "value": false },
"deployAAfAgentSvc": { "value": false }

You can re-enable them later once capacity is available, or deploy Search in a different region.

Soft-deleted resources block redeployment. Azure retains soft-deleted Cognitive Services accounts, Key Vaults, and App Configuration stores for up to 90 days. If you delete a resource group and redeploy, the deployment will fail with FlagMustBeSetForRestore or NameUnavailable. Purge them explicitly before redeploying:

# List and purge soft-deleted resources
az keyvault list-deleted --subscription <sub-id> -o table
az keyvault purge --name <name> --location swedencentral
az appconfig list-deleted --subscription <sub-id> -o table
az appconfig purge --name <name> --location swedencentral --yes
az cognitiveservices account list-deleted --subscription <sub-id> -o table
az cognitiveservices account purge --name <name> --location swedencentral

Key Vault purges are slow — allow 2–5 minutes per vault.

Bastion subnet ID resolution fails with networkIsolation=false. When you disable network isolation, the template passes a relative subnet ID to Bastion instead of a fully qualified resource ID. Disable Bastion, Jump VM, and NAT Gateway for the dev spoke:

"deployBastion": { "value": false },
"deployJumpbox": { "value": false },
"deployVM": { "value": false },
"deployNatGateway": { "value": false }

Write parameters files without BOM. On Windows, Out-File -Encoding utf8 adds a Byte Order Mark that causes az deployment to fail with Unable to parse parameter. Use either:

$content | Out-File -FilePath "spoke.parameters.json" -Encoding utf8NoBOM
# or
[System.IO.File]::WriteAllText("spoke.parameters.json", $content, [System.Text.UTF8Encoding]::new($false))

Part 3: Wiring the Citadel Spoke to the Azure APIM Hub

Add the hub’s APIM gateway URL and subscription key to the spoke’s App Configuration:

az appconfig kv set `
--name <spoke-appconfig-name> `
--key "APIM_GATEWAY_URL" `
--label "ai-lz" `
--value "https://<your-apim>.azure-api.net/openai" `
--yes
az appconfig kv set `
--name <spoke-appconfig-name> `
--key "APIM_SUBSCRIPTION_KEY" `
--label "ai-lz" `
--value "<YOUR_APIM_KEY>" `
--yes

Note: az cognitiveservices account connection create with a YAML file for creating an APIM connection in AI Foundry has known bugs in the current CLI version and will throw NoneType or codec errors. Create this connection via the Azure AI Foundry portal UI instead.

Validate End-to-End

$headers = @{
"Content-Type" = "application/json"
"api-key" = "<YOUR_APIM_KEY>"
}
$body = '{"messages":[{"role":"user","content":"Hello from the Citadel spoke!"}],"max_tokens":50}'
Invoke-RestMethod `
-Uri "https://<your-apim>.azure-api.net/openai/deployments/chat/chat/completions?api-version=2024-02-01" `
-Method POST -Headers $headers -Body $body

A successful response with content_filter_results, prompt_filter_results, and usage confirms the full Citadel loop: spoke → APIM gateway → Azure OpenAI → governance telemetry.

What the Microsoft Foundry Citadel Platform Deploys

After following this guide, your rg-ai-hub-gateway-dev resource group contains:

  • APIM gateway with content safety, PII redaction, token rate limiting, and cost attribution policies
  • Azure OpenAI with gpt-4o and text-embedding-3-large
  • Cosmos DB for usage event logging
  • Logic App for usage processing
  • Application Insights for gateway telemetry

Your rg-ai-spoke-dev resource group contains:

  • AI Foundry account and project
  • gpt-4o and text-embedding-3-large deployments
  • Cosmos DB with a conversations container
  • Key Vault, App Configuration, Storage Account, Application Insights, Log Analytics

App Configuration is fully populated with canonical keys (CHAT_DEPLOYMENT_NAME, AI_FOUNDRY_PROJECT_ENDPOINT, COSMOS_DB_ENDPOINT, and more) ready for agent applications to consume.

This Is a Dev Setup — Here Is What Changes for Non-Prod and Production

The configuration above is a starting point, not a production blueprint. Key differences when moving up the environment stack:

APIM SKU. Developer SKU has no SLA and no VNet support. Switch to Premium SKU for non-prod and production. This significantly increases cost and deployment time but enables private networking, multi-region, and availability zones.

Network isolation. For production, set networkIsolation=true and wire the spoke VNet to your hub VNet via peering (hubIntegrationHubVnetResourceId). This requires coordinating private DNS zones across the hub and spoke. The template supports bringing existing DNS zones via the existingPrivateDnsZone* parameters.

AI Search. Re-enable deploySearchService and deployAAfAgentSvc for non-prod and production. If Sweden Central remains capacity-constrained on Standard SKU, deploy Search to a paired region (East US 2 works well) using the searchServiceLocation parameter.

Bastion and Jump VM. For production with networkIsolation=true, re-enable deployBastion and deployJumpbox so operators can access resources inside the private VNet without public endpoints.

Separate parameter files per environment. Maintain spoke.parameters.dev.json, spoke.parameters.nonprod.json, and spoke.parameters.prod.json with environment-specific values. Use a deployment pipeline (GitHub Actions or Azure DevOps) to apply them consistently.

Model versions. Pin specific model versions in parameters files and validate availability in your target region before each deployment. Azure OpenAI model lifecycle moves fast; versions retire on 18-month cycles, and regional availability varies.

Preview Caveats

Citadel is in active development. Several things you should expect to change:

The parameter schemas for both the hub and spoke accelerators will evolve. Parameters discovered missing or renamed in this guide will likely be reorganized again as the repos mature. Always check the actual main.bicep parameter definitions rather than relying on example files.

The az cognitiveservices account connection create CLI command for AI Foundry connections is incomplete at the time of writing. This will improve as the Foundry CLI surface area matures.

The citadel-v1 branch in the AI Hub Gateway repo is flagged as the recommended path for new deployments. By the time you read this, it may have become the default branch with a cleaner deployment experience.

Regional capacity for AI Search Standard SKU fluctuates. Sweden Central is a high-demand region for AI workloads plan for capacity constraints in any SKU beyond Basic for dev scenarios.

Conclusion

Citadel gives you a credible, opinionated starting point for enterprise AI governance on Azure APIM as the AI gateway, AI Foundry as the agent runtime, Cosmos DB for conversation state, and App Configuration as the configuration backbone. Getting it running today requires navigating several rough edges: parameter schema inconsistencies, soft-delete cascades, model version deprecations, regional capacity constraints, and Windows-specific tooling issues.

None of these are blockers. They are the expected friction of working with a platform in active preview. The underlying architecture is sound, and the pieces that do work, APIM governance policies, Content Safety integration, App Config population, and AI Foundry project wiring deliver real value immediately.

If you are building an AI platform for your organization, a Citadel dev setup is a reasonable first step. Treat it as a learning environment to understand the architecture, validate the tooling, and build the parameter files you will need for non-prod and production. Then evolve it deliberately: add network isolation, re-enable Search and Agent Services as capacity allows, and adopt the Citadel contracts (AI Access Contract, AI Publish Contract) to formalize the hub-spoke integration as your agent portfolio grows.

The governance-velocity paradox Citadel sets out to solve is real. Getting the foundation right now, while it is still in preview and the patterns are malleable, is the right time to start.

Final note: This post reflects a hands-on deployment performed in June 2026. Given the pace of change in this space, verify all CLI commands, parameter schemas, and model versions against current documentation before applying them in your own environment.

Azure API Management Load Balancing and Circuit Breaker for AI Backends

Part 5 of 7 in the “APIM for AI Workloads” series

Azure API Management load balancing for AI workloads solves a problem that every team hits once they move beyond a single Azure OpenAI deployment: PTU capacity is finite, PAYG is a safety net, and when things go wrong on one backend, the rest of your workload should not notice. In Part 1 of this series, I described PTU vs. PAYG as a routing problem. This post is where we solve it.

The combination of backend pools, priority-based routing, and circuit breaker rules in APIM gives you a resilient AI gateway that handles three distinct failure modes: PTU saturation (too many tokens consumed against reserved capacity), regional outages, and transient backend errors. None of these requires changes to calling applications. APIM absorbs the complexity and presents a single stable endpoint.

Azure API Management Load Balancing: Backend Pools for AI

APIM’s backend pool feature lets you define a named group of AI backends and route to them as a unit. You reference the pool in the set-backend-service policy by its pool ID. When a request arrives, APIM selects a backend from the pool based on priority and weight, tracks health state via the circuit breaker, and retries on the next available member if the selected backend fails.

For AI workloads, the standard pattern uses two tiers. The first tier is your PTU deployment reserved capacity in a primary region, assigned priority 1. The second tier is a PAYG deployment in a secondary region, assigned priority 2. APIM routes all traffic to the PTU backend as long as the PTU backend is healthy. When PTU returns a 429 (capacity exceeded) error or becomes unreachable, the circuit breaker trips, and APIM automatically fails over to the PAYG backend.

Priority determines the preference order: lower numbers are preferred. Weight applies when multiple backends share the same priority, distributing load proportionally between them. A common pattern for multi-region PTU deployments is two PTU backends at priority 1, each with a different weight reflecting their provisioned capacity, and a shared PAYG backend at priority 2 as the common overflow.

Circuit Breaker Configuration for Azure API Management AI Backends

The circuit breaker is what makes the backend pool resilient rather than just load-balanced. Without it, APIM continues routing to a saturated or unavailable backend on every request, each one failing with a 429 or timeout before falling back. The circuit breaker short-circuits that path: after a configurable number of failures within a time window, it marks the backend as OPEN and stops sending traffic to it entirely.

The three circuit breaker states map directly to operational behavior:

CLOSED is the normal state. All requests are routed to the backend. Failures APIM counts failures within the configured interval, and the counter resets at the end of each interval if the number of failures remains below the threshold.

After enough failures to exceed the threshold, the breaker trips to OPEN. In this state, APIM bypasses the backend entirely, and APIM routes to the next available pool member without attempting the failed backend again. The tripDuration timer starts counting down immediately.

Once tripDuration elapses, the breaker enters HALF-OPEN and sends a single probe request to test recovery. A successful response transitions the backend back to CLOSED. A failure resets the timer and keeps the circuit OPEN.

For Azure OpenAI specifically, 429 should always be in your failureCondition alongside 503 and 504. A 429 from a PTU endpoint indicates that the provisioned throughput ceiling has been reached and the backend is temporarily unable to serve requests. That is exactly the condition you want to trip the circuit and fail over to PAYG, rather than returning errors to the caller.

Sizing Circuit Breaker Parameters for AI Workloads

The right circuit breaker parameters depend on your traffic pattern and how quickly you need failover to activate. A few practical guidelines:

threshold: For AI workloads, 3 to 5 failures is a reasonable starting point. PTU endpoints return 429 consistently when saturated, so you don’t need a high threshold to detect the condition. Setting it too high means you absorb too many failed requests before failing over.

interval: 60 seconds works well for most workloads. This is the window over which failures are counted. Shorter intervals are more sensitive to transient errors, while longer ones suit bursty traffic patterns where a few failures in a short window are expected.

tripDuration: 30 seconds is a sensible default. PTU capacity refreshes on a per-minute basis, so a 30-second trip duration gives the backend time to recover before the probe fires. For deployments where PTU saturation is a known recurring pattern, a longer trip duration (60 to 120 seconds) reduces the frequency of failed probes.

Retry Policy and Agentic Workload Considerations

Backend pool failover and circuit breaking handle backend-level failures, but you may also want a retry policy in your APIM inbound pipeline for transient errors that do not warrant a full circuit trip. The retry policy can be scoped to specific status codes and configured with a backoff interval, giving you a two-level resilience model: retry for transient errors, circuit break for sustained failures.

For agentic workloads specifically, failover behavior needs careful thought. A conversational agent mid-session that silently switches from a PTU to a PAYG backend will not notice the change at the model API level. But agentic pipelines with multiple sequential tool calls are more sensitive: a mid-pipeline failover can introduce latency spikes that cause timeouts in orchestration layers such as Azure Logic Apps or Semantic Kernel.

The practical mitigation is to expose the remaining token budget via the token limit policy variable from Part 3 and have the orchestration layer monitor it to proactively slow down before circuit breaking kicks in. Prevention is cheaper than recovery when the workload is stateful.

What’s Next in This Azure API Management for AI Series

Part 6 covers semantic caching: how APIM uses an embeddings model and Azure Managed Redis to serve cached responses for semantically similar prompts, reducing token consumption and latency without any changes to calling applications.

New Pricing Plan and Enhanced Networking for Azure Container Apps in Preview

Microsoft recently announced a new pricing plan and enhanced networking for Azure Container Apps in public preview.

Azure Container Apps is a fully managed environment that enables developers to run microservices and containerized applications on a serverless platform. It is flexible and can execute application code packaged in any container without runtime or programming model restrictions.

Earlier Azure Container Apps had a consumption plan featuring a serverless architecture that allows applications to scale in and out on demand. Applications can scale to zero, and users only pay for running apps.

In addition to the consumption plan, Azure Container Apps now supports a dedicated plan, which guarantees single tenancy and specialized compute options, including memory-optimized choices. It runs in the same Azure Container Apps environment as the serverless Consumption plan and is referred to as the Consumption + Dedicated plan structure. This structure is in preview.

Mike Morton, a Senior Program Manager at Microsoft, explains in a Tech Community blog post the benefit of the new plan:

It allows apps or microservice components that may have different resource requirements depending on component purpose or development stack to run in the same Azure Container Apps environment. An Azure Container Apps environment provides an execution, isolation, and observability boundary that allows apps within it to easily call other apps in the environment, as well as provide a single place to view logs from all apps.

At the Azure Container Apps environment scope, compute options are workload profiles. The default workload profile for each environment is a serverless, general-purpose profile available as part of the Consumption plan. For the dedicated workload profile, users can select type and size, deploy multiple apps into the profile, use autoscaling to add and remove nodes and limit the scaling of the profile.

Source: https://techcommunity.microsoft.com/t5/apps-on-azure-blog/azure-container-apps-announces-new-pricing-plan-and-enhanced/ba-p/3790723

With Container Apps, one architect has another compute option in Azure besides App Service and Virtual Machines. Edwin Michiels, a Tech Customer Success Manager at Microsoft, answered in a LinkedIn post the difference between Azure Container Apps and Azure Apps Service, which offer similar capabilities:

In terms of cost, Azure App Service has a pricing model based on the number of instances and resources used, while Azure Container Instances and Azure Kubernetes Service are billed based on the number of containers and nodes used, respectively. For small to medium-sized APIs, Azure App Service may be a more cost-effective option, while for larger or more complex APIs, Azure Container Instances or Azure Kubernetes Service may offer more flexibility and cost savings.

The Consumption + Dedicated plan structure also includes optimized network architecture and security features that offer reduced subnet size requirements with a new /27 minimum, support for Azure Container Apps environments on subnets with locked-down network security groups and user-defined routes (UDR), and support on subnets configured with Azure Firewall or third-party network appliances.

The new pricing plan and enhanced networking for Azure Container Apps are available in the North Central US, North Europe, West Europe, and East US regions. Billing for Consumption and Dedicated plans is detailed on the Azure Container Apps pricing page.

Lastly, the new price plan and network enhancements are discussed and demoed in the latest Azure Container Apps Community Standup.

My Experience with Microsoft Excel During IT Projects

Throughout my extensive career in IT, I often worked with Microsoft Excel. One of my first projects was to leverage Excel to create documentation for a telco’s site surveys. I built a solution with Visual Basic for Applications, a programming language for Excel, and all the other Microsoft Office programs like Word and PowerPoint. With VBA, I could generate multiple worksheets in a Workbook filled with static and dynamic data – from a user’s input or configuration file. Once populated with data and rendered, the Workbook was converted to a Portable Document Format (PDF).

Over the last couple of years, I have had other projects involving Excel. In this post, I will dive into the details of implementations (use cases) concerning Excel Workbooks. One project involved processing Excel files in a Container running on an Azure Kubernetes Service (AKS) cluster, the other generating an Excel Workbook for reporting purposes, orchestrated by an Azure Logic App.

Use Case – Processing an Excel Workbook in a Container

The use case was as follows. In short, I was working on a project for a client a few years ago that required processing a standardized Excel template that their customers could provide for enrichment. The data in the excel file needed to end in a database for further processing (enrichment) so that it could be presented back to them.  The diagram below shows the process of a customer uploading an Excel file via an API. The API would store the Excel in an Azure storage container and trigger code inside a container responsible for processing (parsing the Excel to JSON). The second container had code persist the data in SQL Azure.

The code snippet (as an example) responsible for processing the Excel file:

For creating the Excel Workbook and its sheet with data, I found the EPPlus library, a spreadsheet library for the .NET framework and .NET core. In the project, I imported the EPPlus NuGet package – specifically, I used the ExcelPackage class.

Now let’s move on to the second use case.

Use Case – Generating an Excel Report in Azure

In a recent project for another customer, I had to generate a report of products inside D365 that needed to be an Excel File (a workbook containing a worksheet with data). The file had to be written to an on-premises file share to allow the target system to consume it. The solution I built was using a Logic App to orchestrate the project of generating the Excel file.

Below you see a diagram visualizing the steps from triggering a package in D365 until the writing of the Excel file in a file share on-premises.

The steps are:

  1. Logic App triggering a package in D365 (schedule trigger).
  2. Executing the package to retrieve and export data to a SQL Azure Database.
  3. Query by the same Logic App that triggered the package to retrieve the data from the SQL Azure Database.
  4. Passing the data to (the result of the query) to an Azure Function, which will create an Excel Workbook with one sheet containing the data in a given format. The function will write the Excel to an Azure Storage container.
  5. Subsequently, the Logic App will download and write the file to the on-premises file share (leveraging the On-Premises Data Gateway – ODPGW).

The sequence diagram below shows the flow (orchestration) of the process.

And below is a part of the Logic App workflow definition resembling the sequence diagram above.

The code snippet (as an example) in the Azure Function responsible for creating the Excel file:

For the creation of the Excel Workbook and sheet with data, I used NPOI – an open-source project which can help you read/write XLS, DOC, and PPT file extensions. In Visual Studio, I imported NPOI NuGet Package. The package covers most of the features of Excel like styling, formatting, data formulas, extracting images, etc. In addition, it does not require the presence of Microsoft Office. Furthermore, I used the StorageAccountClass to write the Excel file.

Conclusion

Microsoft Excel is a popular product available for decades and used by millions of people ranging from businesses heavily relying on Excel to home users for basic administration. Moreover, in IT, Excel is used in many scenarios such as project planning, environment overviews, project member administration, reporting, etc. As said earlier, I have encountered Microsoft Excel various times in my career and built solutions involving the product. The two use-cases are examples of that.

In the first example, I faced a challenge finding a library that supported .NET Core 2.0. I found EPPlus, which did the job for us after experimenting with it first. In the second example, the cost and simplicity were the benefits of using the NPOI library. There were constraints in the project to use solutions with a cost (subscription-based or one-off). Furthermore, the solution proved to be stable enough to generate the report.

Note that the libraries I found are not the only ones available to work with Excel. For instance, SpreadsheetGear, and others, which are listed here. In Logic Apps, you can find connectors that can do the job for you, such as CloudMersive (API you connect to convert, for instance, CSV to Excel).

I do feel with code you have the most flexibility when it comes to dealing with Excel. A standard, of-the-shelve can do the job for you, however, cost (licensing) might be involved or other considerations. What you choose in your scenarios depends on the given context and requirements.