Deployment guide

July 14, 2026 · View on GitHub

End-to-end deployment of the three components, validated on Azure (subscription ai-team, resource group rg-gen-ui-foundry, region eastus2).

Prerequisites

  • Azure subscription with access to Microsoft Foundry and quota for a B1 App Service plan and a Model Router deployment (version 2025-11-18). Model Router is available in East US 2 and Sweden Central.
  • Azure Developer CLI (azd) and Azure CLI.
  • .NET 10 SDK, Node.js 20+, and Python 3.10+ with azure-ai-projects + azure-identity (pip install azure-ai-projects azure-identity).

Step 1 — Provision infrastructure

azd auth login
azd up

This provisions: user-assigned managed identity, Log Analytics + App Insights, ACR, the Foundry account + project (with allowProjectManagement) and a Model Router model deployment, the B1 App Service (runtime), and the Static Web App (frontend). It then deploys the runtime and frontend.

Grab the outputs any time with azd env get-values (ACR name, Foundry account, project endpoint, runtime app name, SWA hostname, etc.).

The azure.ai.agents azd postdeploy hook prints AZURE_AI_PROJECT_ENDPOINT is not set — this is cosmetic; deployment succeeds. Node App Service cold start takes ~100 seconds.

Step 2 — Build the agent image

$acr = (azd env get-values | ConvertFrom-StringData).AZURE_CONTAINER_REGISTRY_NAME.Trim('"')
$tag = "maf-agui-$(Get-Date -Format yyyyMMddHHmmss)"
az acr build --registry $acr --image "analytics-agent:$tag" --image "analytics-agent:latest" ./agent

Step 3 — Register the Hosted Agent

Edit scripts/register_agent.py — set IMAGE to the tag you just built and confirm PROJECT_ENDPOINT, AZURE_OPENAI_ENDPOINT, and MODEL_DEPLOYMENT_NAME — then run it:

python scripts/register_agent.py

It calls AIProjectClient(...).agents.create_version(...) with a HostedAgentDefinition (INVOCATIONS protocol via protocol_versions, image via container_configuration=ContainerConfiguration(image=...), cpu="1", memory="2Gi", environment_variables for the model). Poll until the status is ACTIVE:

from azure.ai.projects.models import AgentVersionStatus
project.agents.get_version("analytics-agent", agent_version="N").status == AgentVersionStatus.ACTIVE

azure-ai-projects 2.3.0: AgentProtocol is now AgentEndpointProtocol, the image moved into ContainerConfiguration, and container_protocol_versions is now protocol_versions. See docs/hosted-agent-sdk-2.3.0-api-changes.md.

Step 4 — Grant RBAC to the agent identity

The Foundry account has disableLocalAuth: true, so the agent's Entra instance identity (principal in the create_version response under instance_identity.principal_id) needs data-plane roles:

$sub = (azd env get-values | ConvertFrom-StringData).AZURE_SUBSCRIPTION_ID.Trim('"')
$rg  = "rg-gen-ui-foundry"
$acct= (azd env get-values | ConvertFrom-StringData).FOUNDRY_ACCOUNT_NAME.Trim('"')
$scope = "/subscriptions/$sub/resourceGroups/$rg/providers/Microsoft.CognitiveServices/accounts/$acct"
az role assignment create --assignee <instance-principal-id> --role "Cognitive Services OpenAI User" --scope $scope
az role assignment create --assignee <instance-principal-id> --role "Cognitive Services User"        --scope $scope

Step 5 — Wire the runtime to the invocations endpoint

The endpoint URL must include ?api-version=v1. Older api-versions return 400.

$rg  = "rg-gen-ui-foundry"
$app = (azd env get-values | ConvertFrom-StringData).RUNTIME_APP_NAME.Trim('"')
$inv = "https://<account>.services.ai.azure.com/api/projects/<project>/agents/analytics-agent/endpoint/protocols/invocations?api-version=v1"
az webapp config appsettings set -g $rg -n $app --settings FOUNDRY_AGENT_ENDPOINT="$inv"
az webapp restart -g $rg -n $app

Step 6 — Verify end-to-end

Test only through the SWA hostname — the runtime App Service returns 401 to direct callers by design (locked linked backend). Allow ~100s after a restart.

$swa = "https://<swa-hostname>"

# runtime lists the agent
'{"method":"info"}' | Out-File $env:TEMP\i.json -Encoding ascii -NoNewline
curl.exe -sS -X POST $swa/api/copilotkit -H "Content-Type: application/json" --data-binary "@$env:TEMP\i.json"

# full run → AG-UI SSE
@'
{"method":"agent/run","params":{"agentId":"analytics-agent"},
 "body":{"threadId":"t1","runId":"r1",
   "messages":[{"id":"m1","role":"user","content":"Add a todo 'Ship blog' then list all todos."}],
   "tools":[],"context":[],"state":{},"forwardedProps":{}}}
'@ | Out-File $env:TEMP\run.json -Encoding ascii -NoNewline
curl.exe -sS -N -X POST $swa/api/copilotkit -H "Content-Type: application/json" --data-binary "@$env:TEMP\run.json"

Then open https://<swa-hostname> and try: "Show a pie chart of sales by region", "Add three tasks…", "Schedule a 30-minute meeting".

Redeploying after code changes

ChangedDo
runtime/src/*.tscd runtime; npm run build; azd deploy runtime
frontend/**azd deploy web
agent/**rebuild image (Step 2) → register new version (Step 3) → the endpoint URL is unchanged
Infra (infra/**)azd provision

Troubleshooting

See the table in best-practices.md and the foundry-agent-deploy skill (.copilot/skills/foundry-agent-deploy/).