Skip to content

Commit dcbdc29

Browse files
docs: add service connections guide and rename Execution stage to SafeOutputs (#560)
- Add setup/service-connections.mdx with step-by-step ARM service connection creation instructions (automatic SP, WIF, manual SP with secret) - Add brief service connection mention in quick-start.mdx linking to the new page - Rename all 'Execution' stage/job references to 'SafeOutputs' across docs site Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e5cc86b commit dcbdc29

9 files changed

Lines changed: 212 additions & 17 deletions

File tree

site/src/content/docs/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ stages:
5656
# Network-isolated sandbox, read-only token...
5757
- stage: Detection
5858
# AI threat scan of proposed outputs...
59-
- stage: Execution
59+
- stage: SafeOutputs
6060
# Apply approved PRs and comments...
6161
```
6262
</Fragment>

site/src/content/docs/introduction/how-it-works.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ The second stage reviews the agent's proposed outputs. Its job is to detect prob
2626

2727
Only approved proposals continue to the next stage.
2828

29-
### 3. Execution
29+
### 3. SafeOutputs
3030

31-
The third stage applies approved actions with a separate write-capable token. This stage can create or update Azure DevOps resources such as pull requests, comments, work items, and related artifacts.
31+
The third stage applies approved actions with a separate write-capable token.This stage can create or update Azure DevOps resources such as pull requests, comments, work items, and related artifacts.
3232

3333
Because the write credential is isolated from the agent, the system keeps a strong boundary between reasoning and mutation.
3434

@@ -61,7 +61,7 @@ flowchart TD
6161
B --> C["Azure DevOps pipeline YAML"]
6262
C --> D["Stage 1: Agent"]
6363
D -->|"safe-output proposals"| E["Stage 2: Detection"]
64-
E -->|"approved proposals"| F["Stage 3: Execution"]
64+
E -->|"approved proposals"| F["Stage 3: SafeOutputs"]
6565
6666
style A fill:#7c3aed,color:#fff,stroke:#5b21b6
6767
style B fill:#6d28d9,color:#fff,stroke:#4c1d95

site/src/content/docs/reference/filter-ir.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ When Tier 2/3 filters are configured, the `TriggerFiltersExtension`
365365
during compilation via the `validate()` trait method
366366

367367
The extension uses the `setup_steps()` trait method (not `prepare_steps()`)
368-
because the gate must run in the **Setup job** (before the Execution job).
368+
because the gate must run in the **Setup job** (before the SafeOutputs job).
369369

370370
### Tier 1 Inline Path
371371

site/src/content/docs/reference/network.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ permissions:
129129
### Security Model
130130

131131
- **`permissions.read`**: Mints a read-only ADO-scoped token given to the agent inside the AWF sandbox (Stage 1). The agent can query ADO APIs but cannot write.
132-
- **`permissions.write`**: Mints a write-capable ADO-scoped token used **only** by the executor in Stage 3 (`Execution` job). This token is never exposed to the agent.
132+
- **`permissions.write`**: Mints a write-capable ADO-scoped token used **only** by the executor in Stage 3 (`SafeOutputs` job). This token is never exposed to the agent.
133133
- **Both omitted**: No ADO tokens are passed anywhere. The agent has no ADO API access.
134134

135135
### Compile-Time Validation

site/src/content/docs/reference/safe-outputs.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ Creates a pull request with code changes made by the agent. When invoked:
127127

128128
During Stage 3 execution, the repository is validated against the allowed list (from `checkout:` + "self"), then the patch is applied and a PR is created in Azure DevOps.
129129

130-
**Stage 3 Execution Architecture (Hybrid Git + ADO API):**
130+
**Stage 3 SafeOutputs Architecture (Hybrid Git + ADO API):**
131131

132132
```text
133133
┌─────────────────────────────────────────────────────────────────┐
134-
Stage 3 Execution
134+
│ Stage 3 SafeOutputs
135135
├─────────────────────────────────────────────────────────────────┤
136136
│ │
137137
│ 1. Security Validation │

site/src/content/docs/reference/targets.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The `target` field in the front matter determines the output format and executio
1010
### `standalone` (default)
1111

1212
Generates a self-contained Azure DevOps pipeline with:
13-
- Full 3-job pipeline: `Agent` -> `Detection` -> `Execution`
13+
- Full 3-job pipeline: `Agent` -> `Detection` -> `SafeOutputs`
1414
- AWF (Agentic Workflow Firewall) L7 domain whitelisting via Squid proxy + Docker
1515
- MCP Gateway (MCPG) for MCP routing with SafeOutputs HTTP backend
1616
- Setup/teardown job support
@@ -23,7 +23,7 @@ This is the recommended target for maximum flexibility and security controls.
2323
Generates a pipeline that extends the 1ES Unofficial Pipeline Template:
2424
- Uses `templateContext.type: buildJob` with Copilot CLI + AWF + MCPG (same execution model as standalone)
2525
- Integrates with 1ES SDL scanning and compliance tools
26-
- Full 3-job pipeline: Agent -> Detection -> Execution
26+
- Full 3-job pipeline: Agent -> Detection -> SafeOutputs
2727
- Requires 1ES Pipeline Templates repository access
2828

2929
Example:
@@ -39,7 +39,7 @@ Generates a **job-level ADO YAML template** with `jobs:` at root. This is a
3939
reusable template that can be included in an existing pipeline -- it does not
4040
generate a complete pipeline.
4141

42-
The output contains the same 3-job chain (Agent -> Detection -> Execution) as
42+
The output contains the same 3-job chain (Agent -> Detection -> SafeOutputs) as
4343
`standalone`, with:
4444
- Job names prefixed with the agent name for uniqueness (e.g., `DailyReview_Agent`)
4545
- No triggers, pipeline name, or resource declarations (the parent pipeline owns those)

site/src/content/docs/reference/template-markers.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Examples of fuzzy schedule -> cron conversion:
8282

8383
Should be replaced with the `checkout: self` step. This generates a simple checkout of the triggering branch.
8484

85-
All checkout steps across all jobs (Agent, Detection, Execution, Setup, Teardown) use this marker.
85+
All checkout steps across all jobs (Agent, Detection, SafeOutputs, Setup, Teardown) use this marker.
8686

8787
## `{{ checkout_repositories }}`
8888
Should be replaced with checkout steps for additional repositories the agent will work with. The behavior depends on the `checkout:` front matter:
@@ -174,7 +174,7 @@ If `setup` is empty, this is replaced with an empty string.
174174
## `{{ teardown_job }}`
175175

176176
Generates a separate teardown job YAML if `teardown` contains steps. The job:
177-
- Runs after `Execution` (depends on it)
177+
- Runs after `SafeOutputs` (depends on it)
178178
- Uses the same pool as the main agentic task
179179
- Includes a checkout of self
180180
- Display name: `Teardown`
@@ -436,7 +436,7 @@ If `permissions.read` is not configured, this marker is replaced with an empty s
436436

437437
## `{{ acquire_write_token }}`
438438

439-
Generates an `AzureCLI@2` step that acquires a write-capable ADO-scoped access token from the ARM service connection specified in `permissions.write`. This token is used only by the executor in Stage 3 (`Execution` job) and is never exposed to the agent.
439+
Generates an `AzureCLI@2` step that acquires a write-capable ADO-scoped access token from the ARM service connection specified in `permissions.write`. This token is used only by the executor in Stage 3 (`SafeOutputs` job) and is never exposed to the agent.
440440

441441
The step:
442442
- Uses the ARM service connection from `permissions.write`
@@ -529,7 +529,7 @@ jobs:
529529
- job: DailyCodeReview_Agent
530530
- job: DailyCodeReview_Detection
531531
dependsOn: DailyCodeReview_Agent
532-
- job: DailyCodeReview_Execution
532+
- job: DailyCodeReview_SafeOutputs
533533
dependsOn: [DailyCodeReview_Agent, DailyCodeReview_Detection]
534534
```
535535

site/src/content/docs/setup/quick-start.mdx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,22 @@ This sets the `GITHUB_TOKEN` pipeline variable on the ADO build definition. The
8686
- A **GitHub Personal Access Token (PAT)** -- used by the Copilot CLI at runtime (see [required permissions](#github-pat-permissions) below)
8787
- For Azure DevOps authentication, the command first tries the **Azure CLI** (`az` login session). If the Azure CLI is not available or not logged in, it falls back to prompting for an **Azure DevOps PAT**.
8888

89+
#### Service connections for ADO access
90+
91+
If your agent uses safe outputs that write to Azure DevOps (creating PRs, work items, comments, etc.), you need an **ARM service connection** referenced in your agent file's `permissions.write` field. This connection is used to mint short-lived ADO tokens for the executor stage.
92+
93+
```yaml
94+
permissions:
95+
write: my-write-connection # ARM service connection name
96+
```
97+
98+
See [Service Connections](/ado-aw/setup/service-connections/) for step-by-step creation instructions. You can use an existing connection if one is already configured in your project.
99+
89100
:::caution[Temporary limitation: GitHub PAT required]
90101
The Copilot CLI does not yet support GitHub App tokens. You must provide a fine-grained GitHub PAT. This requirement will be removed once GitHub App token support is added to the Copilot CLI.
91102
:::
92103
93-
Run the pipeline in Azure DevOps -- it executes the three-stage workflow: Agent -> Detection -> Execution.
104+
Run the pipeline in Azure DevOps -- it executes the three-stage workflow: Agent -> Detection -> SafeOutputs.
94105
95106
---
96107
@@ -146,9 +157,11 @@ ado-aw configure
146157

147158
This sets the `GITHUB_TOKEN` pipeline variable. See the [With Agents](#3-push-and-configure) section above for details on what the command prompts for and the current GitHub PAT limitation.
148159

160+
If your workflow uses write-requiring safe outputs, you'll also need an ARM service connection — see [Service Connections](/ado-aw/setup/service-connections/).
161+
149162
### 5. Run the pipeline
150163

151-
Back in Azure DevOps, run the pipeline. It executes the compiled three-stage workflow: Agent -> Detection -> Execution.
164+
Back in Azure DevOps, run the pipeline. It executes the compiled three-stage workflow: Agent -> Detection -> SafeOutputs.
152165

153166
---
154167

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
title: Service Connections
3+
description: Create the Azure Resource Manager service connections needed for ado-aw pipelines
4+
sidebar:
5+
order: 2
6+
---
7+
8+
import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
9+
10+
`ado-aw` pipelines use **Azure Resource Manager (ARM) service connections** to mint Azure DevOps-scoped tokens at runtime. These tokens grant the pipeline controlled access to ADO APIs — separately for the agent (read) and the executor (write).
11+
12+
## Why service connections?
13+
14+
The compiled pipeline uses the `AzureCLI@2` task to call `az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798` — this mints a short-lived ADO token scoped to the service connection's identity. This keeps credentials short-lived and auditable, and avoids storing long-lived PATs.
15+
16+
| Connection | Used by | Purpose |
17+
|------------|---------|---------|
18+
| **Write** (required for safe outputs) | Stage 3 — SafeOutputs executor | Create PRs, work items, wiki pages, etc. |
19+
| **Read** (optional) | Stage 1 — Agent | Query ADO APIs (work items, repos, builds) |
20+
21+
:::tip[Reusing existing connections]
22+
If your project already has ARM service connections with appropriate ADO permissions, you can reuse them — just reference their names in `permissions.write` / `permissions.read` in your agent file. Skip to [Verify your connection works](#verify-your-connection-works) to confirm they're suitable.
23+
:::
24+
25+
---
26+
27+
## Create the write service connection
28+
29+
This is the minimum required connection. It powers Stage 3 safe-output execution.
30+
31+
<Steps>
32+
1. Go to your **Azure DevOps Project → Project Settings → Service connections**
33+
2. Click **New service connection → Azure Resource Manager**
34+
3. Choose your authentication method:
35+
36+
<Tabs>
37+
<TabItem label="Service principal (automatic)">
38+
The simplest option — Azure DevOps creates the app registration and credentials for you.
39+
40+
- Select **Service principal (automatic)**
41+
- Choose an Azure subscription (the identity needs at least **Reader** on the subscription — this is only used for `az` login, not for ADO operations)
42+
- Scope to a resource group if preferred
43+
- Name the connection (e.g. `ado-aw-write`) — this is the value you'll use in `permissions.write`
44+
- Click **Save**
45+
46+
Azure DevOps creates an Entra ID app registration and configures everything automatically.
47+
</TabItem>
48+
<TabItem label="Workload Identity Federation (manual)">
49+
Secretless — no client secrets to rotate. Use this if your organization requires manual control.
50+
51+
- Create an [App Registration in Entra ID](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps) (note the Application ID and Tenant ID)
52+
- In ADO, select **Workload Identity federation (manual)**
53+
- Fill in the Subscription ID, Service Principal Id (Application ID), and Tenant ID
54+
- Name the connection (e.g. `ado-aw-write`)
55+
- Click **Verify and save**
56+
- Copy the generated **Issuer** and **Subject identifier** from the connection details
57+
- Back in Azure Portal → App Registration → **Certificates & secrets → Federated credentials****Add credential** with the Issuer and Subject values
58+
</TabItem>
59+
<TabItem label="Service principal with secret (manual)">
60+
Use if your organization doesn't support Workload Identity Federation or automatic provisioning.
61+
62+
- Create an [App Registration in Entra ID](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps) (note the Application ID and Tenant ID)
63+
- In the App Registration → **Certificates & secrets****New client secret** (copy the value)
64+
- In ADO, select **Service principal (manual)**
65+
- Fill in the Subscription ID, Service Principal Id, Service principal key (secret), and Tenant ID
66+
- Name the connection (e.g. `ado-aw-write`)
67+
- Click **Verify and save**
68+
69+
:::caution[Secret rotation]
70+
Client secrets expire. Set a reminder to rotate before expiry, or prefer Workload Identity Federation to avoid this overhead.
71+
:::
72+
</TabItem>
73+
</Tabs>
74+
75+
4. **Grant ADO permissions** to the service principal. The identity behind the connection needs permission to perform write operations in Azure DevOps:
76+
- Go to **Azure DevOps Organization Settings → Users**
77+
- Add the service principal (search by its app registration name)
78+
- Set access level to **Basic**
79+
- Add it to a project-level group with the permissions your safe outputs need — **Contributors** is sufficient for most use cases (PRs, work items, branches, tags, wiki pages)
80+
81+
5. **Reference it** in your agent front matter:
82+
83+
```yaml
84+
permissions:
85+
write: ado-aw-write # ← name of the service connection you created
86+
```
87+
</Steps>
88+
89+
:::caution[Least privilege]
90+
Only grant the permissions your workflows actually use. If your agents only create work item comments, you don't need full Contributor access. See [Safe Outputs reference](/ado-aw/reference/safe-outputs/) for what each tool requires.
91+
:::
92+
93+
:::note[Compile-time safety check]
94+
If you configure safe outputs that require write access (e.g. `create-pull-request`, `create-work-item`, `add-pr-comment`) but omit `permissions.write`, compilation will fail with a clear error. This ensures write operations always have an explicitly configured credential.
95+
:::
96+
97+
---
98+
99+
## Create the read service connection (optional)
100+
101+
If your agent needs to query Azure DevOps APIs (e.g. read work items, list PRs, fetch build results), create a second connection with **read-only** ADO permissions.
102+
103+
Follow the same steps as above, but:
104+
105+
- Name the connection `ado-aw-read` (or similar)
106+
- In ADO, grant the service principal **Readers** group access (or a custom group with only read permissions)
107+
108+
Then reference both in your agent file:
109+
110+
```yaml
111+
permissions:
112+
read: ado-aw-read
113+
write: ado-aw-write
114+
```
115+
116+
:::note[Why separate connections?]
117+
Separation ensures the Stage 1 agent — which runs untrusted AI-generated code inside an AWF sandbox — can never perform write operations, even if prompt-injected. The write token only exists in Stage 3, after detection has passed.
118+
:::
119+
120+
### Permission combinations
121+
122+
| Configuration | Agent can read ADO? | Safe outputs can write? |
123+
|---|---|---|
124+
| Both `read` + `write` | ✅ | ✅ |
125+
| Only `read` | ✅ | ❌ |
126+
| Only `write` | ❌ | ✅ |
127+
| Neither (default) | ❌ | ❌ |
128+
129+
---
130+
131+
## Authorize the pipeline
132+
133+
On the first run, Azure DevOps will prompt you to authorize the pipeline to use the service connections. You can also pre-authorize:
134+
135+
<Steps>
136+
1. In the service connection's settings, go to **Security**
137+
2. Under **Pipeline permissions**, click **+** and add your ado-aw pipeline (or grant access to all pipelines in the project)
138+
</Steps>
139+
140+
---
141+
142+
## Verify your connection works
143+
144+
To confirm your service connection can mint ADO tokens, create a test pipeline:
145+
146+
```yaml
147+
trigger: none
148+
149+
pool:
150+
vmImage: ubuntu-latest
151+
152+
steps:
153+
- task: AzureCLI@2
154+
displayName: "Test ADO token mint"
155+
inputs:
156+
azureSubscription: 'ado-aw-write' # your connection name
157+
scriptType: 'bash'
158+
scriptLocation: 'inlineScript'
159+
inlineScript: |
160+
TOKEN=$(az account get-access-token \
161+
--resource 499b84ac-1321-427f-aa17-267ca6975798 \
162+
--query accessToken -o tsv)
163+
if [ -n "$TOKEN" ]; then
164+
echo "✅ Successfully minted ADO token (${#TOKEN} chars)"
165+
else
166+
echo "❌ Failed to mint ADO token"
167+
exit 1
168+
fi
169+
```
170+
171+
If this pipeline succeeds, your connection is correctly configured for `ado-aw`.
172+
173+
---
174+
175+
## Troubleshooting
176+
177+
| Symptom | Likely cause |
178+
|---------|-------------|
179+
| `AzureCLI@2` fails with "service connection not found" | The pipeline isn't authorized to use the connection — check pipeline permissions in the connection's Security tab |
180+
| Token mints but safe outputs return 401/403 | The service principal doesn't have sufficient ADO permissions — verify its group membership in ADO Organization Settings → Users |
181+
| "AADSTS700024: Client assertion is not within its valid time range" | Federated credential issuer/subject mismatch — regenerate in the App Registration |
182+
| Compilation error: "require write access to ADO, but no write service connection is configured" | Your agent uses write-requiring safe outputs but is missing `permissions.write` in front matter |

0 commit comments

Comments
 (0)