An agentic pipeline compiler for Azure DevOps. Write pipeline definitions in human-friendly markdown, compile them into secure, multi-stage Azure DevOps pipelines that run AI agents in network-isolated sandboxes.
Inspired by GitHub Agentic Workflows (gh-aw).
If you are an AI agent tasked with creating an ado-aw workflow, read
prompts/create-ado-agentic-workflow.mdfor step-by-step instructions on authoring a workflow file.
You author an agent file — a markdown document with YAML front matter that
describes what the agent should do, when it should run, and which tools it
can use. The ado-aw compiler transforms that file into a production-ready Azure
DevOps pipeline with three jobs:
┌────────────────────────┐ ┌──────────────────────┐ ┌───────────────────────┐
│ PerformAgenticTask │────▶│ AnalyzeSafeOutputs │────▶│ ProcessSafeOutputs │
│ (Stage 1 — Agent) │ │ (Threat Analysis) │ │ (Stage 2 — Executor) │
│ │ │ │ │ │
│ • Runs inside AWF │ │ • Reviews proposed │ │ • Creates PRs │
│ network sandbox │ │ actions for safety │ │ • Creates work items │
│ • Read-only ADO token │ │ • Checks for prompt │ │ • Write ADO token │
│ • Produces safe │ │ injection, leaks │ │ • Never exposed to │
│ output proposals │ │ │ │ the agent │
└────────────────────────┘ └──────────────────────┘ └───────────────────────┘
The agent never has direct write access. All mutations (pull requests, work items) go through a safe outputs pipeline where they are threat-analyzed and then executed with a separate, scoped write token.
Download a release binary from GitHub Releases, or build from source:
cargo build --releaseUse the interactive wizard to scaffold a new agent file:
ado-aw create -o agents/The wizard walks you through:
- Name & description — human-readable identity for the agent
- Model — AI engine (
claude-opus-4.5,claude-sonnet-4.5,gpt-5.2-codex,gemini-3-pro-preview, etc.) - Schedule — when the agent runs, using fuzzy schedule syntax
- Workspace —
rootorrepoworking directory - Repositories — additional repos the agent can access
- MCP servers — tool integrations (ADO, Kusto, IcM, etc.)
- Permissions — ARM service connections for ADO access
This generates a markdown file like:
---
name: "Dependency Updater"
description: "Checks for outdated dependencies and opens PRs"
engine: claude-sonnet-4.5
schedule: weekly on monday around 9:00
pool: AZS-1ES-L-MMS-ubuntu-22.04
mcp-servers:
ado: true
permissions:
read: my-read-arm-connection
write: my-write-arm-connection
safe-outputs:
create-pull-request:
target-branch: main
auto-complete: true
squash-merge: true
reviewers:
- "team-lead@example.com"
---
## Instructions
Check all dependency manifests in this repository. For each outdated
dependency, update it to the latest stable version and create a pull
request with a clear description of what changed and why.ado-aw compile agents/dependency-updater.md -o .pipelines/dependency-updater.ymlThis generates a complete Azure DevOps pipeline YAML file. The compiler also
copies the agent markdown body into agents/dependency-updater.md in the output
tree so it's available at runtime.
Ensure pipelines stay in sync with their source:
ado-aw check agents/dependency-updater.md .pipelines/dependency-updater.ymlThis is useful as a CI gate — if someone edits the markdown but forgets to recompile, the check will fail.
Your repo should contain:
your-repo/
├── agents/
│ └── dependency-updater.md # Agent source (instructions + config)
└── .pipelines/
└── dependency-updater.yml # Compiled pipeline
Push both files to your Azure DevOps repository.
- Go to Pipelines → New Pipeline
- Select your repository
- Choose Existing Azure Pipelines YAML file
- Point to
.pipelines/dependency-updater.yml - Save (or Save & Run)
This is the most important configuration step. Azure DevOps does not support fine-grained PAT scoping — tokens are either read or read-write across the project. To maintain security isolation between the agent and the executor, you need two separate ARM service connections:
| Read Connection | Write Connection | |
|---|---|---|
| Used by | Stage 1 — the AI agent | Stage 2 — the safe outputs executor |
| Purpose | Query ADO APIs (work items, repos, PRs) | Create PRs, work items, link artifacts |
| Exposed to agent? | ✅ Yes (inside network sandbox) | ❌ Never |
| Token variable | SC_READ_TOKEN |
SC_WRITE_TOKEN |
| Front matter field | permissions.read |
permissions.write |
The agent runs in a network-isolated sandbox (AWF) with only the read token.
Even if the agent were compromised or prompt-injected, it cannot perform write
operations. Write actions are only executed in Stage 2 (ProcessSafeOutputs)
after threat analysis, using a completely separate token that the agent never
sees.
-
Navigate to Project Settings → Service connections → New service connection
-
Choose Azure Resource Manager → Service principal (automatic) (or manual if your organization requires it)
-
Create two connections:
Read connection (e.g.,
ado-agent-read):- Scope: subscription or resource group level
- Grants: the ability to mint read-only ADO-scoped tokens
- Used by: the agent job to call
az account get-access-tokenwith the ADO resource ID (499b84ac-1321-427f-aa17-267ca6975798)
Write connection (e.g.,
ado-agent-write):- Scope: subscription or resource group level
- Grants: the ability to mint read-write ADO-scoped tokens
- Used by: the executor job to create PRs, work items, etc.
-
Reference them in your agent front matter:
permissions: read: ado-agent-read write: ado-agent-write
Important
If you configure safe outputs that require write access (create-pull-request
or create-work-item) but omit permissions.write, compilation will fail
with a clear error. This is a safety check — write operations must always
have an explicitly configured credential.
| Configuration | Agent can read ADO? | Safe outputs can write? |
|---|---|---|
Both read + write |
✅ | ✅ |
Only read |
✅ | ❌ |
Only write |
❌ | ✅ |
| Neither (default) | ❌ | ❌ |
On the first run, Azure DevOps will prompt you to authorize the pipeline to use the service connections. Approve the permissions and the pipeline is ready.
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | required | Human-readable name for the agent |
description |
string | required | One-line summary of the agent's purpose |
target |
standalone | 1es |
standalone |
Pipeline output format |
engine |
string or object | claude-opus-4.5 |
AI model to use |
schedule |
string or object | — | Fuzzy schedule expression |
pool |
string or object | AZS-1ES-L-MMS-ubuntu-22.04 |
Agent pool |
workspace |
root | repo |
auto | Working directory mode |
repositories |
list | — | Additional repository resources |
checkout |
list | — | Which repositories to check out |
mcp-servers |
map | — | MCP server configuration |
permissions |
object | — | ARM service connections (read, write) |
safe-outputs |
object | — | Per-tool configuration |
triggers |
object | — | Pipeline trigger configuration |
steps |
list | — | Inline steps before agent runs |
post-steps |
list | — | Inline steps after agent runs |
setup |
list | — | Separate job before agentic task |
teardown |
list | — | Separate job after safe outputs |
network |
object | — | Additional allowed/blocked hosts |
Everything below the front matter --- fence is the agent's instructions. Write
natural language describing the task, constraints, and expected behavior.
The schedule field uses a fuzzy syntax that deterministically scatters
execution times based on the agent name, preventing load spikes.
# Daily
schedule: daily # Scattered across 24 hours
schedule: daily around 14:00 # Within ±60 min of 2 PM
schedule: daily between 9:00 and 17:00 # Business hours
# Weekly
schedule: weekly on monday around 9:00 # Monday morning
# Hourly / Minute intervals
schedule: hourly # Every hour, scattered minute
schedule: every 2h # Every 2 hours
schedule: every 15 minutes # Fixed, not scattered
# With timezone
schedule: daily around 14:00 utc+9 # 2 PM JST → 5 AM UTC
# With branch filtering
schedule:
run: daily around 14:00
branches:
- main
- release/*MCP (Model Context Protocol) servers give the agent access to external tools.
Built-in MCPs are enabled by name; custom MCPs specify a command:
mcp-servers:
# Built-in — enable with true or restrict with allowed list
ado: true
kusto:
allowed:
- query
# Custom — must include command
my-tool:
command: "node"
args: ["path/to/server.js"]
allowed:
- search
- analyzeAvailable built-in MCPs: ado, ado-ext, asa, bluebird, calculator,
es-chat, icm, kusto, msft-learn, stack.
Each MCP automatically adds its required domains to the network allowlist.
Safe outputs are the only way agents produce side effects. The agent proposes actions, and the executor processes them after threat analysis.
| Tool | Description |
|---|---|
create-pull-request |
Creates a PR from the agent's code changes |
create-work-item |
Creates an ADO work item (Task, Bug, etc.) |
memory |
Persists files across agent runs |
noop |
Reports no action was needed |
missing-data |
Reports required data was unavailable |
missing-tool |
Reports a needed tool was missing |
safe-outputs:
create-pull-request:
target-branch: main
auto-complete: true
delete-source-branch: true
squash-merge: true
reviewers:
- "reviewer@example.com"
labels:
- automated
work-items:
- 12345safe-outputs:
create-work-item:
work-item-type: Bug
area-path: "MyProject\\MyTeam"
assignee: "developer@example.com"
tags:
- agent-created
- needs-triageAgents run inside AWF (Agentic Workflow Firewall) containers with L7 domain whitelisting. Only explicitly allowed domains are reachable. The allowlist is built from:
- Core domains — Azure DevOps, GitHub, Microsoft auth, Azure storage
- MCP domains — automatically added per enabled MCP
- User domains — from
network.allowin front matter - Minus blocked —
network.blockedentries are removed
network:
allow:
- "*.mycompany.com"
- "api.external-service.com"
blocked:
- "analytics.tracking.com"ado-aw [OPTIONS] <COMMAND>
Commands:
create Create a new agent markdown file interactively
compile Compile markdown to pipeline definition
check Verify a compiled pipeline matches its source
mcp Run as an MCP server (safe outputs)
execute Execute safe outputs (Stage 2)
proxy Start an HTTP proxy for network filtering
mcp-firewall Start an MCP firewall server
Options:
-v, --verbose Enable info-level logging
-d, --debug Enable debug-level logging
# Build
cargo build
# Test
cargo test
# Lint
cargo clippyThis project uses Conventional Commits
for automated releases via release-please.
See LICENSE for details.