AI-powered web application with Entra ID authentication and Foundry Agent Service integration. Deploy to Azure Container Apps with a single command.
# Clone or initialize from GitHub template
azd init -t microsoft-foundry/foundry-agent-webapp
# Deploy everything
azd up # Full deployment: ~10-12 minutesAlternative: Use GitHub's "Use this template" button or clone directly:
git clone https://github.com/microsoft-foundry/foundry-agent-webapp.git
cd foundry-agent-webapp
azd upThe azd up command:
- Discovers AI Foundry resources in your subscription
- Creates Microsoft Entra ID app registration (via Bicep) and Azure infrastructure (ACR, Container Apps)
- Builds and deploys your application
- Opens browser to your deployed app
Local Development: http://localhost:5173 (frontend), http://localhost:8080 (backend)
Production: https://.azurecontainerapps.io
- PowerShell 7+ -
winget install Microsoft.PowerShell - Azure Developer CLI (azd) -
winget install microsoft.azd - Azure CLI -
winget install Microsoft.AzureCLI - Docker Desktop (optional) - https://docs.docker.com/desktop/install/windows-install/
- .NET 9 SDK - https://dot.net
- Node.js 18+ - https://nodejs.org
- PowerShell 7+ -
brew install powershellor download - Azure Developer CLI (azd) -
brew tap azure/azd && brew install azdorcurl -fsSL https://aka.ms/install-azd.sh | bash - Azure CLI -
brew install azure-cliorcurl -L https://aka.ms/InstallAzureCli | bash - Docker Desktop (optional) -
brew install --cask dockeror download - .NET 9 SDK - https://dot.net
- Node.js 18+ -
brew install nodeor https://nodejs.org
Homebrew not installed? Commands work without Homebrew using direct installers. The deployment script (
azd up) checks for Homebrew and provides appropriate installation instructions.
- PowerShell 7+ - https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-linux
- Azure Developer CLI (azd) -
curl -fsSL https://aka.ms/install-azd.sh | bash - Azure CLI - https://learn.microsoft.com/cli/azure/install-azure-cli-linux
- Docker Engine (optional) - https://docs.docker.com/engine/install/
- .NET 9 SDK - https://dot.net
- Node.js 18+ - https://nodejs.org
- Azure Subscription with Contributor role
- Bicep CLI - Installed automatically with
azd, or manually:az bicep install - Microsoft Foundry Resource with a project and at least one v2 agent — create via ai.azure.com or deploy infrastructure with microsoft-foundry/foundry-samples Bicep templates
Note: Docker is optional. If not installed,
azdautomatically uses Azure Container Registry cloud build for deployment.
If your organization uses a custom npm registry, add .npmrc to frontend/ directory:
registry=https://your-registry.example.com/
//your-registry.example.com/:_authToken=${NPM_TOKEN}Note: .npmrc is automatically copied during Docker builds. Don't commit authentication tokens.
If your organization requires a Service Management Reference for Entra ID app registrations:
azd env set ENTRA_SERVICE_MANAGEMENT_REFERENCE "<guid-from-admin>"See deployment/hooks/README.md for more organization-specific configuration options.
The workspace includes optimized VS Code configuration for AI-assisted development:
| Task | Description | Port |
|---|---|---|
Backend: ASP.NET Core API |
dotnet watch run with hot reload |
8080 |
Frontend: React Vite |
npm run dev with HMR (auto-installs deps) |
5173 |
Start Dev (VS Code Terminals) |
Starts both in parallel (default build task) | - |
Install Frontend Dependencies |
npm install --legacy-peer-deps (runs automatically) |
- |
Hot Reload Workflow:
- Backend: Edit C# → Save → .NET auto-recompiles → Check terminal for errors
- Frontend: Edit TypeScript/React → Save → Browser updates instantly (HMR)
- No restarts needed - just edit, save, and test
AI Agent Benefits: Server logs are visible in VS Code terminals, allowing AI agents to:
- See compilation errors and warnings
- Monitor request handling
- Debug issues without screenshots
| Configuration | Description |
|---|---|
.NET: Launch Backend |
Debug ASP.NET Core API with C# Dev Kit |
.NET: Attach to Backend |
Attach to running dotnet watch process |
Chrome: Frontend |
Debug React app in Chrome with source maps |
Edge: Frontend |
Debug React app in Edge |
Full Stack Debug |
Launch backend + Chrome together |
- GitHub Copilot - Code generation uses instruction files (
github.copilot.chat.codeGeneration.useInstructionFiles: true) - Agent Customization - Agent customization skill enabled (
chat.agentCustomizationSkill.enabled: true) - Skills - On-demand loading from
.github/skills/for efficient context - Terminal Scrollback - Limited to 500 lines to prevent overwhelming AI context
- Markdown Linting - Disabled to prevent noise from instruction files
azd up automatically discovers your Foundry resource, project, and agent:
- 1 resource found: Auto-selects and configures RBAC
- Multiple resources found: Prompts you to select which one to use
- RBAC: Automatically grants the Container App's managed identity
Cognitive Services OpenAI Contributor+Azure AI Developerroles
Coming from the AI Foundry portal? If you clicked "View sample app code" in the portal, you can either paste the portal variables into a root .env file or set them via azd env set, then run azd up:
# Option 1: Paste portal variables into a root .env file
# Create a .env file in the repo root with the portal values, then:
azd up
# Option 2: Set via azd environment
azd env set AZURE_EXISTING_AGENT_ID "your-agent:2"
azd env set AZURE_EXISTING_AIPROJECT_ENDPOINT "https://your-resource.services.ai.azure.com/api/projects/your-project"
azd env set AZURE_EXISTING_RESOURCE_ID "/subscriptions/.../accounts/your-resource"
azd upThe preprovision hook detects these portal variables (from either location) and maps them automatically.
Change AI Foundry resource:
# Option 1: Let azd discover and prompt for selection
azd provision # Re-runs discovery, updates RBAC
# Option 2: Manually configure then provision
azd env set AI_FOUNDRY_RESOURCE_GROUP <resource-group>
azd env set AI_FOUNDRY_RESOURCE_NAME <resource-name>
azd provision # Updates RBAC for new resourceList and switch agents (requires prior azd up):
# List all agents in configured project
.\deployment\scripts\list-agents.ps1
# Switch to different agent (in same resource)
azd env set AI_AGENT_ID <agent-name>
# No provision needed - RBAC already grants access to all agents in the resource💡
azd provision(orazd up) automatically regenerates.envfiles and updates RBAC assignments when configuration changes.
# Run the compound task via Command Palette (Ctrl+Shift+P):
# "Tasks: Run Task" → "Start Dev (VS Code Terminals)"
# Or press Ctrl+Shift+B (default build task)
# Servers run in VS Code terminal panel with visible logs
# AI agents can read logs via get_terminal_output# Start local development (spawns separate terminal windows)
.\deployment\scripts\start-local-dev.ps1- React: Hot Module Replacement (HMR) - instant browser updates
- C#: Watch mode - auto-recompiles on save, check terminal for errors
- Test at: http://localhost:5173
# Deploy code changes to Azure
azd deploy # 3-5 minutesFrontend: React 19 + TypeScript + Vite
Backend: ASP.NET Core 9 Minimal APIs
Authentication: Microsoft Entra ID (PKCE flow)
AI Integration: Foundry Agent Service v2 Agents API (Azure.AI.Projects SDK)
Deployment: Single container, Azure Container Apps
Local Dev: Native (no Docker required)
- Office Documents: DOCX, PPTX, and XLSX files are not supported for upload. Use PDF, images, or plain text files instead.
- Beta SDK: This application uses pre-release Azure SDK packages. Check
backend/WebApp.Api/WebApp.Api.csprojfor current versions. - npm Peer Dependencies: React 19 has peer dependency conflicts with some packages. If adding packages that have peer dependencies (like
yjsfor@lexical/yjs), you must add them explicitly topackage.json. Runnpm cilocally to verify before committing.
See .github/copilot-instructions.md for complete command reference and development workflow.
| Command | Purpose | Duration |
|---|---|---|
azd up |
Initial deployment (infra + code) | 10-12 min |
azd deploy |
Deploy code changes only | 3-5 min |
.\deployment\scripts\start-local-dev.ps1 |
Start local development | Instant |
.\deployment\scripts\list-agents.ps1 |
List agents in your project | Instant |
azd provision |
Re-deploy infrastructure / update RBAC | 2-3 min |
azd down --force --purge |
Delete all Azure resources | 2-3 min |
ARCHITECTURE-FLOW.md- State machines, data flow diagrams, and SSE event mappingbackend/README.md- ASP.NET Core API setup and configurationfrontend/README.md- React frontend developmentinfra/README.md- Azure infrastructure overviewdeployment/README.md- Deployment scripts and hooks
This repository uses VS Code's Agent Skills feature for on-demand context loading:
.github/copilot-instructions.md- Architecture overview (always loaded).github/skills/- Domain-specific guidance loaded when relevant:understanding-architecture- State machines, SSE events, data flowdeploying-to-azure- Deployment commands and troubleshootingwriting-csharp-code- C#/ASP.NET Core patternswriting-typescript-code- TypeScript/React patternswriting-bicep-templates- Bicep infrastructure patternsimplementing-chat-streaming- SSE streaming patternstroubleshooting-authentication- MSAL/JWT debuggingresearching-azure-ai-sdk- SDK research workflowtesting-with-playwright- Browser testing workflowsyncing-mcp-servers- MCP server config synchronizationtesting-cli-compatibility- CLI compatibility validationwriting-unit-tests-csharp- C#/MSTest unit test patternswriting-unit-tests-typescript- TypeScript/Vitest unit test patternsvalidating-ui-features- UI feature validation procedurescommitting-code- Commit message format and conventional commit workflowreviewing-documentation- Documentation audit checklists and quality standardstriaging-issues- Issue triage workflow, priority definitions, and report formatplanning-features- Structured plan template for feature implementation
.github/hooks/— Agent hook system (e.g., commit gate) for enforcing workflows
This template deploys the following Azure resources:
- Azure Container Apps - Serverless container hosting (0.5 vCPU, 1GB RAM, scale-to-zero enabled)
- Azure Container Registry - Private container image storage (Basic tier)
- Log Analytics Workspace - Centralized logging (30-day retention)
- Application Insights (Backend) - OpenTelemetry traces, metrics, and distributed tracing (
APPLICATIONINSIGHTS_CONNECTION_STRINGenv var) - Application Insights (Frontend) - Browser telemetry via
@microsoft/applicationinsights-web(separate resource to isolate browser metrics from server metrics) - User-Assigned Managed Identity -
isolationScope: Regional— used for ACR pull, AI Foundry RBAC, and OBO FIC. No admin credentials or secrets.
All resources deploy to the same region (AZURE_LOCATION). The managed identity's regional isolation ensures it can only be assigned to compute resources in the deployment region.
Region tip: For best resilience, deploy to the same region as your AI Foundry resource. The
preprovisionhook warns if regions don't match. To align:azd env set AZURE_LOCATION <your-ai-foundry-region>
By default, azd up configures everything automatically:
| Component | Identity | How |
|---|---|---|
| Frontend → Backend | User's Entra ID token (MSAL.js PKCE) | SPA app registration created by Bicep |
| Backend → Agent Service | Container App's managed identity | User-assigned MI + RBAC (see Azure Resources) |
The managed identity has Cognitive Services OpenAI Contributor + Azure AI Developer roles on the AI Foundry resource. All agent tool calls (MCP, OpenAPI, Logic Apps) use the agent's own identity configured in the Foundry portal — NOT the web app's identity and NOT the user's identity.
Scope requested by AIProjectClient: https://ai.azure.com/.default
OBO replaces the managed identity with the user's own identity for Agent Service API calls. This gives you per-user audit trails and rate limiting but adds enterprise friction.
⚠️ Important: OBO does NOT pass the user's identity to agent tools. Tool authentication (MCP servers, OpenAPI endpoints, Logic Apps) is controlled by the Agent Identity configured in the Foundry portal. OBO only affects who the Agent Service API sees as the caller.
┌──────────┐ JWT (user) ┌──────────────┐ OBO token (user) ┌──────────────┐
│ Frontend │ ────────────►│ Backend API │ ──────────────────►│ Agent Service│
│ (MSAL.js)│ │ (ASP.NET) │ │ (Foundry) │
└──────────┘ └──────────────┘ └──────────────┘
│
│ 1. ManagedIdentityClientAssertion
│ → gets MI token (audience: api://AzureADTokenExchange)
│
│ 2. OnBehalfOfCredential(tenantId, backendClientId,
│ miAssertionCallback, userJWT)
│ → exchanges user JWT for OBO token
│ → scope: https://ai.azure.com/.default
│
│ No secrets! MI token replaces client secret.
References:
- OBO flow protocol
- Federated Identity Credentials (FIC) with managed identities
- ManagedIdentityClientAssertion
- Agent Identity in Foundry
azd env set ENABLE_OBO true
azd upThis creates a backend API app registration with FIC, sets api://{backendClientId} identifier URI, and attempts admin consent. If consent fails, follow the printed instructions.
| Requirement | Who | What | Why |
|---|---|---|---|
| FIC creation | Deployer | Application Administrator role in Entra ID |
To create the federated identity credential on the backend app |
| Admin consent | Entra admin | Grant Azure Machine Learning Services / user_impersonation delegated permission |
The OBO token exchange requests https://ai.azure.com/.default which resolves to Azure Machine Learning Services (appId: 18a66f5f-dbdf-4c17-9dd7-1634712a9cbe). Without admin consent, token acquisition fails with AADSTS65001. |
| User RBAC | Each user | Azure AI User role on the Foundry resource |
OBO tokens carry the user's identity; the user needs data-plane permissions (docs) |
| Known client | Deployer (optional) | Add SPA client ID to backend app's knownClientApplications |
Enables combined consent prompt so users consent to both SPA + backend in one step (docs) |
⚠️ Common mistake: consenting to the wrong service. The Azure portal shows "Microsoft Cognitive Services" (https://cognitiveservices.azure.com, appId:7d312290-...) which looks like the right choice — butAIProjectClientactually requests tokens forhttps://ai.azure.com/.defaultwhich maps to Azure Machine Learning Services (appId:18a66f5f-dbdf-4c17-9dd7-1634712a9cbe). These are different first-party service principals. You must consent to the correct one:# Add the CORRECT permission (Azure Machine Learning Services, not Cognitive Services) az ad app permission add \ --id <your-backend-app-id> \ --api 18a66f5f-dbdf-4c17-9dd7-1634712a9cbe \ --api-permissions 1a7925b5-f871-417a-9b8b-303f9f29fa10=Scope # Then grant admin consent az ad app permission admin-consent --id <your-backend-app-id>Or in the portal: API permissions → Add → "APIs my organization uses" → search "Azure Machine Learning" →
user_impersonation(Delegated).
| Gotcha | Detail |
|---|---|
| Wrong consent target | Portal shows "Microsoft Cognitive Services" (cognitiveservices.azure.com, appId 7d312290-...) — this is NOT correct. AIProjectClient uses ai.azure.com/.default → Azure Machine Learning Services (appId 18a66f5f-...). Consenting to wrong one gives green checkmark but runtime AADSTS65001. |
| Tool identity is separate | OBO only affects the Agent Service API caller. Agent tools (MCP, OpenAPI, Logic Apps) use the agent's identity from Foundry portal. Configure Agent Identity separately for per-user tool access. |
| Conversations not user-scoped in MI mode | MI uses a shared identity — all users see all conversations. OBO provides per-user isolation. |
| Local dev uses CLI credentials | OBO requires a managed identity for FIC. Locally, the app uses az login credentials regardless of ENTRA_BACKEND_CLIENT_ID. |
├── backend/WebApp.Api/ # ASP.NET Core API + serves frontend
├── frontend/ # React + TypeScript + Vite
├── infra/ # Bicep infrastructure templates
├── deployment/
│ ├── hooks/ # azd lifecycle automation
│ ├── scripts/ # User commands
│ └── docker/ # Multi-stage Dockerfile
└── .github/
├── copilot-instructions.md # Architecture overview (always loaded)
├── hooks/ # Agent hooks (commit gate, custom policies)
└── skills/ # 18 on-demand AI assistant skills