AI Foundry Spoke Model Deployment: Why It Still Happens

A comment on the first post in this series asked why AI Foundry Spoke model deployment happens at all in the Citadel pattern, a question worth answering properly in its own post rather than buried in a reply thread.

Great article as usual! Just wondering, in the given Governance Hub & Agent Spoke architecture, what is the purpose of deploying the models both to the Spoke and the Hub? Shouldn’t they only be deployed to the Hub and provided from there?

That’s a sharp question, and it points at a real tension in the Citadel pattern that the original post didn’t call out explicitly enough. Here’s the direct answer, followed by the reasoning behind it.

The short answer

No, they shouldn’t both serve your application’s inference needs. Only the Hub deployment should. The model deployment sitting in the Spoke exists today because of how Azure AI Foundry’s Agent Service currently works, not because the architecture intends a second, governance-free inference path.

That distinction matters, so it’s worth walking through why the Spoke deployment is there at all.

Why AI Foundry Spoke Model Deployment Happens at All

In the ideal version of the Citadel pattern, every model call flows through the Hub’s APIM gateway. That’s the entire point of centralizing governance in one place. It’s what the rest of the series demonstrated: every agent call routed through apim-wpvlimv4ngkns, with token tracking, content safety, cost attribution, and the kill switch all enforced at that single choke point.

The Spoke still ends up with its own local model deployment because the AI Foundry Agent Service needs one for two reasons. This is a direct consequence of how the AI Landing Zone Bicep templates provision the Spoke, not a choice made anywhere in this series.

It powers the Agent Service’s own internal capabilities. Thread management, agent orchestration, and built-in tools like Code Interpreter or File Search (when enabled) call the model directly, through Foundry’s own runtime, rather than through any external endpoint you control. That runtime doesn’t route through APIM. It talks to whatever model deployment sits alongside it in the same project.

It satisfies the Foundry project’s provisioning requirements. A Foundry project currently expects an associated model deployment to exist as part of setting up the project, even if your actual application traffic never calls that deployment directly.

Neither of these is a governance decision. They’re artifacts of how the Agent Service is architected right now.

Two paths, not one

The practical result is that a Citadel deployment ends up with two separate paths to a model, and they serve different purposes.

Diagram showing two paths to a model in the Citadel architecture. Application code routes through APIM in the Hub to the Hub's Azure OpenAI deployment, a governed path shown with a solid green arrow. Foundry's internal Agent Service runtime calls a local model deployment in the Spoke directly, bypassing APIM, shown with a dashed red arrow labeled as ungoverned.
Application traffic should only ever reach a model through APIM in the Hub. The Spoke’s local deployment exists for Foundry’s internal agent runtime, not for your code to call directly.

The Spoke’s local deployment exists for Foundry’s own internal agent runtime. It’s not meant to see your production traffic, and if it does, none of the governance you built in the Hub applies to those calls.

The Hub’s deployment, reached through APIM, is what your application code should use. That’s what we wired up explicitly in Part 2 of this series, with the standard OpenAI SDK pointed at the gateway rather than directly at the Foundry endpoint. The Hub itself is built on the AI Hub Gateway Solution Accelerator, and its AI gateway capabilities are exactly what give APIM the token metering, content safety, and audit trail features this series has leaned on throughout.

The second path exists precisely because of a limitation the series already documented. The Agent Service SDK, in its current preview state, doesn’t route its own LLM calls through APIM. It bypasses the gateway entirely, which means using it directly would mean giving up token metering, policy enforcement, and audit trails on every call the agent makes. That’s why Part 2 used the standard OpenAI SDK pointed at APIM instead of the native Agent Service SDK, and it’s the same underlying issue this reader’s question is really about.

What this means in practice

If you’re building on this pattern today, treat the Spoke’s model deployment as infrastructure the platform needs to exist, not as a second inference endpoint your application is allowed to call. Point your application code at the Hub, through APIM, every time. Leave the Spoke deployment alone to do the job Foundry needs it for internally, and don’t build anything that calls it directly for your own traffic.

If you’re reviewing someone else’s Citadel-pattern deployment, this is worth checking explicitly. A model deployment sitting in a Spoke isn’t wrong by itself, but it’s worth confirming nothing in the application is quietly calling it and skipping the gateway.

Where this is heading

I’d expect this to tighten up as the Agent Service SDK matures out of preview and gains native APIM routing support. When that happens, the two-path situation described here becomes a one-path situation, and the Spoke deployment stops being something you need to actively route around.

Side-by-side comparison diagram. Left panel, labeled Today, shows application code and the Agent Service each reaching separate model deployments, one through APIM in the Hub, one bypassing it in the Spoke. Right panel, labeled Future, shows both application code and the Agent Service routing through a single APIM gateway to one model deployment in the Hub.
Once the Agent Service SDK supports native APIM routing, both application and agent traffic converge on a single governed path.

Until then, the answer to the original question stands: deploy to the Hub, govern everything through APIM, and treat the Spoke’s model deployment as plumbing the platform needs rather than a second front door.

Thanks to the reader who asked the original question. It’s exactly the kind of detail that’s easy to leave implicit in an architecture diagram and much more useful said out loud.

Microsoft Foundry Citadel Platform Azure: Conversation Persistence with Cosmos DB

In the previous post, we connected a real tool-calling agent to the Microsoft Foundry Citadel Platform on Azure, routing every LLM call through the APIM governance hub in Sweden Central. The agent answered weather questions; the hub captured usage events in Cosmos DB; and Application Insights confirmed that both LLM calls were governed. The agent worked, but it had no memory. Every run started fresh, with no record of what was asked or answered.

This post adds conversation persistence to the Microsoft Foundry Citadel Platform on Azure. Every agent run now produces a structured document in the spoke’s Cosmos DB conversations container: the user’s question, the tool call made, the tool result, the agent’s answer, token counts, model version, and timestamp. The agent gains a memory layer, marking the transition as the spoke’s data tier becomes active.

What We Build

Each agent run writes one document to the spoke Cosmos DB:

{
"id": "run-20260625-143022-stockholm",
"principal_id": "steefjan@msn.com",
"timestamp": "2026-06-25T14:30:22.441Z",
"question": "What is the weather like in Stockholm right now?",
"tool_calls": [
{
"name": "get_weather",
"arguments": {"location": "Stockholm"},
"result": {
"location": "Stockholm, Sweden",
"temperature_celsius": 22.5,
"wind_speed_kmh": 7.2,
"condition": "Overcast"
}
}
],
"answer": "The weather in Stockholm is overcast with a temperature of 22.5°C...",
"model": "gpt-4o-2024-11-20",
"prompt_tokens": 234,
"completion_tokens": 67,
"total_tokens": 301,
"apim_gateway": "apim-wpvlimv4ngkns.azure-api.net"
}

The partition key is /principal_id matching the container definition deployed by the spoke Bicep template. In addition, this arrangement ensures that all conversations for a given user are grouped into the same logical partition, making per-user history queries efficient.

The agent writes the document after completing the run, so a failed or incomplete run leaves no record.Moreover, it’s clean, simple, and auditable.

Prerequisites

From the previous two posts you should have:

  • Hub deployed in rg-ai-hub-gateway-dev
  • Spoke deployed in rg-ai-spoke-dev with Cosmos DB cosmos-tggi2gmkw22w4, database cosmos-dbtggi2gmkw22w4, container conversations
  • App Config appcs-tggi2gmkw22w4 populated with COSMOS_DB_ENDPOINT and CONVERSATIONS_DATABASE_CONTAINER
  • agent.py, config.py, and tools.py from the previous post
  • Virtual environment activated with openai, azure-appconfiguration, azure-identity, and requests installed

Step 1 — Install the Cosmos DB SDK

With your virtual environment activated:

pip install azure-cosmos

Pitfall: Cosmos DB Public Network Access

If your Cosmos DB has firewall rules enabled (which the spoke Bicep template sets by default), your local IP needs to be in the allowed list or public access needs to be set to All networks for dev. Check via the portal: cosmos-tggi2gmkw22w4 (your instance) → NetworkingPublic accessAll networks → Save. In production this would be networkIsolation=true with private endpoints only.

Step 2 — Extend Config to Read Cosmos DB Settings

The spoke App Config already contains COSMOS_DB_ENDPOINT and CONVERSATIONS_DATABASE_CONTAINER — populated automatically during deployment. Extend config.py to pull these:

$lines = @(
"from azure.appconfiguration import AzureAppConfigurationClient",
"from azure.identity import DefaultAzureCredential",
"",
"APP_CONFIG_ENDPOINT = 'https://appcs-tggi2gmkw22w4.azconfig.io'",
"LABEL = 'ai-lz'",
"",
"def get_config() -> dict:",
" credential = DefaultAzureCredential()",
" client = AzureAppConfigurationClient(",
" base_url=APP_CONFIG_ENDPOINT,",
" credential=credential",
" )",
" keys = [",
" 'AI_FOUNDRY_PROJECT_ENDPOINT',",
" 'CHAT_DEPLOYMENT_NAME',",
" 'APIM_GATEWAY_URL',",
" 'APIM_SUBSCRIPTION_KEY',",
" 'COSMOS_DB_ENDPOINT',",
" 'CONVERSATIONS_DATABASE_CONTAINER',",
" 'DATABASE_NAME',",
" ]",
" config = {}",
" for key in keys:",
" setting = client.get_configuration_setting(key=key, label=LABEL)",
" config[key] = setting.value",
" return config",
"",
"if __name__ == '__main__':",
" cfg = get_config()",
" for k, v in cfg.items():",
" print(f'{k}: {v[:40]}...')"
)
[System.IO.File]::WriteAllLines("$PWD\config.py", $lines, [System.Text.UTF8Encoding]::new($false))

Test it:

python config.py

You should now see seven keys including COSMOS_DB_ENDPOINT pointing to https://cosmos-tggi2gmkw22w4.documents.azure.com:443/ and CONVERSATIONS_DATABASE_CONTAINER set to conversations.

Step 3 — Create cosmos.py

Create a dedicated Cosmos DB module:

$code = 'import os
cosmos = """from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
import uuid
from datetime import datetime, timezone
def get_cosmos_client(endpoint):
return CosmosClient(url=endpoint, credential=DefaultAzureCredential())
def save_conversation(endpoint, database_name, container_name, principal_id, question, tool_calls, answer, model, prompt_tokens, completion_tokens, total_tokens, apim_gateway):
container = get_cosmos_client(endpoint).get_database_client(database_name).get_container_client(container_name)
now = datetime.now(timezone.utc)
fmt = \"%Y%m%d-%H%M%S\"
doc_id = f\"run-{now.strftime(fmt)}-{str(uuid.uuid4())[:8]}\"
document = {\"id\": doc_id, \"principal_id\": principal_id, \"timestamp\": now.isoformat(), \"question\": question, \"tool_calls\": tool_calls, \"answer\": answer, \"model\": model, \"prompt_tokens\": prompt_tokens, \"completion_tokens\": completion_tokens, \"total_tokens\": total_tokens, \"apim_gateway\": apim_gateway}
container.create_item(body=document)
print(f\"Saved conversation: {doc_id}\")
return document
def get_conversation_history(endpoint, database_name, container_name, principal_id, limit=5):
container = get_cosmos_client(endpoint).get_database_client(database_name).get_container_client(container_name)
query = f\"SELECT TOP {limit} c.id, c.timestamp, c.question, c.answer, c.total_tokens FROM c WHERE c.principal_id = @principal_id ORDER BY c._ts DESC\"
return list(container.query_items(query=query, parameters=[{\"name\": \"@principal_id\", \"value\": principal_id}], partition_key=principal_id))
"""
agent = """import json
from openai import AzureOpenAI
from config import get_config
from tools import get_weather, WEATHER_TOOL_DEFINITION
from cosmos import save_conversation, get_conversation_history
PRINCIPAL_ID = \"steefjan@msn.com\"
def run_agent_with_memory(user_question):
cfg = get_config()
apim_base = cfg[\"APIM_GATEWAY_URL\"].rstrip(\"/\").replace(\"/openai\", \"\")
client = AzureOpenAI(azure_endpoint=apim_base, api_key=cfg[\"APIM_SUBSCRIPTION_KEY\"], api_version=\"2024-02-01\")
messages = [{\"role\": \"user\", \"content\": user_question}]
print(f\"Sending request via APIM: {apim_base}\")
response = client.chat.completions.create(model=cfg[\"CHAT_DEPLOYMENT_NAME\"], messages=messages, tools=[WEATHER_TOOL_DEFINITION], tool_choice=\"auto\")
msg = response.choices[0].message
messages.append(msg)
tool_calls_log = []
answer = msg.content or \"\"
total_prompt = response.usage.prompt_tokens
total_completion = response.usage.completion_tokens
if msg.tool_calls:
for tool_call in msg.tool_calls:
args = json.loads(tool_call.function.arguments)
print(f\" -> Tool call: get_weather({args})\")
result_str = get_weather(**args)
result_json = json.loads(result_str)
print(f\" -> Tool result: {result_str}\")
tool_calls_log.append({\"name\": tool_call.function.name, \"arguments\": args, \"result\": result_json})
messages.append({\"role\": \"tool\", \"tool_call_id\": tool_call.id, \"content\": result_str})
response2 = client.chat.completions.create(model=cfg[\"CHAT_DEPLOYMENT_NAME\"], messages=messages)
answer = response2.choices[0].message.content
total_prompt += response2.usage.prompt_tokens
total_completion += response2.usage.completion_tokens
save_conversation(endpoint=cfg[\"COSMOS_DB_ENDPOINT\"], database_name=cfg[\"DATABASE_NAME\"], container_name=cfg[\"CONVERSATIONS_DATABASE_CONTAINER\"], principal_id=PRINCIPAL_ID, question=user_question, tool_calls=tool_calls_log, answer=answer, model=cfg[\"CHAT_DEPLOYMENT_NAME\"], prompt_tokens=total_prompt, completion_tokens=total_completion, total_tokens=total_prompt+total_completion, apim_gateway=apim_base.replace(\"https://\", \"\"))
return answer
if __name__ == \"__main__\":
cfg = get_config()
print(\"=== Recent conversation history ===\")
history = get_conversation_history(cfg[\"COSMOS_DB_ENDPOINT\"], cfg[\"DATABASE_NAME\"], cfg[\"CONVERSATIONS_DATABASE_CONTAINER\"], PRINCIPAL_ID, 3)
if history:
for h in history:
print(f\" [{h[\"timestamp\"]}] Q: {h[\"question\"][:60]}...\")
else:
print(\" No previous conversations found.\")
print()
question = \"What is the weather like in Amsterdam right now?\"
print(f\"Question: {question}\")
answer = run_agent_with_memory(question)
print(f\"Answer: {answer}\")
"""
with open("cosmos.py", "w", encoding="utf-8") as f:
f.write(cosmos)
with open("agent_with_memory.py", "w", encoding="utf-8") as f:
f.write(agent)
print("Done")
'
[System.IO.File]::WriteAllText("$PWD\write_files.py", $code, [System.Text.UTF8Encoding]::new($false))
python write_files.py

Pitfall: Managed Identity RBAC for Cosmos DB

The CosmosClient with DefaultAzureCredential uses your Azure CLI identity locally. That identity needs the Cosmos DB Built-in Data Contributor role on the Cosmos DB account — not a standard Azure RBAC role, but a Cosmos DB data plane role. The spoke deployment should have assigned this automatically via the assignCosmosDBCosmosDbBuiltInDataContributorExecutor deployment. If you get a 403, verify:

az cosmosdb sql role assignment list `
--account-name cosmos-tggi2gmkw22w4 `
--resource-group rg-ai-spoke-dev `
--output table

Your principal ID (8e856fa1-f4c4-4a02-91a5-a6ccc6afc6b3) should appear with role definition ID ending in 00000000-0000-0000-0000-000000000002 (Built-in Data Contributor). If not, assign it:

az cosmosdb sql role assignment create `
--account-name cosmos-tggi2gmkw22w4 `
--resource-group rg-ai-spoke-dev `
--role-definition-id /subscriptions/dc0f4d72-3734-4b03-8884-ccfb9c2c4cc7/resourceGroups/rg-ai-spoke-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-tggi2gmkw22w4/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002 `
--principal-id 8e856fa1-f4c4-4a02-91a5-a6ccc6afc6b3 `
--scope /subscriptions/dc0f4d72-3734-4b03-8884-ccfb9c2c4cc7/resourceGroups/rg-ai-spoke-dev/providers/Microsoft.DocumentDB/databaseAccounts/cosmos-tggi2gmkw22w4

Pitfall: No Connection Strings

Never use Cosmos DB connection strings or account keys in the agent code. The pattern here uses DefaultAzureCredential throughout — locally it picks up your az login identity, in production it uses the spoke’s Managed Identity. This is the NEN 7510 and cVGZ security baseline compliant approach.

Step 4 — Create agent_with_memory.py

$lines = @(
"import json",
"from openai import AzureOpenAI",
"from config import get_config",
"from tools import get_weather, WEATHER_TOOL_DEFINITION",
"from cosmos import save_conversation, get_conversation_history",
"",
"PRINCIPAL_ID = 'steefjan@msn.com'",
"",
"def run_agent_with_memory(user_question: str) -> str:",
" cfg = get_config()",
" apim_base = cfg['APIM_GATEWAY_URL'].rstrip('/').replace('/openai', '')",
"",
" client = AzureOpenAI(",
" azure_endpoint=apim_base,",
" api_key=cfg['APIM_SUBSCRIPTION_KEY'],",
" api_version='2024-02-01',",
" )",
"",
" messages = [{'role': 'user', 'content': user_question}]",
" print(f'Sending request via APIM: {apim_base}')",
"",
" # First LLM call - tool decision",
" response = client.chat.completions.create(",
" model=cfg['CHAT_DEPLOYMENT_NAME'],",
" messages=messages,",
" tools=[WEATHER_TOOL_DEFINITION],",
" tool_choice='auto',",
" )",
"",
" msg = response.choices[0].message",
" messages.append(msg)",
" first_usage = response.usage",
"",
" tool_calls_log = []",
" answer = msg.content or ''",
" total_prompt_tokens = first_usage.prompt_tokens",
" total_completion_tokens = first_usage.completion_tokens",
"",
" # Handle tool calls",
" if msg.tool_calls:",
" for tool_call in msg.tool_calls:",
" args = json.loads(tool_call.function.arguments)",
" print(f' -> Tool call: get_weather({args})')",
" result_str = get_weather(**args)",
" result_json = json.loads(result_str)",
" print(f' -> Tool result: {result_str}')",
"",
" tool_calls_log.append({",
" 'name': tool_call.function.name,",
" 'arguments': args,",
" 'result': result_json,",
" })",
"",
" messages.append({",
" 'role': 'tool',",
" 'tool_call_id': tool_call.id,",
" 'content': result_str,",
" })",
"",
" # Second LLM call - synthesis",
" response2 = client.chat.completions.create(",
" model=cfg['CHAT_DEPLOYMENT_NAME'],",
" messages=messages,",
" )",
" answer = response2.choices[0].message.content",
" total_prompt_tokens += response2.usage.prompt_tokens",
" total_completion_tokens += response2.usage.completion_tokens",
"",
" total_tokens = total_prompt_tokens + total_completion_tokens",
"",
" # Save to Cosmos DB conversations container in the spoke",
" save_conversation(",
" endpoint=cfg['COSMOS_DB_ENDPOINT'],",
" database_name=cfg['DATABASE_NAME'],",
" container_name=cfg['CONVERSATIONS_DATABASE_CONTAINER'],",
" principal_id=PRINCIPAL_ID,",
" question=user_question,",
" tool_calls=tool_calls_log,",
" answer=answer,",
" model=cfg['CHAT_DEPLOYMENT_NAME'],",
" prompt_tokens=total_prompt_tokens,",
" completion_tokens=total_completion_tokens,",
" total_tokens=total_tokens,",
" apim_gateway=apim_base.replace('https://', ''),",
" )",
"",
" return answer",
"",
"if __name__ == '__main__':",
" # Show last 3 conversations before running",
" from config import get_config",
" cfg = get_config()",
" print('=== Recent conversation history ===')",
" history = get_conversation_history(",
" endpoint=cfg['COSMOS_DB_ENDPOINT'],",
" database_name=cfg['DATABASE_NAME'],",
" container_name=cfg['CONVERSATIONS_DATABASE_CONTAINER'],",
" principal_id=PRINCIPAL_ID,",
" limit=3,",
" )",
" if history:",
" for h in history:",
" print(f' [{h[chr(116)+chr(105)+chr(109)+chr(101)+chr(115)+chr(116)+chr(97)+chr(109)+chr(112)]}] Q: {h[chr(113)+chr(117)+chr(101)+chr(115)+chr(116)+chr(105)+chr(111)+chr(110))[:60]}...')",
" else:",
" print(' No previous conversations found.')",
" print()",
"",
" question = 'What is the weather like in Amsterdam right now?'",
" print(f'Question: {question}')",
" answer = run_agent_with_memory(question)",
" print(f'Answer: {answer}')"
)
[System.IO.File]::WriteAllLines("$PWD\agent_with_memory.py", $lines, [System.Text.UTF8Encoding]::new($false))

Run it:

python agent_with_memory.py

A successful run looks like this:

Run it a second time and the history section will show the previous exchange:

Step 5 — Validate in Cosmos DB Data Explorer

Go to the Azure Portal → cosmos-tggi2gmkw22w4Data Explorercosmos-dbtggi2gmkw22w4conversationsItems.

You should see your conversation document with all fields populated. The partition key /principal_id should match steefjan@msn.com.

To query all conversations for a user:

SELECT * FROM c WHERE c.principal_id = 'steefjan@msn.com' ORDER BY c._ts DESC

To get a summary of all runs with token totals:

SELECT c.id, c.timestamp, c.question, c.total_tokens, c.model
FROM c
WHERE c.principal_id = 'steefjan@msn.com'
ORDER BY c._ts DESC

Step 6 — What’s in the Document

Looking at a stored conversation document, every field serves a purpose:

FieldPurpose
idUnique run identifier — traceable back to a specific agent invocation
principal_idPartition key — enables per-user history queries and RBAC scoping
timestampISO 8601 UTC — audit trail, correlatable with APIM logs
questionOriginal user input — searchable for pattern analysis
tool_callsFull tool call log including arguments and results — debugging and audit
answerFinal agent response — quality review and feedback loops
modelModel version — tracks which model version answered which questions
prompt_tokens / completion_tokensCumulative across both LLM calls — accurate per-conversation cost
total_tokensSum of both calls — FinOps input per user per conversation
apim_gatewayGateway used — identifies which hub instance served the request

The token counts here are cumulative across both LLM calls (tool decision and synthesis), yielding a true per-conversation costrather than a per-call figure. This is more useful for FinOps reporting you care about the cost of answering a question, not the cost of individual API calls within that answer.

Pitfalls Summary

PitfallFix
Cosmos DB firewall blocks local IPPortal → Networking → All networks for dev, or add specific IP
403 on Cosmos DB writeAssign Cosmos DB Built-in Data Contributor data plane role to your principal
CosmosResourceNotFoundErrorVerify database name (cosmos-dbtggi2gmkw22w4) and container name (conversations) match exactly
Partition key mismatchContainer was created with /principal_id — every document must include this field
DefaultAzureCredential fails locallyRun az login and ensure the correct subscription is selected
Never use connection stringsUse DefaultAzureCredential throughout — locally via az login, in production via Managed Identity

What the Full Citadel Data Layer Now Looks Like

After this post, the spoke’s data tier is fully active:

StoreWhat it holdsWho writes it
Hub Cosmos DB ai-usage-containerPer-LLM-call usage events (tokens, model, gateway, IP)APIM gateway automatically
Spoke Cosmos DB conversationsPer-run conversation documents (question, tools, answer, cumulative tokens)Agent code explicitly
App Config appcs-tggi2gmkw22w4All configuration keys for the spokeSpoke deployment automatically

The hub’s ai-usage-container captures the infrastructure view of every API call, governed and logged. The spoke’s conversations container captures the application view of every user interaction, structured and queryable. Together, they give you both compliance evidence and application telemetry from a single agent run.

What’s Next

The next post in this series showcases the Citadel Kill Switch and explains how it stops a governed agent when necessary. It details how the five-layer containment system in APIM effectively shuts down the process without affecting the spoke or agent code. The conversation history you’ve created illustrates the clear before-and-after contrast: requests flow to Cosmos DB and then abruptly halt at the gateway layer.

Azure PaaS Integration for Architects: A Practitioner’s Map

If you’ve spent any time in the Azure portal lately, you’ll know the problem isn’t a lack of PaaS services; it’s too many of them, with overlapping capabilities and just enough marketing gloss to make every option look like the right one. App Service or Container Apps? Logic Apps or Functions? Service Bus or Event Grid? The Azure PaaS catalog has grown quickly, and for integration architects specifically, the decisions compound: pick the wrong option at the compute layer, and you’re fighting the platform every time you add a connector, a retry policy, or a compliance control.

This post is a map, not a comparison matrix. I’m grouping the Azure PaaS services that matter to integration work into four layers: compute, integration, data, and governance, and walking through the decision points that arise when designing for a regulated enterprise environment rather than a greenfield demo. If you’ve followed my Logic Apps Agent Loop series or the APIM for AI workloads series, this sits underneath both the platform primer and the deeper posts, which assume you already have read.

Why Azure PaaS still matters to integration architects

IaaS provides you with a VM and asks you to manage everything above it. SaaS gives you the finished product and asks for nothing. PaaS sits in between: the platform owns patching, scaling, and availability, and you own the application logic and configuration. For integration workloads specifically, that trade-off is usually the right one: you rarely need to control the OS of a message broker, but you do need fine-grained control over routing, transformation, and policy enforcement.

The practical test I use: if a service requires you to think about instance sizing, OS patch cycles, or cluster upgrades, it’s leaning IaaS regardless of what the marketing page calls it. If it requires you to think about triggers, bindings, connectors, and scaling rules, it’s PaaS. AKS sits deliberately on that boundary; more on that below.

Diagram showing five stacked Azure PaaS layers for integration architects: Compute (App Service, Functions, Container Apps, AKS), Integration (Logic Apps, API Management, Service Bus, Event Grid), Data (Azure SQL Database, Cosmos DB, Cache for Redis), Governance and identity (Entra ID, Key Vault, Azure Policy, Monitor), and Governance and resilience — the agentic gap (per-action authorization, compensating actions, evaluation and drift detection).
The four core PaaS layers for integration work compute, integration, data, and governance/identity plus the fifth layer, agentic workloads, expose: per-action authorization, compensating actions, and evaluation.

Layer 1: Compute in Azure PaaS Integration

Azure App Service

Still, the default is web APIs and backend services that don’t need event-driven scaling. App Service gives you deployment slots, built-in autoscale, and managed TLS with minimal ceremony. For integration architects, the main use case is hosting synchronous REST APIs that front a backend system the kind of thing that used to be a WCF service or an on-prem IIS site.

The limitation that catches people out: App Service scales on CPU/memory/queue-length rules, not on arbitrary event volume. If your workload is bursty and event-driven rather than steadily loaded, you’ll either overprovision or look elsewhere.

Azure Functions

The event-driven counterpart. Functions are the right choice when the unit of work is a discrete event: a message landing in a queue, a file arriving in Blob Storage, or an HTTP call that needs to fan out. The Consumption plan gives true scale-to-zero, which matters for cost in low-traffic integration scenarios; the Premium and Flex Consumption plans trade some of that elasticity for warm instances and VNet integration, which most enterprise integration platforms need anyway because you’re rarely allowed to expose a public endpoint without a private link in front of it.

Where Functions get uncomfortable: long-running orchestrations. A single function execution has a timeout, and while Durable Functions solves the orchestration problem, you’re now managing a stateful workflow engine on top of a stateless compute primitive. That’s usually the point where I ask whether Logic Apps would do the job with less code.

Azure Container Apps

The newer entrant is increasingly my default recommendation for anything that needs to run a container without the operational overhead of Kubernetes. Container Apps gives you KEDA-based event-driven scaling, Dapr integration for service-to-service calls and pub/sub, and revision-based traffic splitting all without you touching a node pool. For integration architects building agent-based or microservice-style integration components, this is often the sweet spot: you get container portability (useful if the workload might move, or if you’re standardizing on containers for other reasons) without inheriting cluster lifecycle management.

Azure Kubernetes Service (AKS)

Worth naming even though it’s not strictly Azure PaaS: Microsoft manages the control plane, yet you still own node pool upgrades, networking configuration, and workload scheduling. AKS earns its place when you have genuine Kubernetes-native requirements: custom operators, a multi-team platform where Kubernetes is the common substrate, or workloads that need capabilities Container Apps doesn’t expose yet. For most integration teams, reaching for AKS by default is over-engineering. Reach for it when a specific requirement forces your hand, not because it’s the more “serious” option.


Layer 2: Integration with Azure PaaS

Azure Logic Apps

The workflow orchestration layer with Azure PaaS remains the most direct route to enterprise connectors: SAP, IBM MQ, mainframe hosts, and the long tail of line-of-business systems that lack a modern REST API. Standard Logic Apps (running on the single-tenant model) close most of the gaps that made Consumption Logic Apps hard to use in regulated environments: VNet integration, built-in state management, and per-workflow scaling.

The honest trade-off: Logic Apps designer-first workflows are fast to build and easy for less code-heavy teams to maintain, but they get harder to reason about and harder to code-review once a workflow grows past a certain complexity. I’ve found the practical ceiling is somewhere around “a dozen actions with a couple of branches.” Past that, either decompose into smaller workflows or move the logic into a Function.

Azure API Management

Not just a gateway for integration architects, APIM is where governance actually gets enforced. Rate limiting, authentication, request/response transformation, and policy-based routing all live here, in front of whatever compute layer is doing the real work. If you’re building any platform where multiple consumers hit a shared set of backend capabilities, APIM is the control point that lets you change backend implementations without breaking consumers and enforce policy without touching application code.

The thing worth planning for early: policy authoring in APIM is a distinct skill, separate from the languages your team already knows. Please budget time for the team to learn the policy XML dialect rather than treating it as an afterthought. Badly written policies are a common source of latency and hard-to-diagnose failures.

Azure Service Bus

The durable, ordered, transactional messaging backbone. Reach for Service Bus when you need guaranteed delivery, sessions for ordered processing, or transactional message handling across multiple operations. Topics and subscriptions give you pub/sub without standing up a separate broker.

Azure Event Grid

The lightweight, high-throughput event router. Where Service Bus is about reliable delivery of business messages, Event Grid is about routing high-volume, fire-and-forget events resource state changes, custom application events, IoT telemetry to whichever subscriber cares about them. The two are frequently used together: Event Grid fans out a notification, and a subscriber puts a durable message on Service Bus for guaranteed processing.

A rule of thumb I use with teams new to Azure integration: if losing a message would be a business incident, it belongs on Service Bus. If losing a message would just mean a missed notification, Event Grid is fine.


Layer 3: Data in Azure PaaS

Integration architecture lives and dies by what’s underneath it, and the PaaS data services matter as much as the compute and messaging layers.

Azure SQL Database remains the default for relational, transactional workloads with a need for strong consistency think reference data, transactional state, anything with real foreign-key relationships. In addition, Azure Cosmos DB earns its place when you need global distribution, flexible schema, or the kind of horizontal scale that a single SQL instance won’t give you cheaply; it’s also increasingly the default choice for conversation and state storage in agentic workloads, given its low-latency reads and flexible document model. Finally, Azure Cache for Redis sits in front of both, absorbing read load and giving you a fast, ephemeral store for session state or short-lived coordination data.

The mistake I see most often: teams default to Cosmos DB because it’s the “modern” choice, then discover they actually needed relational integrity and end up hand-rolling consistency checks that SQL would have given them for free. Pick based on the access pattern, not the reputation.


Layer 4: Governance and identity for Azure PaaS

This is the layer that separates a proof of concept from something you can run in a regulated industry. Microsoft Entra ID and managed identities remove the need for connection strings and API keys scattered across configuration files. Each of the Azure PaaS services above should authenticate via managed identity. Key Vault holds what can’t be a managed identity (third-party API keys, certificates). Azure Policy and Microsoft Defender for Cloud provide the guardrails and posture visibility that an auditor or security team will ask for. Azure Monitor and Application Insights are non-negotiable for integration platforms, especially when a message fails somewhere in a chain of five services. Distributed tracing is the difference between a five-minute diagnosis and a day of log archaeology.


Layer 5: The gap that agentic workloads expose

Everything above holds for conventional integration platforms. Agentic AI workloads add a wrinkle that a recent round of discussion on LinkedIn I saw around an enterprise agent architecture diagram put well: the model is arguably the least differentiated part of a production agent deployment. Identity, permissions, observability, governance, and reliable orchestration are what separate a working demo from something you can run against real systems, and a few of those deserve to be called out specifically for integration architects, because they don’t map cleanly onto the governance layer above.

Diagram showing a caller validated at a green dashed identity perimeter (Entra ID / RBAC) before entering an agent's reasoning loop of plan, call tool, observe result, and act. A coral dashed arrow shows a poisoned result from an untrusted tool or RAG source entering the loop directly, bypassing the perimeter. A per-action authorization control sits inside the loop, asking whether each specific call is allowed for the tenant.
The identity perimeter validates the caller once, at the edge. A poisoned tool result enters the reasoning loop from the data the agent requested and never crosses that boundary. Per-action authorization is the control that reaches inside the loop where the threat actually lives.

Idenitity

Identity secures who the agent is, not what it does. Entra ID and managed identity answer “Is this caller who it claims to be?” They don’t address what happens when a poisoned tool result or a manipulated retrieved document changes the agent’s next action mid-reasoning loop. Prompt injection rides in through the RAG layer and tool outputs inside the loop, where identity checks at the perimeter don’t reach. The practical implication for PaaS design is that authorization needs to occur per action, not just per identity. This is exactly what APIM policy scoping and per-tool consent in Logic Apps and AI Foundry connectors are for: to treat each tool call as its own authorization decision, rather than an inherited privilege from a validated caller.

Recovery

Recovery means compensating actions, not just retries. Agent actions have side effects across systems: a ticket got created, a record got updated, an email went out. A failed step three actions into an agent loop can’t just retry from the top; it needs a saga-style compensating action to undo what already happened. Service Bus sessions and Logic Apps’ native support for scoped try/catch-with-compensation are the building blocks here — but the compensation logic has to be designed in explicitly, because neither service provides it by default.

Evaluation

Evaluation and drift detection are a first-class layer, not an afterthought. Application Insights and Azure Monitor provide operational observability into latency, error rates, and throughput. They don’t tell you if the agent’s outputs are quietly degrading in quality over time. That’s a separate concern, and one worth budgeting for from the start rather than bolting on after the first bad production incident.

The questions worth asking before an agent goes anywhere near a real system: what did it access, what tool did it call, why did it act, what policy constrained it, what happened when it failed, and who owns the outcome. If a PaaS architecture can’t answer all six, the gap isn’t in the model; it’s in the platform around it.


Azure PaaS Integration decisions: a framework, not a decision tree for integration architects

None of these layers are picked in isolation; the compute choice constrains the integration pattern, and the integration pattern constrains what the data layer needs to support. When I’m working through this with a team, the questions I ask in order are:

  1. Is the trigger an event or a schedule/request? Event-driven points toward Functions or Container Apps with KEDA; request-driven points toward App Service or APIM-fronted compute.
  2. Does a human or a low-code team need to maintain this workflow? If yes, Logic Apps earns serious consideration even if a Function would be more “elegant.”
  3. What’s the cost of losing a message? Business-critical → Service Bus. Best-effort notification → Event Grid.
  4. Does the data need strong relational integrity, or flexible scale? SQL for the former, Cosmos DB for the latter — and don’t let the “modern” label make the decision for you.
  5. Is everything behind managed identity and traced end to end? If the answer is no anywhere in the chain, that’s the next thing to fix, not the last.
Decision flowchart for choosing Azure PaaS services: question one splits event-driven compute (Functions, Container Apps) from request-driven compute (App Service, APIM-fronted); question two routes low-code-maintained workflows to Logic Apps; question three splits business-critical messaging (Service Bus) from best-effort events (Event Grid); question four chooses Azure SQL for relational integrity or Cosmos DB for scale; question five is a gate requiring managed identity and end-to-end tracing before deployment.
The five questions as a branching flow trigger type, workflow ownership, message-loss cost, data access pattern, and the managed-identity gate that comes before anything ships.

Azure PaaS Integration Conclusion

That’s the shape of it. In practice, most enterprise integration platforms end up using several of these services together: API Management fronting a mix of Logic Apps and Functions, backed by Service Bus for reliable delivery and Cosmos DB or SQL for state and the architecture work is less about picking a single winner than about drawing clean boundaries between them.

Azure Logic Apps Agent Loop Production Operations

Part 7 of 7 in the Logic Apps Agent Loop series

Part 6 covered the security stack for agentic workflows: Easy Auth, Managed Identity, and Key Vault. This final post closes the series with Azure Logic Apps agent loop production operations: how to monitor agent loops with Application Insights, what the pricing model looks like across Standard and Consumption, the key platform limits to be aware of, and how to deploy agentic workflows through a repeatable DevOps pipeline.

By the end of this post, you will have a complete picture of what it takes to run an agentic workflow in production, not just to build one.

Azure Logic Apps agent loop production monitoring with Application Insights

The run history you have used throughout this series is the starting point for understanding what an agent loop did and why. For production workloads you need more: aggregated metrics across multiple runs, structured log queries, alerting on failures, and tracing across distributed systems. Application Insights provides all of this for Standard logic apps.

Enabling Application Insights

If you did not enable Application Insights when you created la-agent-loop, you can add it after deployment:

  1. In the Azure portal, open your la-agent-loop logic app resource
  2. Navigate to Application Insights under Settings in the left sidebar
  3. Click Turn on Application Insights
  4. After the pane updates, click Apply → Yes
  5. Click View Application Insights data to open the dashboard

Application Insights begins collecting telemetry from that point forward; it does not backfill historical run data.

What Application Insights captures for agent loops

For Standard agentic workflows, Application Insights captures enhanced telemetry beyond what the run history provides. Key data points include:

Requests — each workflow trigger appears as an incoming request, with duration, success/failure status, and HTTP response code.

Dependencies — each tool call the agent makes appears as a dependency call, with the target service, duration, and result. Moreover, for an agent loop that invokes Azure OpenAI and Azure AI Search, you will see both as dependency entries, making it straightforward to identify which tool call is slowest.

Exceptions — any workflow failure surfaces as an exception with a full stack trace, correlated to the specific run and iteration where it occurred.

Custom metrics — Logic Apps emits custom metrics for agent loop iterations, token usage, and tool invocation counts. These are queryable via Kusto (KQL) in the Logs blade.

Useful KQL queries for agent loops

You can query agent loop run durations for, let’s say, over the last 72 hours:

requests | where timestamp > ago(72h) | where name contains "agent" | summarize avg(duration), max(duration), count() by bin(timestamp, 1h) | render timechart

To identify failed agent loop runs:

requests | where timestamp > ago(72) | where success == false | project timestamp, name, duration, resultCode, cloud_RoleInstance | order by timestamp desc

To track tool call durations:

dependencies | where timestamp > ago(24h) | where type == "HTTP" | summarize avg(duration), count() by target | order by avg_duration desc

Reading the run history for agent loops

The run history in the Logic Apps portal is the fastest way to debug a specific agent loop run. For agentic workflows it shows more than a conventional run history — each agent action expands to show its iterations, and each iteration shows the model’s reasoning, the tool calls it made, and the results it received.

The Agent activity tab is the most useful view for agentic workflows. It shows the conversation between the model and the tools in chronological order, every message the model generated, every tool it invoked, and every result it received. The agent loop reveals its chain of thought.

Key things to look for in the run history:

  • Iteration count — how many Think → Act → Observe cycles the loop ran. A loop that runs the maximum number of iterations (default 100) without completing is a signal that the instructions are ambiguous or the tools are not returning usable results.
  • Tool call inputs and outputs — expand each tool call to see exactly what the model passed as parameters and what the tool returned. This is the fastest way to diagnose a tool that is returning unexpected data.
  • Token usage — the metadata output of each agent action shows total tokens, prompt tokens, and completion tokens. High prompt token counts indicate the conversation history is growing large — consider enabling agent history reduction.

Azure Logic Apps agent loop production pricing: Standard versus Consumption

The pricing model for agentic workflows differs between Standard and Consumption, and it differs significantly from conventional Logic Apps pricing.

Standard

Standard logic apps use a fixed App Service Plan pricing model — you pay for the compute capacity whether the workflow is running or not. Agentic workflows on Standard do not incur extra charges beyond the base App Service Plan cost. However, every Azure OpenAI call the agent makes is billed separately against your Azure OpenAI resource at standard token rates.

For the la-agent-loop workflows in this series:

  • The Standard logic app itself: App Service Plan (Workflow Standard WS1 or higher)
  • Each GPT-4o call: billed to aoai-demo-ptu at your PTU reservation rate
  • Azure AI Search queries (if used): billed separately at Search tier rates

The practical implication is that Standard agentic workflow costs scale with model usage, not with workflow execution count. A loop that runs five iterations and calls GPT-4o five times costs five times more in model tokens than a loop that resolves in one iteration.

Consumption

Consumption agentic workflows use a pay-as-you-go model. Agent loop pricing is based on the number of tokens each agent action uses and appears as Enterprise Units on your bill. This is a different billing unit from the standard Consumption action executions — each token consumed by the agent is metered separately.

The Consumption agent loop is also subject to throttling based on token usage — unlike Standard, which is constrained only by the App Service Plan compute capacity.

For production workloads with predictable, high-volume agent loop usage, Standard with a PTU Azure OpenAI deployment is the more cost-predictable option. For low-volume or experimental workloads, Consumption pay-as-you-go avoids the fixed App Service Plan cost.

Known limits for agentic workflows

Before going to production, be aware of the current platform limits:

Tool constraints — tools can only contain actions, not triggers. A tool must start with an action and always contains at least one action. Control flow actions (conditions, loops, switches) are not supported inside tools. A tool only works inside the agent loop where it is defined — it cannot be shared across agent actions.

Consumption-specific limits — Consumption agentic workflows can only be created in the Azure portal, not Visual Studio Code. The AI model can come from any region, so data residency for a specific region is not guaranteed for data the model handles. The agent action is throttled based on token usage.

Agent history — by default the agent loop accumulates the full conversation history across iterations. For long-running loops this can push the context length toward the model’s limit. Enable agent history reduction in the agent action’s Settings tab to manage this. The default strategy is token count reduction with a ceiling of 128,000 tokens — adjust this based on your model’s context window and your scenario’s complexity.

Deploying agentic workflows through a DevOps pipeline

Standard logic apps are built on the Azure Functions runtime and deploy the same way as any other Standard logic app — via zip deploy, Azure Pipelines, or GitHub Actions. The workflow definitions are JSON files on disk, making them version-controllable and deployable through standard CI/CD patterns.

What to include in source control

For an agentic workflow project, the key files to version-control are:

  • sequential-agents/workflow.json — the sequential agent loop definition
  • sample/workflow.json — the autonomous agent from Post 2
  • mcp-research/workflow.json — the MCP research workflow from Post 4
  • connections.json — connection references (without credentials — those go in Key Vault)
  • host.json — Logic Apps host configuration
  • local.settings.json — local development settings (excluded from source control, .gitignore)

Deploying with Azure CLI

The simplest production deployment from a CI/CD pipeline uses the Azure CLI:

# Zip the logic app project zip -r la-agent-loop.zip . -x "*.git*" "local.settings.json"

# Deploy to Azure az logicapp deployment source config-zip \ --name la-agent-loop \ --resource-group rg-ai-solutions \ --src la-agent-loop.zip

Environment-specific configuration

Agent connections and app settings differ between development and production environments. Use Azure CLI or Bicep to set environment-specific app settings as part of the deployment pipeline:

az logicapp config appsettings set \ --name la-agent-loop \ --resource-group rg-ai-solutions \ --settings \ agent_openAIEndpoint="https://aoai-prod.openai.azure.com/" \ OPENAI__endpoint="https://aoai-prod.openai.azure.com/"

This keeps environment-specific values out of source control and injected at deploy time — the standard twelve-factor app pattern applied to Logic Apps.

Closing the series

This post closes a seven-part series on Azure Logic Apps agent loop production operations, from first principles through to observability, pricing, and DevOps deployment. The series covered:

  1. Why the agent loop is a different design paradigm from conventional workflow automation
  2. The anatomy of a single agent loop — trigger, instructions, model, and tools
  3. Autonomous versus conversational agentic workflows: when to use each
  4. Building tools: connectors, custom connectors, and MCP servers
  5. Multi-agent patterns: prompt chaining, routing, handoff, and orchestrator-workers
  6. Securing agentic workflows: Easy Auth, Managed Identity, and Key Vault
  7. Observability, pricing, and production operations — this post

The agent loop is still a rapidly evolving capability in Azure Logic Apps. The platform limitations documented throughout this series Foundry Models connection persistence, API Center MCP wizard regional constraints, Foundry OpenAPI tool network restrictions will be addressed in future platform releases. The architectural patterns, however, are stable: the four building blocks of an agent loop, the three tooling layers, the four multi-agent patterns, and the two-concern security model will remain the right mental model for this platform regardless of how the surface-level tooling evolves.

Azure Logic Apps Agentic Workflow Security in Production

Part 6 of 7 in the Logic Apps Agent Loop series

Part 5 covered multi-agent patterns in the Azure Logic Apps agentic workflow series. Each pattern extends your agent’s reach, but that reach comes with a security cost. The more capable and connected your agent, the more important it is to understand who can call it and under what conditions. This post covers the expanded caller surface, the developer key’s limitations, and the full production security stack.

Conventional Logic Apps workflows have a bounded caller surface. The callers are known systems: a scheduler, a service bus, and an HTTP client you control. The authentication model is straightforward: SAS tokens, Managed Identity, and IP filtering. Agentic workflows fundamentally change this, particularly conversational ones. When you expose a chat interface to external callers, those callers can be people, other agents, MCP servers, or automation clients from networks you do not control. The security model has to change with the threat model.

Two-column diagram showing the security model for Azure Logic Apps agentic workflows. Left column shows the caller surface: human users via external chat client, external agents with dynamic unknown callers, MCP servers on untrusted networks, automation clients for CI/CD, and a developer key marked as portal testing only and not for production. Arrows from all caller types point toward the right column. Right column shows the security stack from top to bottom: Entry via Easy Auth with Microsoft Entra ID and Conditional Access, Logic app Standard running agentic workflows and agent loops, Managed Identity for backend authentication to Azure OpenAI, AI Search, and Storage, Azure Key Vault for secrets that cannot use Managed Identity, and Consumption OAuth 2.0 with Entra ID agent auth policy at the bottom. Legend shows teal for auth layers, purple for workflow and caller, coral for avoid in production.
Figure 1 — The two security concerns for Azure Logic Apps agentic workflows. The caller surface (left) expands significantly compared to conventional workflows. Human users, external agents, MCP servers, and automation clients can all reach the workflow endpoint from networks you do not control. The developer key used during portal development is explicitly not suitable for any of these caller types. The security stack (right) addresses the expanded surface area in two directions: Easy Auth with Microsoft Entra ID secures who can invoke the workflow, while Managed Identity and Key Vault secure what the workflow can call, without storing credentials in app settings.

The expanded caller surface

The shift from nonagentic to agentic workflows introduces a qualitatively different caller population. In a nonagentic workflow the trigger is called by a known system at a known time for a known reason. In a conversational agentic workflow the trigger is called by:

  • Human users interacting through an external chat client
  • External agents invoking the workflow as a tool
  • MCP servers routing requests through the workflow
  • Automation clients from untrusted or unknown networks

Each of these caller types introduces different identity, trust, and access control requirements. A billing system calling a webhook is easy to reason about. An external agent calling your workflow from an unknown network at unpredictable intervals is not.

This expanded surface area is why Microsoft’s documentation draws a sharp distinction between the developer key used during design and testing in the Azure portal and proper production authentication. Understanding that distinction is the starting point for securing any agentic workflow.

The developer key: what it is and what it is not

Understanding the developer key’s limitations is the starting point for any serious Azure Logic Apps agentic workflow security implementation. When you test a conversational agentic workflow in the Logic Apps designer, the Azure portal authenticates your test calls using a developer key. The developer key is a convenience mechanism that lets you skip manual authentication setup during development. It fires automatically when you run a workflow, call a Request trigger, or interact with the integrated chat interface.

The developer key has five hard limitations that make it unsuitable for production:

  • It is not a substitute for Easy Auth, Managed Identity, federated credentials, or signed SAS callback URLs.
  • In addition, it is designed for large or untrusted caller populations, agent tools, or automation clients.
  • It is also not a per-user authorization mechanism; it has no granular scopes or roles.
  • And finally, it is not governed by Conditional Access policies at the request execution layer, only at the portal sign-in layer. And it is not intended for programmatic or CI/CD usage.

The developer key is linked to a specific user and tenant based on an Azure Resource Manager bearer token. Because of that binding, you cannot distribute it externally. It is, in the Microsoft documentation’s own framing, a mechanism for quick testing before you formalize authentication, not a path to production.

Azure Logic Apps agentic workflow security: Standard versus Consumption

The right production authentication mechanism depends on your Logic Apps hosting model.

Setting up Managed Identity for backend connections

Easy Auth secures who can call your agentic workflow. Managed Identity secures what your workflow can call. These are two distinct security concerns and both need to be addressed in production.

When your agent invokes a tool, Azure OpenAI, Azure AI Search, a storage account, or a Service Bus namespace, that call needs to be authenticated. The default approach during development is often to store an API key or connection string in app settings. In production, replace these with Managed Identity connections wherever possible. This removes credentials from app settings entirely. The logic app authenticates to backend services using its Azure AD identity, which is governed by RBAC, auditable, and revocable without rotating keys.

  1. Go to your la-agent-loop resource → IdentitySystem assigned → turn Status to On
  2. Save — Azure assigns a service principal to the logic app
  3. In each target resource (Azure OpenAI, AI Search, Storage), go to Access control (IAM)Add role assignment
  4. Assign the appropriate role to the logic app’s Managed Identity:
    • Azure OpenAI: Cognitive Services OpenAI User
    • Azure AI Search: Search Index Data Reader
    • Azure Blob Storage: Storage Blob Data Reader
  5. In the Logic Apps connections, switch from API key authentication to Managed Identity for each backend service where possible.

Note: Managed Identity authentication for the agent model connection is only supported when the model type is AzureOpenAI. If your workflows use the MicrosoftFoundry model type, as in this series, the agent connection must use Key authentication. Managed Identity remains the right choice for all other backend connections such as Azure AI Search, Blob Storage, and Service Bus.

Azure portal Identity blade for the la-agent-loop Standard logic app. The System assigned tab is selected, Status is set to On, and the Object principal ID is shown as 8db32242-e936-4d84-a44a-6b39d37f24f7. An Azure role assignments button is visible under Permissions.
Figure 2 — System-assigned Managed Identity enabled on the la-agent-loop Standard logic app. Once enabled, Azure registers the logic app as a service principal in Microsoft Entra ID. Click Azure role assignments to assign the appropriate RBAC roles to each backend resource: Cognitive Services OpenAI User for Azure OpenAI and Search Index Data Reader for Azure AI Search, so the agent can authenticate to those services without storing any credentials in app settings.

Setting up Easy Auth for your Azure Logic Apps agentic workflow

For Standard logic apps, the production authentication path is Easy Auth, also known as App Service Authentication. Easy Auth is an App Service platform feature that sits in front of your logic app and enforces identity-based authentication on every incoming request before it reaches your workflow.

When you enable Easy Auth on a Standard logic app, external callers, whether human users, external agents, or MCP servers, must present a valid identity token. Easy Auth validates the token against Microsoft Entra ID before allowing the request through. This gives you full Conditional Access policy enforcement, per-user identity, token revocation, and audit logging, the full production security stack.

To set up Easy Auth on a Standard logic app:

  1. In the Azure portal, open your la-agent-loop logic app resource
  2. Navigate to Authentication in the left sidebar under Settings
  3. Click Add identity provider
  4. Select Microsoft as the identity provider
  5. Under App registration, select an existing registration or choose Create new app registration and name it la-agent-loop-auth
  6. Under Supported account types, select Current tenant — single tenant for internal workloads
  7. Set Unauthenticated requests to HTTP 401 Unauthorized: recommended for APIs
  8. Leave Token store enabled
  9. Click Add

Note: Easy Auth operates at the App Service host level, before the Logic Apps runtime processes the request. Authentication failures are rejected at the infrastructure layer with a 401 the workflow never executes and no run history entry is created for unauthenticated calls.

Azure portal Authentication blade for the la-agent-loop Standard logic app. Authentication settings show App Service authentication as Enabled, Restrict access set to Require authentication, and Unauthenticated requests set to Return HTTP 401 Unauthorized. The Identity provider section shows Microsoft with app registration la-agent-loop-auth and client ID bc8d8407-a79e-4a18-be48-f0fc54fa4966.
Figure 3 — Easy Auth configured on the la-agent-loop Standard logic app. App Service authentication is enabled, unauthenticated requests return HTTP 401 Unauthorized, and Microsoft Entra ID is registered as the identity provider via the la-agent-loop-auth app registration. Any external caller, human user, external agent, or MCP servermust now present a valid Entra ID token before the Logic Apps runtime processes the request.

Consumption: OAuth 2.0 with Microsoft Entra ID

For Consumption logic apps, configure an agent authorization policy on the logic app resource using OAuth 2.0 with Microsoft Entra ID. This provides equivalent identity enforcement to Easy Auth for the Consumption hosting model. For the full configuration steps, see Create conversational agent workflows in Azure Logic Apps on Microsoft Learn.

Key Vault for secrets that cannot use Managed Identity

Not every connection in an Azure Logic Apps agentic workflow supports Managed Identity. Where API keys or connection strings are unavoidable, store them in Azure Key Vault and reference them from Logic Apps app settings using the Key Vault reference syntax:

@Microsoft.KeyVault(SecretUri=https://your-keyvault.vault.azure.net/secrets/your-secret/)

This keeps credentials out of app settings in plain text, provides centralized rotation, and gives you audit logs of every secret access. The Standard logic app accesses Key Vault using its Managed Identity; no separate credentials are needed for the vault itself.

Network controls for Standard workflows

Standard logic apps run on the App Service infrastructure, which gives you network-level controls that Consumption workflows do not have:

Private endpoints allow your logic app to receive inbound traffic only from within a virtual network, removing public internet exposure entirely. This is the recommended configuration for production agentic workflows that serve internal users or agents.

VNet integration allows your logic app to make outbound calls to services within a virtual network, including on-premises systems, private Azure services, and internal APIs, without exposing those services to the internet.

IP access restrictions let you restrict inbound traffic to specific IP ranges at the App Service level, providing a lighter-weight alternative to private endpoints for scenarios where full network isolation is not required.

For production agentic workflows processing sensitive data, patient records, financial data, internal business intelligence, and private endpoints with VNet integration is the right starting point.

Azure Logic Apps agentic workflow security checklist

Before going live with any agentic workflow:

  • Easy Auth configured with Microsoft Entra ID (Standard) or OAuth 2.0 agent authorisation policy (Consumption)
  • Developer key not used or referenced in any production caller
  • Managed Identity enabled on the logic app and assigned to all backend services
  • API keys and connection strings moved to Key Vault references
  • Private endpoints configured for Standard workflows handling sensitive data
  • Conditional Access policies applied to the Entra ID app registration backing Easy Auth
  • Run history access restricted to authorised operations personnel

What comes next

The final post in this series concludes with operations: Application Insights integration, agent loop pricing, run history analysis, and deployment of agentic workflows through a CI/CD pipeline. Part 7 covers everything you need to run agent loops confidently in production.

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.

Azure CLI output showing successful deployment of the Microsoft Foundry Citadel Governance Hub including APIM, Azure OpenAI chat and embedding model deployments, private endpoints, and Logic App in Sweden Central.
The AI Hub Gateway Solution Accelerator was deployed successfully in Azure Sweden Central after 21 hours and31 minutes, provisioning APIM, Azure OpenAI, Content Safety, Application Insights, private endpoints, and the usage processing Logic App.

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
PowerShell output showing a successful chat completion response from the Microsoft Foundry Citadel APIM gateway in Azure Sweden Central, with content filter results, prompt filter results, and token usage confirmed.
Validating the Citadel Governance Hub by calling the APIM gateway endpoint via PowerShell, the response confirms gpt-4o-2024-11-20 routing, Content Safety filtering, PII redaction, and token usage tracking are all active.

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.

PowerShell output showing a successful end-to-end chat completion from the Citadel agent spoke through the Azure APIM Governance Hub, confirming spoke to hub routing, content filter results, and token usage tracking in Sweden Central.
End-to-end validation of the Citadel hub-and-spoke setup: a request from the agent spoke routes through the APIM Governance Hub in Sweden Central, returning a successful gpt-4o response, with Content Safety filtering and token usage tracking confirmed.

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.

Anatomy of an Agent Loop in Azure Logic Apps

Part 2 of 7 in the Logic Apps Agent Loop series

Part 1 explained why the Azure Logic Apps agent loop is a different design paradigm from conventional workflow automation. This post gets hands-on with the anatomy of that loop. We will look at the four building blocks that make up every agent loop trigger, instructions, connected model, and tools, and walk through how to wire them together in a Standard logic app.

By the end of this post you will have a working autonomous agent that accepts a prompt from a trigger, reasons over it using Azure OpenAI, invokes a connector action as a tool, and returns a result. The run history will show you exactly how the loop iterated.

The Azure Logic Apps agent loop: four building blocks

Before opening the designer, it helps to have a clear mental model of what you are assembling. Every Azure Logic Apps agent loop consists of four parts.

Trigger

The trigger starts the workflow, exactly as it does in any nonagentic Logic Apps workflow. For an autonomous agent, this can be any supported trigger an HTTP request, a timer, a Service Bus message, a new email, or anything else in the connector library. The trigger’s output becomes the initial input to the agent: the prompt or data the model will reason over.

Instructions

Instructions are the system prompt for the agent. You provide them as a block of natural language text in the agent action’s configuration pane. They define the agent’s role, what it can and cannot do, how it should respond, and any constraints it should observe. A well-written instructions block is the single most important factor in how well the agent performs. Think of it as the job description you hand to the model at the start of every run.

Connected model

The agent needs a language model to reason with. In Standard Logic Apps, you connect the agent to an Azure OpenAI Service resource and specify the model deployment to use — typically a GPT-4o deployment. The agent sends the instructions, the trigger input, and the results of any tool calls to the model at each iteration of the loop. The model’s response tells the agent what to do next.

Tools

A tool is a sequence of one or more connector actions that the agent can choose to invoke. You build tools directly in the Logic Apps designer by adding actions from the connector gallery inside the agent action. Each tool gets a name and a description — the model uses these to decide which tool to call and when. A single agent can have multiple tools. An agent with no tools can still respond to prompts using the model’s built-in knowledge, but it cannot take action on external systems.

The diagram below shows how these four parts fit together inside a single agent loop execution.

Anatomy of an Azure Logic Apps agent loop — trigger, instructions, model, and tools
Figure 1 — Every Azure Logic Apps agent loop consists of four building blocks: a trigger that starts the workflow, instructions that define the agent’s role, a connected model (Azure OpenAI / GPT-4o) that reasons over each iteration, and tools built from connector actions. The loop cycles through Think, Act, and Observe until the model determines the task is complete.

Building your first agent loop in Azure Logic Apps

The demo for this post is deliberately simple: an agent that receives a topic via an HTTP trigger, uses Azure OpenAI to generate a summary, and returns the result to the caller. One trigger, one model, one tool is enough to see all four building blocks in action and to read a meaningful run history.

Prerequisites

  • A Standard logic app resource deployed in Azure
  • An Azure OpenAI Service resource with a GPT-4o model deployment
  • Contributor access to both resources

Step 1: Create the workflow

In the Azure portal, open your Standard logic app and select Workflows from the sidebar. Choose Add, then select Autonomous Agents as the workflow type. Give the workflow a name and select Stateful. Logic Apps creates a new workflow with an empty agent action already in place.

Step 2: Configure the trigger

The autonomous agent workflow template starts with a When a HTTP request is received trigger by default. Leave the method as POST. In the request body JSON schema, add a single property: topic of type string. This is the input the agent will work with.

Step 3: Write the instructions

Select the agent action in the designer to open its configuration pane. On the Parameters tab, find the Instructions field. Enter something like the following:

You are a research assistant. When given a topic, use the available tools to retrieve relevant information and return a concise summary of no more than three sentences. Always cite your source.

Keep instructions specific and bounded. Vague instructions produce unpredictable behaviour. The model will take the instructions literally, so precision matters.

Step 4: Connect the model

Still on the Parameters tab, select Add connection under the model configuration section. Choose Azure OpenAI Service, select your resource, and choose your GPT-4o deployment. Logic Apps establishes the connection and stores it against the workflow.

Step 5: Add a tool

Inside the agent action, select Add a tool. This opens the connector gallery filtered to actions that can be used as tools. For this demo, add the HTTP action as a tool — name it search_web, give it the description “Retrieves content from a given URL”, and configure it to accept a URL as input. In a production scenario you would use Azure AI Search or a more capable connector here; the HTTP action keeps the demo self-contained.

Step 6: Save and run

Save the workflow. Use a REST client to POST a JSON body like {"topic": "Azure Logic Apps agent loop"} to the workflow’s trigger URL. The agent fires, the model reasons over the instructions and the topic, invokes the search tool, and returns a summary.

Logic Apps designer view of an autonomous agent workflow. The canvas shows an HTTP request trigger connected to an Agent action containing a Tool with an HTTP action inside. The right pane shows the Agent parameters: AI model set to GPT-4o via Foundry Models, instructions for the research assistant role, and the topic dynamic value wired as user instructions item 1.
Figure 2 — The completed agent configuration in the Logic Apps designer. The Agent action is connected to GPT-4o via Foundry Models, the instructions define the research assistant role and output format, and the topic value from the HTTP trigger is passed in as the user instruction. The Tool contains a single HTTP action that the agent can invoke to retrieve content from a given URL.

Reading the run history

he run history is where the Azure Logic Apps agent loop becomes visible. Open the workflow’s Run history and select the latest run. You will see the trigger, followed by the agent action. Expand the agent action and you will find each iteration of the loop shown as a numbered step: the model’s reasoning output, the tool call with its inputs and outputs, and the model’s decision on whether to loop again or return a final answer.

This is the key difference from a nonagentic run history. In a conventional workflow, the run history shows a flat list of actions. In an agent loop, it shows a nested, iterative structure the model’s chain of thought made visible.

Run history of a Logic Apps autonomous agent workflow completed in 9.21 seconds, showing the HTTP trigger, a first agent iteration that invoked the HTTP tool in 3.7 seconds, and a second agent iteration that sent the final chat message.
Figure 3 — The run history of the minimal autonomous agent from this post. The loop ran two iterations: the first agent step (3.9s) reasoned over the topic prompt and invoked the HTTP tool (0.6s); the second agent step (3.2s) observed the result and composed the final response. The canvas shows iteration 1 of 3 steps — trigger, tool, and HTTP action — all succeeded in 9.21 seconds total.

For a simple prompt, you may see a single iteration. For a more complex task involving multiple tool calls, you will see the loop unfold across three, five, or more steps. Each step shows exactly what the model decided and why.

Standard versus Consumption: model connections

In Standard logic apps, you configure the model connection yourself — selecting an Azure OpenAI Service resource and specifying the deployment. This gives you full control over which model version you use, where it is hosted, and how it is secured via Managed Identity.

In Consumption logic apps (currently in public preview), the model connection is handled via Microsoft Foundry and the configuration is more constrained. For any production workload, Standard remains the right choice.

What comes next

The agent in this post is autonomous it runs without human interaction, triggered by an HTTP call and returning a result when done. That covers a wide range of integration scenarios, but not all of them. Some tasks require a back-and-forth with a user: a support conversation, a guided data-entry flow, a multi-turn research session.

The next part will cover exactly that distinction, autonomous versus conversational agentic workflows, and walk through when to choose each pattern and what changes in the designer when you do.

Why the Agent Loop Changes Everything in Azure Logic Apps

Part 1 of 7 in the Logic Apps Agent Loop series

The Azure Logic Apps agent loop introduces a fundamentally different way to design workflows on the platform. While conventional Logic Apps workflows follow a fixed sequence of steps defined at design time, the agent loop delegates reasoning to a large language model at runtime, looping through think, act, and observe cycles until a task is complete. This post opens a seven-part series on building agentic workflows in Logic Apps. It starts with the question that matters most: why does this change anything?

For years, Azure Logic Apps has been the platform of choice for integration architects who need to orchestrate business processes across cloud services and on-premises systems. You build a workflow, wire up triggers and actions, define your conditions, handle your errors, deploy, and move on. The flow is predictable (deterministic): given the same inputs, it does the same thing every time. That predictability is the point.

The agent loop breaks that contract, deliberately and usefully.

With the introduction of agentic workflows in Azure Logic Apps, Microsoft has extended the platform from a fixed automation engine into something that can reason, adapt, and decide. At its core, the agent loop drives this shift. It is a repeating process: the connected language model thinks through a problem, selects a tool, acts on the result, and decides whether the task is done.Unlike a conventional workflow, there is no hardcoded sequence of steps. Instead, the model determines the path based on the task.

This post is the opening of a seven-part series on building agentic workflows in Azure Logic Apps. Before going hands-on with triggers, connectors, and multi-agent patterns in later posts, this one makes the case for why the agent loop matters and what it fundamentally changes about how you think about workflow design.

How the Azure Logic Apps agent loop differs from nonagentic workflows

Nonagentic Logic Apps workflows are excellent at exactly the kind of work they were designed for: stable, predictable, repeatable processes. An approval workflow, an ETL pipeline, and a B2B message exchange are all scenarios where the path through the workflow is known in advance. The trigger fires, the conditions evaluate, the actions execute in sequence, and the run history tells you exactly what happened and why.

The challenge arises when the environment you are integrating with is unstable or unpredictable. When incoming data is unstructured. Or when the right action depends on context that cannot be captured in a condition expression. Or when you need to handle a customer query that could go a dozen different directions depending on what the customer actually says.

These are the cases where deterministic workflows buckle. You end up building sprawling switch-case structures, hardcoding edge cases as branches, and constantly patching the workflow every time a new variation appears. The workflow becomes a maintenance problem rather than a solution.

Agentic workflows excel in dynamic environments where unexpected events occur, the choice of the right tool relies on the input, and the system must manage unstructured data without specific instructions for each variant.

The agent loop: Think, Act, Learn

How the agent loop works: Think, Act, Learn

The Azure Logic Apps agent loop follows a three-step process.

Think. The agent collects available information: task instructions, prior inputs, and previous tool results. It then passes all of this to the connected language model.The model reasons over the context and decides what to do next: invoke a tool, ask a follow-up question, or return a final answer.

Act. In Logic Apps, tools are actions drawn from 400+ connectors. These include Azure OpenAI, Azure AI Search, Office 365, and custom APIs. Once the action runs, the result feeds back into the next cycle.

Optionally, the loop adapts. The agent can use feedback or external signals to adjust its behaviour over time, though this is the most advanced capability and not required for most workflows.

Iterations, not instructions

This loop continues think, act, observe, decide until the model determines the task is complete. You can change the number of iterations as needed. A simple query might resolve in one loop. A complex multi-step task might require five or ten.

The diagram below shows the difference between a conventional non-agentic workflow, which follows a linear sequence of predetermined steps, and the agent loop, which dynamically iterates until the model determines that the task is complete.

Figure 1 — A conventional nonagentic workflow follows a fixed path defined at design time (left). The agent loop iterates dynamically at runtime: the LLM thinks, acts, observes the result, and decides whether to loop again or return a final answer (right).

Agent versus nonagentic: a structural comparison

The difference is not just philosophical. It shows up in how you design, deploy, and maintain the workflow.

In a nonagentic workflow, the logic architect owns the decision tree. Every branch, every condition, every action path is explicitly modelled. This is powerful for known, bounded scenarios, but it places all the reasoning burden on the architect at design time.

In an agentic workflow, the reasoning is delegated to the model at runtime. The architect’s job shifts: instead of modelling every path, you define the agent’s instructions, give it the right tools, and trust the model to navigate the task. This is a different skill and a different mindset closer to prompt engineering and system design than to traditional workflow modelling.

The Microsoft documentation puts it plainly: agentic workflows can adapt to environments where unexpected events happen, choose which tools to use based on prompts and available data, and handle unstructured data at a level of flexibility that nonagentic workflows simply cannot match. Moreover, nonagentic workflows function best in stable environments with static, predictable, repetitive tasks.

Neither is universally better. They address different problems. But for integration architects, the arrival of the agent loop means Logic Apps can now cover territory that previously required a custom-coded application or a fully separate agent framework.

Standard versus Consumption: what you need to know now

Azure Logic Apps offers two hosting models: Standard (single-tenant, runs on Azure Functions runtime) and Consumption (multitenant, pay-per-execution). Agentic workflows are fully available in Standard. Consumption support is in public preview as of early 2026 and carries some restrictions.

For production agentic workloads, Standard is the right choice today. The rest of this series will use Standard throughout, with notes where the Consumption behaviour differs.

What this series covers

The seven posts in this series move from concept to production:

  1. Why the agent loop changes everything — this post
  2. Anatomy of an agent loop — instructions, the connected model, tool calls, and how the loop iterates
  3. Autonomous versus conversational workflows — choosing between unattended execution and human-in-the-loop patterns
  4. Building tools for the agent — connectors, custom connectors, and MCP servers as tool providers
  5. Multi-agent patterns — handoffs, orchestrators, and sequential agent loops
  6. Securing agentic workflows — authentication, the expanded caller surface, and Easy Auth
  7. Observability, pricing, and running in production — Application Insights, agent loop pricing, and DevOps deployment

The next post gets hands-on: we will look at the anatomy of a single agent loop in the Logic Apps designer, walk through the instructions pane, wire up Azure OpenAI as the model, and watch the run history to see how the iterations unfold.

AWS European Sovereign Cloud Launches—But Does It Solve the Real Problem?

Earlier, AWS officially launched its European Sovereign Cloud, backed by a €7.8 billion investment in Brandenburg, Germany. The infrastructure is physically and logically separated from AWS global regions, managed by a new German parent company (AWS European Sovereign Cloud GmbH), and staffed exclusively by EU residents. On paper, it checks every compliance box for data residency and operational sovereignty. AWS CEO Matt Garman called it “a big bet” for the company, and it is. The question is whether it’s the right bet for Europe.

European Sovereign Cloud: Real Isolation, Real Trade-offs

The technical separation is genuine. An AWS engineer who deployed services to the European Sovereign Cloud confirmed on Hacker News that proper boundaries exist—U.S.-based engineers can’t see anything happening in the sovereign cloud. To fix issues there, they play “telephone” with EU-based engineers. The infrastructure uses the partition name *aws-eusc* and the region name *eusc-de-east-1*, which are completely separate from AWS’s global regions. All components, IAM, billing systems, and Route 53 name servers using European Top-Level Domains—remain within EU borders.

But this isolation comes with costs. As that same engineer warned, “it really slows down debugging issues. Problems that would be fixed in a day or two can take a month.” This is the sovereignty trade-off in practice: more control, less velocity. The service launches with approximately 90 AWS services, not the full catalog. Plans exist to expand into sovereign Local Zones in Belgium, the Netherlands, and Portugal, but this remains a subset of AWS’s offerings globally.

For some workloads, this trade-off makes sense. For others, it’s a deal-breaker.

Why the European Sovereign Cloud Can’t Escape U.S. Jurisdiction

Here’s the uncomfortable truth that AWS’s marketing carefully sidesteps: technical isolation doesn’t create legal isolation. AWS, headquartered in America, remains subject to U.S. jurisdiction. The CLOUD Act allows U.S. authorities to compel U.S.-based technology companies to provide data, regardless of where it is stored globally. Courts can require parent companies to produce data held by subsidiaries.

This isn’t theoretical hand-wraving. Microsoft had to admit in a French court that it cannot guarantee data sovereignty for EU customers. When Airbus executive Catherine Jestin discussed AWS’s sovereignty claims with lawyers late last year, she said: “I still don’t understand how it is possible” for AWS to be immune to extraterritorial laws.

Cristina Caffarra, founder of the Eurostack Foundation and competition economist, puts it bluntly:

A company subject to the extraterritorial laws of the United States cannot be considered sovereign for Europe. That simply doesn’t work.

The AWS response focuses on technical controls—encryption, the Nitro System preventing employee access, and hardware security modules. These are important safeguards, but they don’t address the core legal issue. If a U.S. court orders Amazon.com Inc. to produce data, technical barriers become legal obstacles the parent company must overcome, not protections.

Europe’s European Sovereign Cloud Strategy: The Cloud and AI Development Act

AWS’s launch comes as Europe finalizes its own legislative response. The EU Cloud and AI Development Act, expected in Q1 2026, aims to strengthen Europe’s autonomy over cloud infrastructure and data. As Christoph Strnadl, CTO of Gaia-X, explains:

For critical data, you will never, ever use a US company. Sovereignty means having strategic options — not doing everything yourself.

The Act is part of the EU’s Competitiveness Compass and addresses a fundamental problem: Europe’s 90% dependency on non-EU cloud infrastructure, predominantly American companies. This dependency isn’t just about data residency—it’s about strategic autonomy. When essential services depend on infrastructure governed by foreign law, questions arise about jurisdiction, resilience, and what happens during geopolitical disruption.

Current estimates indicate that AWS, Microsoft Azure, and Google Cloud collectively control over 60% of the European cloud market. European providers account for only a small share of revenues. The Cloud and AI Development Act aims to establish minimum criteria for cloud services in Europe, mobilize public and private initiatives for AI infrastructure, and create a single EU-wide cloud policy for public administrations and procurement.

Importantly, Brussels isn’t seeking to ban non-EU providers. As Strnadl notes:

Sovereignty does not mean you have to do everything yourself. Sovereignty means that for critical things, you have strategic options.

Gaia-X and the European Sovereign Cloud: A Lesson in Sovereignty Washing

Europe has been down this path before. Gaia-X, launched in 2019, intended to create a trustworthy European data infrastructure. Then American companies lobbied to be included. Once Microsoft, Google, and AWS were inside, critics argue, Gaia-X lost its purpose. The fear now is that AWS’s European Sovereign Cloud represents sophisticated “sovereignty washing”—placing datacenters on European soil without resolving the fundamental legal issue.

Recent European actions suggest growing awareness of this problem. Austria, Germany, France, and the International Criminal Court in The Hague are taking concrete steps toward genuine digital independence. These aren’t just policy statements—they’re actual migrations away from U.S. hyperscalers toward European alternatives.

European Sovereign Cloud Adoption: No Full Migration in 2026

Forrester predicts that no European enterprise will fully shift away from U.S. hyperscalers in 2026, citing geopolitical tensions, volatility, and new legislation, such as the EU AI Act, as barriers. The scale of dependency is too deep, the feature gap too wide, and the migration costs too high for rapid change.

Gartner forecasts European IT spending will grow 11% in 2026 to $1.4 trillion, with 61% of European CIOs and tech leaders wanting to increase their use of local cloud providers. Around half (53%) said geopolitical factors would limit their use of global providers in the future. The direction is clear, even if the pace remains uncertain.

This creates a transitional period where organizations must make pragmatic choices. For non-critical workloads, AWS’s European Sovereign Cloud may be sufficient. For truly sensitive data—government communications, defense systems, critical infrastructure—organizations need genuinely European alternatives: Hetzner, Scaleway, OVHCloud, StackIT by Schwarz Digits.

What AWS’s European Sovereign Cloud Actually Delivers

Let’s be precise about what AWS European Sovereign Cloud achieves. It provides:

  • Data residency within the EU
  • Operational control by EU residents  
  • Governance through EU-based legal entities
  • Technical isolation from the global AWS infrastructure
  • An advisory board of EU citizens with independent oversight

What it doesn’t provide is independence from U.S. legal jurisdiction. For compliance requirements focused purely on data residency and operational transparency, this may be sufficient. For organizations requiring protection from U.S. government data requests, it fundamentally isn’t.

As Eric Swanson from CarMax noted in a LinkedIn post:

Sovereign cloud offerings do not override the Patriot Act. They mainly reduce overlap across other contexts: data location, operational control, employee access, and customer jurisdiction.

European Sovereign Cloud and Strategic Autonomy: Not Autarky

Europe’s path forward isn’t about digital isolationism. As Strnadl emphasizes, technology adoption that involves a paradigm shift doesn’t happen in two years. The challenge is adoption, not frameworks. “Cooperation needs trust,” he says, “and trust needs a trust framework.”

The Cloud and AI Development Act, expected this quarter, will provide that framework. It will set minimum criteria, promote interoperability, and establish procurement rules that favor sovereignty for critical workloads. The question for organizations is: what constitutes critical?

For email, public administration, political communication, and defense systems, the answer should be obvious. These require European alternatives. For other workloads, AWS’s European Sovereign Cloud may strike an acceptable balance between capability and control.

The Bottom Line

AWS’s €7.8 billion investment is real. The technical isolation is real. The economic contribution to Germany’s GDP (€17.2 billion over 20 years) is real. What’s also real is that Amazon.com Inc., a U.S. company, ultimately controls this infrastructure and remains subject to U.S. law.

For organizations seeking compliance checkboxes and data residency guarantees, AWS European Sovereign Cloud delivers. For organizations requiring genuine independence from U.S. legal jurisdiction, it remains fundamentally insufficient. That’s not a criticism of AWS’s engineering—it’s a statement of legal reality.

The sovereignty question Europe faces isn’t technical. It’s strategic: do we accept managed dependency or build genuine autonomy? AWS offers the former. Only European alternatives can provide the latter.

The market will decide which answer matters more.

Agentic Orchestration: The Evolution of SOA

For decades, integration professionals have shaped the digital backbone of enterprises from EAI to SOA to microservices. Today, agentic orchestration marks the next step in that evolution: transforming how we compose, coordinate, and reason across enterprise services. This isn’t a replacement for what we know; it’s an intelligent upgrade to it.

We built the bridges, the highways, and the intricate railway networks of the digital world. Yet, let’s be honest—for all our sophistication, our orchestrations often felt like a meticulous, rigid dance.

Enter Agentic Orchestration. This isn’t just another buzzword. It’s a profound shift, an evolution that takes the core principles of SOA and infuses them with intelligence, dynamism, and a remarkable degree of autonomy. For the seasoned integration architect and engineer, this isn’t about replacing what we know—it’s about enhancing it, elevating it to a new plane of capability.

How SOA Composites Differ from Agentic Orchestration

Cast your mind back to the golden age of SOA. For those of us in the Microsoft ecosystem, this meant nearly two and a half decades with BizTalk Server as our workhorse, our battleground, our canvas. We diligently crafted composite services using orchestration designers, adapters, and pipelines. Others wielded BPEL and ESBs, but the principle was the same. Our logic was clear, explicit, and, crucially, deterministic.

If a business process required validating a customer, then checking inventory, and finally processing an order, we laid out that sequence with unwavering precision—whether in BizTalk’s visual orchestration designer or in BPEL code:

XML

<bpel:sequence name="OrderFulfillmentProcess">
  <bpel:invoke operation="validateCustomer" partnerLink="CustomerService"/>
  <bpel:invoke operation="checkInventory" partnerLink="InventoryService"/>
  <bpel:invoke operation="processPayment" partnerLink="PaymentService"/>
</bpel:sequence>

Those of us who spent years with BizTalk know this dance intimately: the Receive shapes, the Decision shapes, the carefully constructed correlation sets, the Scope shapes wrapped around every potentially fragile operation. We debugged orchestrations at 2 AM, optimized dehydration points, and became masters of the Box-Line-Polygon visual language.

This approach delivered immense value. It brought order to chaos, reused services, and provided a clear, auditable trail. However, its strength was also its weakness: rigidity. Any deviation or unforeseen circumstance required a developer to step in, modify the orchestration, and redeploy. The system couldn’t “think” its way around a problem it merely executed a predefined script a well-choreographed ballet, beautiful but utterly inflexible to improvisation.

Agentic Orchestration: From Fixed Scripts to Intelligent Collaboration

Now, imagine an orchestration that doesn’t just execute a script, but reasons. An orchestration where the “participants” are not passive services waiting for an instruction, but intelligent agents equipped with goals, memory, and a suite of “tools”—which, for us, are often our existing services and APIs.

This is the essence of agentic orchestration. It shifts from a predefined, top-down command structure to a more collaborative, goal-driven paradigm. Instead of meticulously charting every step, we define the desired outcome and empower intelligent agents to find the best path to it.

Think of it as moving from a detailed project plan (SOA) to giving a highly skilled project manager (the Orchestrator Agent) a clear objective and a team of specialists (worker agents, each with specific skills/tools).

Key Differences that Matter

From Fixed Sequence to Dynamic Planning:

Traditional SOA executes a predetermined sequence: Step A, then Step B, then Step C. Agentic orchestration takes a different approach — agents dynamically construct their plan based on current context and available resources, asking: “What tools do I have, and which best serve this step?”

From Explicit Error Handling to Self-Correction:

In SOA, elaborate try-catch blocks covered every potential failure. BizTalk veterans will remember wrapping Scope shapes inside Scope shapes, each carrying its own exception handler. With agentic systems, a failing tool triggers reasoning rather than a halt — the agent may retry with a different tool, consult another agent, or revise its plan entirely.

From API Contracts to Intent-Based Communication:

Traditional SOA services communicate via strict, often verbose XML or JSON contracts — schema design and message transformation consumed countless engineering hours. Agentic systems shift to intent-based communication instead. An “Order Fulfillment Agent” can instruct a “Shipping Agent” with a clear goal: “Ship this package to customer X by date Y.” The Shipping Agent then determines which underlying tools, FedEx API, DHL API, best achieve that outcome, abstracting away the complexity of individual service calls.

From Static Connectors to Smart Tools:

Connectors and adapters in SOA are fixed pathways, each requiring explicit configuration per integration point. BizTalk veterans know this well from hours spent configuring adapters for every specific endpoint. In agentic architectures, existing APIs, databases, message queues, and even legacy systems are reframed as tools that agents can discover and wield intelligently. A Logic App connector to SAP is no longer just a connector; it becomes a capable SAP tool that an agent can invoke when the situation calls for it. The Model Context Protocol (MCP) is making this kind of dynamic tool discovery increasingly seamless.

A Concrete Example

Consider an order that fails the inventory check in our traditional BPEL or BizTalk orchestration. In SOA: hard stop, send error notification, await human intervention, and process redesign.

In an agentic system, the orchestrator agent might dynamically query alternate suppliers, adjust delivery timelines based on customer priority, suggest product substitutions, or even negotiate partial fulfillment—all without hardcoded logic for each scenario. The agent reasons about the business goal (fulfill the customer order) and uses available tools to achieve it, adapting to circumstances we never explicitly programmed for.

Azure Logic Apps: The Bridge to the Agentic Future

Azure Logic Apps demonstrates this evolution in practice, and it’s particularly compelling for integration professionals. For those of us coming from the BizTalk world, Logic Apps already felt familiar—the visual designer, the connectors, the enterprise reliability. Now, we’re not throwing away our decades of experience with these patterns. Instead, we’re adding an “intelligence layer” on top.

The Agent Loop within Logic Apps, with its “Think-Act-Reflect” cycle, transforms our familiar integration canvas into a dynamic decision-making engine. We can build multi-agent patterns—agent “handoffs” in which one agent completes a task and passes it to another, or “evaluator-optimizer” setups in which one agent generates a solution and another critiques and refines it.

All this, while leveraging the robust, enterprise-ready connectors we already depend on. Our existing investments in integration infrastructure don’t become obsolete; they become more powerful. The knowledge we gained from debugging BizTalk orchestrations, understanding message flows, and designing for reliability? All of that remains valuable. Microsoft is simply upgrading our toolkit.

Adopting Agentic Orchestration: The Path Forward for Integration Architects

For integration engineers and architects, this is not a threat but an immense opportunity. We are uniquely positioned to lead this charge. We understand the nuances of enterprise systems, the criticality of data integrity, and the challenges of connecting disparate technologies. Those of us who survived the BizTalk years are battle-tested, we know what real-world integration demands.

Agentic orchestration frees us from the burden of explicit, step-by-step programming for every conceivable scenario. It allows us to design systems that are more resilient, more adaptive, and ultimately, more intelligent. It enables us to build solutions that not only execute business processes but also actively contribute to achieving business outcomes.

Start small: Identify one rigid orchestration in your current architecture that would benefit from adaptive decision-making. Perhaps it’s an order-fulfillment process with too many exception handlers, or a customer-onboarding workflow that breaks when regional requirements change. That’s your first candidate for agentic enhancement.

Let’s cast aside the notion of purely deterministic choreography. Let us instead embrace the era of intelligent collaboration, where our meticulously crafted services become the powerful tools in the hands of autonomous, reasoning agents.

The evolution is here. It’s time to orchestrate a smarter future.