This project deploys the FastAPI container to Azure Container Apps using Bicep. The API exposes /health and /forecast; /forecast calls the URL in BACKEND_API_BASE.
Application Insights is connected to the existing Log Analytics workspace. Container Apps platform logs, application logs, request traces, and requests dependency spans are available through Azure Monitor.
Application endpoints require an API key in the x-api-key header. The /health endpoint remains unauthenticated for platform probes.
flowchart LR
subgraph Local[Developer machine]
Copilot[VS Code Copilot]
MCP[mcp_server.py<br/>MCP stdio server]
Config[.vscode/mcp.json<br/>AGENT_API_BASE_URL<br/>AGENT_API_KEY]
Copilot -->|MCP tools| MCP
Config -. configures .-> MCP
end
subgraph Azure[Azure]
ACR[Azure Container Registry<br/>python-agent image]
subgraph ACA[Azure Container Apps]
App[python-agent-app<br/>FastAPI + Uvicorn :8000]
Env[Managed environment<br/>1-3 replicas]
App -. runs in .-> Env
end
Insights[Application Insights]
Logs[Log Analytics workspace]
ACR -->|image pull| App
App -->|telemetry and stdout logs| Insights
Env -->|platform and app logs| Logs
Insights -->|workspace-backed| Logs
end
Deploy[deploy.ps1] -->|docker buildx build --push| ACR
Deploy -->|Bicep deployment| ACA
MCP -->|HTTPS + x-api-key| App
Client[API clients / probes] -->|HTTPS| App
Forecast[Open-Meteo or configured backend<br/>BACKEND_API_BASE] <-->|GET /forecast| App
sequenceDiagram
participant C as Client or MCP tool
participant A as FastAPI app
participant B as Forecast backend
participant O as Azure Monitor
C->>A: GET /health or /forecast<br/>optional x-request-id
A->>A: Skip auth for /health<br/>otherwise compare x-api-key
alt Protected request has a valid key
A->>B: GET /forecast\nlatitude, longitude, current
B-->>A: Forecast JSON
A-->>C: 200 JSON + x-request-id
else Missing or invalid key
A-->>C: 401 Invalid or missing API key
end
A-)O: Structured request logs, traces, dependency spans
| Component | Responsibility | Configuration boundary |
|---|---|---|
main.py |
FastAPI routes, API-key middleware, request IDs, backend call, telemetry | API_KEY, BACKEND_API_BASE, REQUEST_TIMEOUT_SECONDS |
mcp_server.py |
Exposes health_check and get_forecast as MCP tools over stdio |
AGENT_API_BASE_URL, AGENT_API_KEY |
Dockerfile |
Builds the production API image and starts Uvicorn on port 8000 | requirements.txt |
deploy.ps1 |
Builds and pushes the image, then deploys the Bicep template | Azure resource names, image tag, API key |
infra/main.bicep |
Defines Container Apps, registry access, secrets, scaling, and monitoring | Bicep parameters |
- Azure CLI logged in with access to the target subscription
- Docker Desktop running with
buildxsupport - Existing Azure resources:
- Resource group:
python-agent-rg-eastus - Azure Container Registry:
pythonagentacr - Log Analytics workspace:
workspace-pythonagent-eastus - Container Apps environment:
python-agent-env
- Resource group:
The ACR must have admin credentials enabled because the Bicep template uses its registry credentials for image pulls.
From this directory, run:
.\deploy.ps1The script securely prompts for the API key when -ApiKey is omitted. Do not commit the key or place it in infra/main.parameters.json.
The script builds and pushes a linux/amd64 image, then applies infra/main.bicep to the East US resource group.
To use a different backend API or image tag:
.\deploy.ps1 -BackendApiBase 'https://example.internal/api' -ImageTag 'v2'Call the protected API with:
curl.exe -H "x-api-key: YOUR_API_KEY" "https://python-agent-app.calmcliff-2f307d44.eastus.azurecontainerapps.io/forecast?lat=52.52&lon=13.41"az deployment group what-if `
--resource-group python-agent-rg-eastus `
--template-file infra/main.bicep `
--parameters @infra/main.parameters.json apiKey='preview-only'The deployment outputs the public Container App URL as containerAppUrl.
The workflow at .github/workflows/deploy-container-app.yml builds and pushes a linux/amd64 image tagged with the commit SHA, then runs az containerapp update.
Configure these GitHub Actions secrets:
AZURE_CLIENT_ID: Entra application or user-assigned managed identity client IDAZURE_TENANT_ID: Entra tenant IDAZURE_SUBSCRIPTION_ID: Azure subscription ID
Configure a federated GitHub credential for the Azure identity with issuer https://token.actions.githubusercontent.com, subject repo:OWNER/REPOSITORY:ref:refs/heads/main, and audience api://AzureADTokenExchange. Grant the identity AcrPush on pythonagentacr and Contributor on python-agent-app or the python-agent-rg-eastus resource group.
After pushing to main, the workflow updates the app to the image tagged with that commit SHA. It does not change the existing API-key secret; authentication remains managed by the Container App configuration.
The API writes structured request logs to stdout, including status, duration, and request ID. It also records backend dependency requests and propagates an x-request-id response header. Configure verbosity or backend timeout when needed:
.\deploy.ps1Use Log Analytics with the Container App's environment logs, and use Application Insights transaction search and the Application Map for request and dependency telemetry.
The workspace file .vscode/mcp.json configures Copilot to launch mcp_server.py over stdio. The bridge calls the deployed FastAPI URL and prompts for the protected API key without storing it in the repository. Start or refresh the fastapi-agent MCP server from VS Code's MCP controls, then approve the prompt for the API key.