Replace a multi-week FinOps assessment with a single conversation.
Quantified savings, a FinOps maturity score, a CFO-ready deck, and ready-to-run remediation scripts — in minutes.
The agent fixes the issue for you, or hands you the az cli script. Read & write on your behalf, never delete. Your tenant. Your tokens. Your control.
No Azure tenant? Two ways to demo:
- Public pricing questions — ask about Azure VM SKUs, regions, reservations, savings plans. Agent uses the public Retail Prices API (no auth).
- Drop a sample file — drag any CSV/JSON/XLSX/PDF from
demo-data/into the chat. The agent inspects the schema, runs aggregates, and answers without ever loading the raw payload into the LLM. Includes realistic cost exports, Advisor JSON, audit logs, and FinOps notes.
Save any prompt with a cadence (every 15 min / hourly / daily / weekly) and the agent keeps running it in the background — even with your browser closed. Every run lands in the job's own conversation, so you come back to a full history of answers, tables, and charts.
- GPU hunt & reserve — the flagship template: checks quota + capacity for H100/H200 sizes across all your subscriptions and regions every 15 minutes, and the moment something is deployable it secures it by creating an on-demand Capacity Reservation (idempotent, cost-warned, cleanup via a reviewable script).
- One-click templates — daily cost digest, hourly anomaly watch, budget guard, weekly idle-resource sweep, Advisor watch — or write your own prompt. Choose “run immediately, then repeat” or wait one interval.
- Your permissions, nothing more — background runs re-mint tokens from your delegated Entra sign-in. No app permissions, no admin consent: a job can never do more than your own login allows, and it pauses itself with a “reconnect Azure” status if your refresh token expires.
- Guardrails — max 3 active jobs per user; sub-daily jobs expire after 7 days, daily/weekly after 90; auto-pause after 5 consecutive failures. Deletes are always blocked — removals ship as scripts you review and run yourself.
Vue 3 SPA → .NET 10 minimal API → GitHub Copilot SDK → Azure APIs (Cost Management, ARM, Resource Graph, Microsoft Graph, Log Analytics) using your delegated Entra tokens — read + write on your behalf, never delete. A background scheduler runs your saved jobs on the same delegated tokens. Hosted on Azure App Service or Container Apps. OpenTelemetry to your Application Insights.
flowchart LR
U[User] --> FE[Vue 3 SPA]
FE --> API[.NET 10 API]
API --> SDK[Copilot SDK]
API --> SCHED[Job Scheduler] --> SDK
SDK --> AOAI[Azure OpenAI]
SDK --> TOOLS[Azure Tools]
TOOLS --> ARM[ARM / Cost Mgmt]
TOOLS --> GRAPH[Microsoft Graph]
TOOLS --> LA[Log Analytics]
U -. OAuth .-> ENTRA[Entra ID]
ENTRA --> API
One command provisions and deploys everything into your own subscription:
az login --tenant <id> # sign into the target tenant
az account set --subscription <id>
azd auth login # sign into azd (same tenant)
azd up # provision + build image + deployWhat azd up does:
- Creates a resource group, Log Analytics + Application Insights, Azure Container Registry (Basic, admin disabled), Azure AI Foundry (
AIServices) account +gpt-5.6-solmodel deployment, Linux App Service Plan, and the containerised Web App with system-assigned managed identity. - Grants the Web App's MI
AcrPullon the registry andCognitive Services OpenAI Useron the Foundry account (BYOK via managed identity — no API keys). - Creates a multi-tenant Microsoft Entra ID app registration with the 5 incremental-consent permission tiers (ARM, Microsoft Graph, Log Analytics, Azure Storage) — see setup-entra-app.ps1.
- Builds the Docker image server-side via
az acr build(no local Docker daemon required) and restarts the Web App.
Override defaults via azd env set before running azd up:
| Variable | Default | Notes |
|---|---|---|
AZURE_LOCATION |
(prompted) | Region for the resource group and most resources. Restricted to the 29 regions where the full stack (incl. gpt-5.6-sol Global Standard) is available |
AZURE_OPENAI_LOCATION |
swedencentral |
AOAI region. Restricted to the 29 regions offering gpt-5.6-sol Global Standard |
APP_SERVICE_PLAN_SKU |
B1 |
Use P0V3 for production-grade (~$77/mo vs ~$13/mo) |
AZURE_OPENAI_MODEL_NAME / _VERSION |
gpt-5.6-sol / 2026-07-09 |
Reasoning model required — the agent sets ReasoningEffort=xhigh in code. gpt-4o / gpt-4 will not work. |
AZURE_OPENAI_DEPLOYMENT_NAME |
gpt-5.6-sol |
Surfaced as AzureOpenAI__DeploymentName to the app |
EXISTING_AOAI_RESOURCE_ID |
(empty) | Full resource ID to reuse an existing AOAI account instead of creating one |
AZURE_ENTRA_APP_ID / AZURE_ENTRA_CLIENT_SECRET |
(empty) | Reuse an existing Entra app instead of creating one |
Tear down with azd down --purge (purge is required because Cognitive Services soft-deletes by default). A postdown hook then deletes the Entra app registration that azd up created — only when azd created it; a bring-your-own AZURE_ENTRA_APP_ID is left untouched — so no orphaned app or secret is left behind.
azd up needs rights on the Azure subscription and the Microsoft Entra tenant. The easiest combination is Owner on the subscription plus the ability to create app registrations in the tenant. For the least-privilege breakdown (Contributor + User Access Administrator, Application Administrator role, resource providers, quota), see docs/azd-up-permissions.md.
- .NET 10 SDK
- Node.js 22+
- Azure CLI — authenticated via
az login - An Azure OpenAI resource with a deployed model (e.g.,
gpt-5.6-sol)
Secrets are stored via .NET User Secrets — outside the repo, in your OS user profile. They cannot be accidentally committed.
cd src/Dashboard
# Required — the app throws InvalidOperationException on startup if this is missing:
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://YOUR-RESOURCE.openai.azure.com/"
# Optional — set the model deployment name (defaults to gpt-5.6-sol if omitted):
dotnet user-secrets set "AzureOpenAI:DeploymentName" "gpt-5.6-sol"
# Optional — pin the BYOK token tenant. Set this when your az CLI's DEFAULT
# account lives in a different tenant than the Azure OpenAI resource
# (symptom: every AI turn fails with "Token tenant does not match resource tenant"):
dotnet user-secrets set "AzureOpenAI:TenantId" "TENANT-GUID-OF-YOUR-AOAI-RESOURCE"
# Optional — enables the "Connect Azure" OAuth flow for Cost Management, Advisor, etc.:
dotnet user-secrets set "Microsoft:ClientId" "YOUR-ENTRA-APP-CLIENT-ID"
dotnet user-secrets set "Microsoft:ClientSecret" "YOUR-ENTRA-APP-CLIENT-SECRET"
dotnet user-secrets set "Microsoft:TenantId" "common"
# Optional — enables Application Insights telemetry:
dotnet user-secrets set "ApplicationInsights:ConnectionString" "InstrumentationKey=...;IngestionEndpoint=https://...;"
AzureOpenAI:Endpointis the only fail-fast key — the app will not start without it.
All other keys degrade gracefully (Azure connect is disabled, telemetry is skipped, etc.).
Run dotnet user-secrets list (from src/Dashboard) to verify what's stored.
cd src/Dashboard/frontend
npm install
npm run build # outputs to src/Dashboard/wwwroot/cd src/Dashboard
$env:ASPNETCORE_ENVIRONMENT = "Development"
dotnet run --project Dashboard.csproj --urls "http://localhost:5000"Open http://localhost:5000.
If
AzureOpenAI:Endpointis not set, the app intentionally crashes with anInvalidOperationExceptiontelling you exactly whichdotnet user-secrets setcommand to run. This is the fail-fast safety net.
User Secrets are stored outside the repo — they can never be accidentally committed:
- Windows:
%APPDATA%\Microsoft\UserSecrets\1190b8a4-6595-436b-9479-b9951bd00f16\secrets.json - macOS / Linux:
~/.microsoft/usersecrets/1190b8a4-6595-436b-9479-b9951bd00f16/secrets.json
In production (Azure App Service), the same configuration keys are sourced from environment variables / App Service application settings via the standard IConfiguration environment-variable provider — e.g., AzureOpenAI__Endpoint, Microsoft__ClientId. Environment variables always take precedence over User Secrets; User Secrets only loads when ASPNETCORE_ENVIRONMENT=Development.
The deploy workflow (deploy.yml) injects secrets into App Service as application settings at deploy time. Add them under Settings → Secrets and variables → Actions → New repository secret in the GitHub UI.
| GitHub Secret name | App Service setting | Purpose |
|---|---|---|
AZURE_OPENAI_ENDPOINT |
AzureOpenAI__Endpoint |
Azure OpenAI resource URL — required |
AZURE_CLIENT_ID |
(OIDC login) | Managed identity / service principal client ID |
AZURE_TENANT_ID |
(OIDC login) | Azure AD tenant ID |
AZURE_SUBSCRIPTION_ID |
(OIDC login) | Target subscription ID |
Convention: App Service application settings use
__(double underscore) to map to .NETIConfigurationhierarchy —AzureOpenAI__Endpointmaps toAzureOpenAI:Endpoint. Follow the same pattern for any future secrets (e.g.,Microsoft__ClientId→Microsoft:ClientId).To add a new secret: (1) create it in the GitHub UI, (2) add
KEY="${{ secrets.SECRET_NAME }}"to the--settingslist in theConfigure App Service settingsstep ofdeploy.yml.
Tip for local dev: Besides
dotnet user-secrets, you can also exportAzureOpenAI__Endpointas a shell environment variable ($env:AzureOpenAI__Endpoint = "..."in PowerShell) — environment variables are picked up byIConfigurationautomatically and take precedence over User Secrets.
MIT · See CONTRIBUTING.md · Code of Conduct

