Azure OpenAI and ASP.NET — a story based on facts
On 16 December I gave a lecture at Serverless Wrocław called Azure OpenAI + ASP.NET — a story based on facts. Polish talk, company-branded slides so nothing to host here — this post is the English write-up of the same material, including what happened to our Azure OpenAI resource in the weeks after the meetup.
No sample repository to clone. The lecture walked through a real internal experiment: matching people to project requirements in a software house, using Azure OpenAI from an ASP.NET app, with Azure AI Search in the middle. At the end I also compared that path with Copilot Studio — mostly to show that Microsoft gives you more than one way to solve similar problems.
What we were trying to do
In a software house, staffing questions come up all the time. A project needs someone who knows technologies X, Y, and Z. Someone has to figure out who is available, who has done similar work, and whose CV actually matches. That information sits in CVs, availability trackers, internal profiles, and — if you are lucky — a system someone already maintains.
The experiment took a project requirements description (Polish, English, whatever the client used) and returned a recommendation of which employee might fit best. A human still made the final call — AI suggested, the manager decided. We pulled from skills, experience, CVs, availability, and internal profiles.
One functional requirement mattered a lot: isolation. A manager should not see another manager’s team data. On the non-functional side, the UI had to stay simple; too many clicks and nobody would open it. And I had a personal goal: I wanted to learn Azure OpenAI hands-on, so the design had to use it.
We tested early prompts on ChatGPT with synthetic or otherwise public-safe data. The slide deck reminder still holds: when you try things on public AI services, only use data you would be comfortable putting on the internet.
Dataverse or our own app
Before writing much code we looked at Microsoft Dataverse — data and integrations in one place, straightforward path toward Azure OpenAI — and at a custom ASP.NET application where we define the context ourselves: the model plus only the sources we attach.
Dataverse means other systems connect to it, you watch scope so the model does not see more than intended, and legal/compliance review is part of the conversation from the start. Custom code means we own application logic, integrations, and the security model, but the boundary is easier to explain to anyone asking “what exactly does the model see?”
We chose the ASP.NET route as a small side project, not a company-wide platform. Bounded scope, clear data perimeter, room to stop if it did not work out.
How it was wired
Azure OpenAI for the model — provisioned in Azure, prompts and deployments tuned in OpenAI Studio. Azure AI Search for retrieval — indexes, indexers, and skillsets so we sent relevant chunks to the prompt instead of whole CVs every time. ASP.NET backend with the Azure.AI.OpenAI NuGet package. A simple web front end for managers.
Infrastructure and security followed the usual Azure pattern: RBAC, no keys in the browser, orchestration on the server. The browser calls your API; your API calls OpenAI and Search.
Once you leave the Studio playground, message types matter — System for behaviour, User for the manager’s input, Assistant for model replies, Tool for structured calls to external APIs. Most of our flow was system + user + assistant. We proved prompts in OpenAI Studio first, then moved the same shape into C# — still my default workflow.
Copilot Studio on the same slide
Near the end I put custom code next to Copilot Studio. Custom code takes more engineering skill, but you choose data sources, how they are processed, and how access is isolated. Costs show up in Azure pricing tools — at least for the Azure parts you provision deliberately.
Copilot Studio is faster to stand up for many scenarios: Entra-backed access, Teams deployment, a lot of configuration without opening an IDE. Less flexibility in how data is combined, connectors that are often read-focused, billing that can span Power Platform as well as Azure.
We went with ASP.NET because the experiment was about learning and control in a narrow scope — our Search indexes, our isolation rules, our prompt lifecycle. Copilot Studio is a good fit for other teams; in the talk it was the honest “you could also do it this way” slide, not the design we committed to.
The billing plot twist — why I am writing this now
One thing I did not have for the lecture stage: what happened to our Azure OpenAI resource in the weeks after the meetup.
For a while after the demo worked, no invoice showed up. I kept checking cost management and wondering whether the usage was just too small to notice. The app ran; managers could try it; everything looked fine.
Then it stopped. Not a graceful degradation — the whole thing simply stopped working until we started a proper subscription. It turned out we had been on a trial that was not called out clearly anywhere I looked in the portal at the time. When it expired, the resource was effectively bricked until billing was sorted out.
So the slide about watching Azure costs had a mirror image I had not earned yet: sometimes the problem is not “the bill is too high” but “there was no bill because it was trial, and nobody told us loudly enough.” If you build something people start to rely on — even an internal experiment — check entitlement, trial end dates, and what happens on expiry before you call it done.
We did get it running again after moving to a paid subscription. The lesson landed harder than any quota error message — and it is the main reason this post exists a few weeks after the talk instead of the same evening.
The parts I would still defend are pretty simple: use AI for recommendation not final decisions when people are involved; isolate data when the domain requires it; Azure AI Search + OpenAI beats pasting entire documents into every prompt; prototype in OpenAI Studio then integrate with Azure.AI.OpenAI with secrets off the client; and whichever path you pick — custom code or Copilot — confirm how access is billed before the pilot becomes someone’s daily tool.