Building a Grounded AI Agent on Microsoft Foundry

A Real Build Log From the Microsoft Agent-a-thon: SharePoint Gaps Closed, a Wide Microsoft Stack Explored, and Verifiable DevRel Proof to Show for It

Agent-A-Thon presented by Microsoft Foundry and Founderz
BJ
Bob James
•
11 min read
0 votes
0 comments

We set out to build a grounded, SharePoint-connected AI agent on Microsoft Foundry for the Microsoft Agent-a-thon. A wide tour of the Microsoft stack.

Most Agent-a-thon write-ups show you the finished agent. This one shows the actual build, including the parts that went wrong.

We didn't start with Microsoft Foundry. We started with a simpler question: can any AI agent already in our Microsoft 365 tenant browse our SharePoint site?

The answer to that question is what set the whole project in motion, and it's worth telling honestly from the start, because the dead ends taught us as much as the working agent did.


What You'll Learn 🚀

  1. The capability gap: The systematic way we confirmed a SharePoint-grounded agent
  2. Why we tried Copilot Studio first: And the three-surface billing chain
  3. How Foundry IQ grounding works: Knowledge bases, agentic retrieval
  4. The obstacles: A regional quota wall and the silent SharePoint path quirk
  5. The first live test: A correct, well-cited answer, and one gap
  6. What we built to get here: Two small PowerShell toolkits, `foundrl` and `graphctrl`
  7. Why this article is DevRel proof: How it helps close our career-positioning gap

By the end, you'll have a realistic picture of what building a production-grade agent on Foundry actually involves, not just the demo version.



The Gap: No Agent Could Actually See Our SharePoint Site

Before writing a single line of config, we tested what already existed, rather than assuming the answer. Could Microsoft 365 Copilot chat, or any agent already available in our tenant, browse and enumerate our SharePoint site, rather than only reading files explicitly shared into a chat?

We ran it as a proper investigation, not a guess:

  • Copilot chat could read a file once it was uploaded or linked directly, but couldn't browse a site or enumerate a folder from a link alone.
  • The Microsoft 365 Admin agent proved it had live, Graph-connected tenant access (it could return our tenant ID and list all 284 agents in the org), but when asked to list SharePoint sites or files, it fell back to PnP PowerShell guidance instead of actually querying.
  • The SharePoint admin center had no "Agent insights" report enabled for our tenant.
  • Our site's Site Assets library had zero .agent files.

Four independent checks, all pointing the same way. This wasn't a permissions problem and the Admin agent's own live tenant access proved that.

The genuine gap: no SharePoint-grounded agent existed in the tenant yet. One needed to be built.

The fastest place to start building one was Copilot Studio 🙌

First Attempt: Copilot Studio 🤖

Copilot Studio is low-code, and it has a Knowledge tab built for exactly this. We created an agent scoped tightly to one SharePoint folder, with explicit instructions to answer only from that folder's content and never fabricate an answer when nothing relevant was found.

Testing it immediately failed with Error code: EnforcementUsageCredits. Getting past that meant configuring capacity across three separate admin surfaces, in order:

  1. Azure - confirm a Pay-As-You-Go subscription exists under a billing profile.
  2. Power Platform admin center - create a billing policy linking that subscription and a resource group to Power Platform. This is the step that actually grants capacity.
  3. Attach the policy to the specific environment the agent lives in.

Copilot Studio's own Settings → Billing screen shows nothing useful until that chain is wired up end to end, and none of it is documented anywhere close to the agent-creation flow itself - it's a Power Platform billing concept bleeding into a chat-builder UI.

Once that was sorted, the Copilot Studio agent worked proving the underlying idea was sound. But proving the idea wasn't the actual assignment. The Agent-a-thon is built specifically around Microsoft Foundry, a separate, more developer-facing platform with it's own agent runtime and it's own retrieval mechanics.

The same grounding problem had to be solved a second time, the Foundry way, from scratch.

Building the Thing: A Foundry Agent Grounded in SharePoint

Foundry doesn't share Copilot Studio's Knowledge tab, so grounding on the same SharePoint content meant building an entirely separate agent, retrieval pipeline and all, from scratch.

Creating it meant choosing Build an agent (a declarative prompt agent) over Code an agent (a hosted agent via the Microsoft Agent Framework), and naming it as an API identifier, not a display label - bob-james-career-agent, kebab-case from the start.

The YAML view confirmed what actually got created: kind: prompt, model gpt-5-mini, and a default web_search tool nobody asked for.

Three configuration decisions followed, each one the default the portal ships with turning out to be the wrong choice for this agent:

  • web_search came straight back out. It works against the grounding instruction, and it sends data outside the Azure compliance boundary.
  • Memory (Preview) was skipped. Persistent cross-session recall isn't needed for a stateless grounded-answer use case.
  • Guardrail (Preview) stayed on it's default, Microsoft.DefaultV2 - which doesn't cover indirect prompt injection. That's a gap worth naming: role-matching means job descriptions from outside sources straight into the conversation.

Grounding itself came down to two further decisions where the obvious default was also wrong:

  • Foundry's quickstart defaults to metered Serverless pricing for Foundry IQ, not the flat-free tier assumed going in. Fixed by provisioning genuine Free tier Azure AI Search manually, then confirming empirically that it still supports agentic retrieval.
  • SharePoint (Remote) was chosen over Indexed to stay always-current without a separate Entra app registration. That meant confirming the library URL segment was Shared Documents with a space, via Microsoft Graph directly - a guess would have broken the filter silently.

💡 Tip: The Foundry IQ Free tier's support for agentic retrieval wasnt found to be documented anywhere in the Foundry UI however was confirmed by testing the connection live.

The Instructions field was written as a prompt file, not typed into the portal: ground every answer only in the indexed role map, and keep evidence and gaps clearly separate.

The knowledge base got a name too - bob-james-role-resume-map - set to Answer synthesis mode with Minimal retrieval reasoning effort.

Voice mode got it's own round of live testing once the text agent worked, and it surfaced fixable problems: symbols read aloud literally ("CI slash CD"), a clarifying question answered instead of ending the turn, and a proactiveEngagement setting that kept the agent re-prompting after being told to stop.

All three got fixed and confirmed on live re-runs 🎉

With the agent's shape, tools, guardrails, knowledge base, and voice behaviour all settled, one thing remained true: none of this ran smoothly against the Azure infrastructure underneath it.

Real Obstacles We Hit Along the Way

Three ordinary problems slowed things down before any of the RAG work above could even be tested - the friction of a brand-new subscription meeting production infrastructure, each one costing time before a workaround was found.

Our first-choice model, gpt-4.1-nano, wouldn't deploy at all - every real-time deployment type blocked in the target region. gpt-5-mini worked around it, already auto-deployed with it's own working quota, so we rebuilt the resource in a different region to match the plan. The other two were Windows-specific install snags with quick fixes: Controlled Folder Access blocking a module install (a temporary toggle-off), and PowerShell 5.1's outdated package manager, sidestepped by installing PowerShell 7 alongside it.

With the model finally deployed and the tooling working, the agent was ready for the moment the whole project had been building toward: a question, asked for the first time, in the Playground.

The Live Test: Finding the Gap Was the Win

That question, the one the whole build had been aiming toward, was: "Which resume should I use for a technical operations role?"

The response, in about 44 seconds, correctly identified the right resume variant, cited strategic-priority language from the source, and surfaced evidence gaps verbatim-accurate against it.

The tool trace confirmed the knowledge base was queried, with citations linking back to the SharePoint URL - the proof of concept working end to end.

We didn't stop at "it looked right." Checking every claim against the source directly, line by line, is what paid off: one claim didn't hold up. The response stated "100k+ URL migrations"; a figure that appears nowhere in the source, which only says "zero-downtime migrations," no quantity given at all.

⚠️ Gotcha: A grounded agent citing it's source correctly is not the same as every claim in it's answer being sourced. Check the numbers specifically, not just whether a citation exists.

Everything else was sourced from the document. Finding that one gap on the very first live test is the result worth having - a concrete, fixable target instead of a vague worry. A grounded agent that never gets checked this closely doesn't have fewer gaps, just undiscovered ones.

What was already in place, and what wasn't enough

Two mitigations were already written into the agent's instructions before this test ever ran: a hard grounding rule against inventing anything, and a mandatory self-check pass to re-verify every claim before answering.

Both were active when the fabrication happened anyway - a self-check instruction isn't a guarantee, and the Guardrail layer's default (Microsoft.DefaultV2) never targeted grounding accuracy in the first place.

The concrete fix? an explicit rule that no number may appear unless it matches the source verbatim was scoped but not yet written back in at the time of writing.

None of this, the grounding, the retrieval, the live test, would have moved nearly as fast without two other things built alongside it: tooling for operating Foundry and SharePoint, instead of hand-typing every command.


What We Built to Get Here: `foundrl` and `graphctrl`

Neither toolkit was planned up front, both came from hitting the same frictions. What matters about each of them is the specific, ordinary pain each one was built to remove.

`foundrl`: no more re-pasting prompts into the portal

Why foundrl exists: iterating on a Foundry agent's instructions meant manually re-pasting a prompt file into the Foundry portal UI every single revision - slow, easy to get wrong, and impossible to script or version-control.

foundrl turns that into one command each way: pull an agent's live prompt down to a local file, edit it properly, push it back.

Shell
.\scripts\Sync-FoundrlAgentPull.ps1 -Agent bob-james-career-agent
# Pulls the live instructions down to agent.json

.\scripts\Sync-FoundrlAgentPush.ps1 -Agent bob-james-career-agent
# Pushes the edited agent.json back as a new version

No more portal round-trips, and the prompt finally lives somewhere with version control instead of only inside the portal's own versioning history.

`graphctrl`: a config file instead of a repeated manual chore

Why graphctrl exists: creating a declared SharePoint folder structure by hand meant repeating interactive Connect-MgGraph / New-MgDriveItemChild commands. graphctrl turns "what folders should exist" into a config file instead of a script you re-type.

Shell
.\scripts\New-GraphctrlFolderStructure.ps1 -Structure foundry -DryRun
# Previews the structure - nothing written yet

.\scripts\New-GraphctrlFolderStructure.ps1 -Structure foundry
# Creates it for real, safe to re-run without duplicating anything

The first live run took plenty of debugging to get working end to end. But same as foundrl, the point was never the bug list; it's that a folder structure is now something you declare, not something you retype.

Both toolkits follow the same shape underneath: an importable PowerShell module, thin script wrappers around it, and a plan/ folder logging every issue and decision, not just the final answer - because the wrong turns turned out to be worth keeping, even if they're not the headline.


More Than a Build Log: The DevRel Gaps This Project Actually Closes

None of this started as a career exercise, but it became a genuinely useful one, and it's worth naming that directly instead of leaving it implied.

A separate working session with the same career agent, this time in voice mode, mapped out exactly what was missing to back up a developer-relations-facing positioning: no talks, no public technical writing, no hands-on API projects documented publicly.

The agent's own priority plan named three concrete, verifiable proofs to close that gap, in order: a meetup talk (event, recorded video, slide deck and write-up), a developer-facing tutorial and one genuine open-source contribution.

The project turned into a genuinely wide tour of the Microsoft stack along the way, not a narrow one:

  • Microsoft Entra ID, Azure subscriptions and RBAC
  • Microsoft Foundry, Azure OpenAI, Foundry IQ, Azure AI Search
  • Microsoft Graph - both the API and its PowerShell SDK
  • SharePoint Online, Copilot Studio, Microsoft 365 Copilot
  • PowerShell 5.1 and 7, PSScriptAnalyzer
  • Azure Cost Management, Application Insights

That's not a list assembled to look impressive after the fact. It's the real surfaces touched getting from a bare subscription to a working, grounded agent.

Exactly the kind of cross-platform depth that's hard to fake and easy to verify.


That's a Wrap for the September Microsoft Agent-a-thon

Although the Microsoft Agent-a-thon submission itself didnt quite make it we have a working, grounded Foundry agent, and two production PowerShell toolkits built to run and iterate on it properly.

It's what three days of verifiable work looks like, and it's already doing double duty as the developer-facing tutorial and public code it's own career agent said was worth having.

Hi, I'm Bob, the Tech Wizard nobody's heard of.

Until the next build log it's build time make sure you have a great time.

Login to vote on this article

Share:

Discussion

Login to add a comment

No comments yet 🤯 but its such an awesome article.
Be the first to comment and start a discussion 🤗