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.agentsazdpostdeployhook printsAZURE_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:
AgentProtocolis nowAgentEndpointProtocol, the image moved intoContainerConfiguration, andcontainer_protocol_versionsis nowprotocol_versions. Seedocs/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
| Changed | Do |
|---|---|
runtime/src/*.ts | cd 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/).