The Azure DevOps Flavor — and Making the Pattern Yours
2026-07-034 min readAzure, AI, DevOps, CI/CD
Chapter 3 walked the GitHub Actions flow. But plenty of organizations — especially the enterprise Azure shops most likely to be deploying Foundry agents — live in Azure DevOps. The good news sits right in the reference repo: foundry-agents-lifecycle ships a complete ADO implementation in .azdo/pipelines/ alongside the GitHub one (thanks again to Eric Chansen for maintaining both — comparing them side by side is half the education). Same Python scripts, same Bicep, same eval gates; only the YAML dialect and the auth plumbing change.
What maps to what
| Concept | GitHub Actions | Azure DevOps |
|---|---|---|
| Pipeline files | .github/workflows/ci.yml, cd.yml | .azdo/pipelines/ci-pipeline.yml, cd-pipeline.yml |
| Azure auth | OIDC federation + azure/login | Service connection (workload identity federation) |
| Promotion gates | GitHub Environments + required reviewers | ADO Environments + approvals and checks |
| Reuse across stages | Reusable/composite workflows | YAML templates (templates/deploy-stage.yml) |
| Azure calls | run: + az/python | AzureCLI@2 task |
The deploy stage itself becomes:
# .azdo/pipelines/cd-pipeline.yml (shape)
stages:
- template: templates/deploy-stage.yml
parameters: { environment: dev, autoApprove: true }
- template: templates/deploy-stage.yml
parameters: { environment: test }
- template: templates/deploy-stage.yml
parameters: { environment: prod }# templates/deploy-stage.yml (core of it)
- task: AzureCLI@2
inputs:
azureSubscription: "foundry-${{ parameters.environment }}" # service connection
scriptType: bash
inlineScript: |
python src/scripts/deploy_agent.py --env ${{ parameters.environment }}
python src/scripts/run_evaluation.py --env ${{ parameters.environment }}That deploy-stage.yml template is the ADO version of chapter 2's mantra — same logic, different parameters. One template, three instantiations; fix a bug once and every environment inherits it.
The two pieces worth configuring carefully
Service connections — insist on workload identity federation. ADO's classic service connections stored a service principal secret that expired at the worst possible moment. The modern default uses workload identity federation — the moral equivalent of chapter 3's OIDC story: no stored secret, short-lived tokens, per-connection scoping. Create one connection per environment (foundry-dev, foundry-test, foundry-prod), each mapped to an identity holding Azure AI User on only its own environment's Foundry resource. The blast radius of any single connection stays one environment wide.
ADO Environments — richer gates than you might expect. Like GitHub Environments, ADO Environments attach approvals to stages (test: one approver; prod: two-plus). But ADO's checks go further if you want them: business-hours windows, Azure Monitor alert checks, invoked-REST-API checks, exclusive locks to serialize prod deploys. Enterprises with change-management processes can encode a surprising amount of them here instead of in a ticketing system.
The evaluation gate needs no translation at all — run_evaluation.py exits non-zero below threshold, and a failed task blocks the stage in ADO exactly as it does in Actions. Quality gates are portable because they live in scripts, not in pipeline vendor features. That's a design principle, and it's why the repo keeps its pipelines thin.
Making the pattern yours
The repo is an educational demo — a calculator tool and a sample prompt. Adapting it to a real agent is mostly substitution. The checklist I'd work through:
- Swap the agent definition. Your prompt into
src/agent/prompts/system_prompt.md, your function tools intosrc/agent/tools/, real names and models into the threeconfig/agent-config.{env}.jsonfiles. If you prototyped in the Foundry portal, this is the moment you codify (portal → code, once). - Right-size the environments. Three environments is enterprise-shaped. A personal project or small team does fine with two — dev and prod — which means deleting a parameter file and a stage, not re-architecting. (My own site effectively runs dev = local, prod = live.)
- Write the eval dataset before you tighten thresholds. Twenty to fifty real questions in
eval_dataset.jsonl, including the should-say-I-don't-know and adversarial cases. Start thresholds forgiving, ratchet up as the dataset matures — a strict gate over a weak dataset just teaches your team to click re-run. - Wire the auth for your platform. GitHub: OIDC federated credentials pinned per environment. ADO: workload-identity service connections per environment. Either way: Azure AI User per environment, nothing broader, zero stored secrets.
- Decide the multi-agent story early. More agents can mean more config files in one repo (fine to start) or a repo per agent with a shared template (better past a handful). The building blocks don't change; the multiplication factor does.
- Mind the adjacent resources. If your agent's tools call Logic Apps or other services, those need their own CI/CD lane — the repo's
docs/enterprise-guidance.mdcovers the Logic Apps patterns (Standard supports zip deploy; Consumption means ARM/Bicep). Deploy tools before the agent that references them.
Where this lands on my own roadmap
Full disclosure: the agents on this site (the chat assistant, the blog-writing agent, the contact triage agent) are currently deployed portal-first — reasonable for a solo project with one environment. But the failure modes from chapter 1 don't care about team size; they just arrive slower. Codifying my three agents into exactly this structure — definitions in the site repo, create_version deploys, my existing eval habits promoted to gates — is now on the roadmap, and this series doubles as my implementation notes.
Wrapping up the series
The whole series in four sentences: Foundry agents have no artifact — the agent is configuration, so it lives in git (chapter 1). A lifecycle repo needs four blocks: Bicep infra, per-environment configs, markdown prompts, and a thin SDK deploy script built on create_version (chapter 2). Pipelines add validation, secret-free auth, human approvals, and evaluations as blocking gates (chapter 3). And the pattern is platform-portable — GitHub Actions or Azure DevOps — because the logic lives in scripts, not pipeline YAML (this chapter).
Go clone foundry-agents-lifecycle, run azd up, and watch an agent travel dev → test → prod with quality gates in an afternoon. Credit to Eric Chansen for building the reference this series stands on — the best kind of repo: small enough to read completely, complete enough to steal from confidently.
Questions about any chapter? The contact form on this site goes through an AI triage pipeline built with these same ingredients — your message will find me well-categorized.