> ## Documentation Index
>
> Fetch the complete documentation index at: https://superpowers.frontis.nl/llms.txt
> Use this file to discover all available pages before exploring further.

---

title: Azure DevOps pipelines
summary: Frontis guidance for coding agents working with Azure DevOps cloud, YAML multi-stage pipelines, branch policies, service connections, artifacts and Azure deployments.
order: 70

---

# Azure DevOps pipelines

Use this guide when creating, reviewing or changing Azure DevOps pipelines in Frontis repositories.

This document is written for coding agents. It explains how to work safely with Azure DevOps cloud, YAML multi-stage pipelines and Azure deployments while respecting local Frontis conventions.

Frontis repositories may use:

```text
Azure DevOps cloud
Azure Repos
YAML multi-stage pipelines
Visual Studio
VS Code
.NET
Nuxt
Azure hosting
```

Local repository files remain the source of truth.

Before changing Azure DevOps YAML or deployment behavior, read:

```text
AGENTS.md
.frontis/project.json
.frontis/project-context.yaml
.frontis/naming-convention.yaml
.frontis/azure-conventions.yaml
```

Also read the existing pipeline files before proposing changes.

## Purpose

This guide helps agents:

- create clear multi-stage YAML pipelines;
- keep pipeline filenames aligned with Frontis naming conventions;
- avoid secrets in source control;
- use service connections safely;
- publish and download artifacts consistently;
- deploy to Azure in a controlled way;
- preserve production safety;
- understand Azure Repos PR validation behavior;
- summarize risks before changing deployment behavior.

## Source-of-truth priority

Use this priority order:

1. explicit user instruction;
2. local repository files;
3. Frontis online agent documentation;
4. existing pipeline style;
5. official Azure DevOps documentation;
6. general model knowledge.

Local repository files override generic examples.

Never invent Frontis pipeline or Azure naming conventions from memory.

## Required startup procedure

Before changing pipeline files:

1. Read `AGENTS.md`.
2. Read `.frontis/project.json`.
3. Read `.frontis/project-context.yaml`.
4. Read `.frontis/naming-convention.yaml`.
5. Read `.frontis/azure-conventions.yaml`.
6. Inspect existing pipeline YAML files.
7. Identify trigger behavior.
8. Identify stages, jobs, tasks and environments.
9. Identify service connections, variable groups and secure variables.
10. Identify artifact flow.
11. Identify deployment targets.
12. Summarize the effective pipeline context.
13. Produce a short plan before editing.

The effective pipeline context summary should include:

```text
company
product
component
canonical project name
pipeline filename
pipeline type
hosting model
target environments
service connection names
variable groups
artifact names
deployment targets
branch policy impact
risk level
```

## Pipeline filename convention

Pipeline filenames must follow the local naming convention.

Read:

```text
.frontis/naming-convention.yaml
```

before creating or renaming pipeline files.

Typical filename shape:

```text
{company}-{product}-{component}-{pipeline_type}-{hosting_variant}.yml
```

Typical pipeline types:

```text
infra
release
build
validation
```

Typical hosting variants:

```text
shared
dedicated
```

Example:

```text
FrontisRAD-AgentDocs-Website-release-shared.yml
```

Do not create vague pipeline names such as:

```text
azure-pipelines.yml
build.yml
deploy.yml
release.yml
pipeline.yml
main.yml
```

unless the repository convention explicitly allows them.

## Recommended pipeline structure

Prefer multi-stage YAML pipelines.

A common structure:

```yaml
stages:
  - stage: Build
    displayName: Build
    jobs:
      - job: Build
        steps:
          - checkout: self
          - script: echo "Build"

  - stage: Deploy_Prod
    displayName: Deploy production
    dependsOn: Build
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: DeployProduction
        environment: "production"
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploy"
```

Use clear stage names.

Recommended stage names:

```text
Build
Validate
Package
Deploy_Test
Deploy_Acceptance
Deploy_Prod
```

For repositories with production only:

```text
Build
Deploy_Prod
```

Do not create unused environments just because an example has them.

## Triggers

Define branch and path triggers intentionally.

Example:

```yaml
trigger:
  branches:
    include:
      - main
  paths:
    include:
      - src/**
      - tests/**
      - azure-pipelines/**
```

For Azure Repos Git, do not rely on YAML `pr` triggers for PR validation. Configure PR validation through branch policies and build validation.

Recommended for Azure Repos:

```yaml
pr: none
```

Then configure build validation on the target branch in Azure DevOps branch policies.

## Branch policies

Use branch policies for PR validation in Azure Repos.

Recommended branch policy setup:

```text
main:
  require build validation
  require reviewer approval
  require linked work item where team policy requires it
  block direct pushes where team policy requires it
```

Build validation should run:

- build;
- tests;
- lint;
- typecheck;
- convention validation;
- pipeline validation where available.

Do not change branch policies without explicit human approval.

## Service connections

Use Azure Resource Manager service connections for Azure deployments.

Do not put service principal secrets in YAML.

Pipeline YAML should reference the service connection name:

```yaml
variables:
  azureServiceConnection: "sc-frontis-agent-docs"

steps:
  - task: AzureCLI@2
    inputs:
      azureSubscription: "$(azureServiceConnection)"
      scriptType: "bash"
      scriptLocation: "inlineScript"
      inlineScript: |
        az account show
```

Rules:

- use least privilege;
- prefer scoped resource group permissions over subscription-wide permissions when possible;
- prefer workload identity federation when available;
- do not echo credentials;
- do not use personal credentials;
- do not create new service connections without approval.

## Secrets

Never put secrets in YAML.

Do not commit:

```text
client secrets
storage account keys
connection strings
passwords
certificates
private keys
personal access tokens
SAS tokens
JWT signing keys
API keys
```

Use:

- Azure DevOps secret variables;
- variable groups;
- Azure Key Vault;
- managed identity;
- service connections;
- App Service settings;
- Key Vault references.

If a secret is found in source control, stop and report it without repeating the secret.

## Variables

Use variables for stable, non-secret values.

Example:

```yaml
variables:
  nodeVersion: "24.x"
  buildConfiguration: "Release"
  artifactName: "webapp"
```

Use secure variables or variable groups for sensitive values.

Do not hide risky deployment targets behind unclear variable names.

Bad:

```yaml
variables:
  target: "prod"
```

Better:

```yaml
variables:
  productionResourceGroupName: "rg-frontis-agent-docs-prod"
```

For environment-specific configuration, prefer variable groups or stage-level variables when the values differ by environment.

## Parameters

Use parameters for reusable pipeline templates.

Example:

```yaml
parameters:
  - name: environmentName
    type: string
  - name: resourceGroupName
    type: string
  - name: storageAccountName
    type: string
```

Avoid over-abstracting a one-off pipeline.

Do not create complex template hierarchies unless they reduce duplication and remain readable.

## Artifacts

Use pipeline artifacts to pass build output from build jobs or stages into deploy jobs or stages.

Publish example:

```yaml
- publish: "$(Build.ArtifactStagingDirectory)/site"
  artifact: "frontis-agent-docs-site"
```

Download example:

```yaml
- download: current
  artifact: "frontis-agent-docs-site"
```

Rules:

- artifact names should describe the contents;
- deploy stages should deploy from artifacts, not rebuild;
- verify the expected files exist before publishing;
- avoid publishing secrets;
- keep artifacts minimal.

For static documentation sites, verify at least:

```bash
test -f "$SITE_DIR/index.html"
test -f "$SITE_DIR/llms.txt"
test -f "$SITE_DIR/llms-full.txt"
```

## Environments and approvals

Use Azure DevOps environments for deployment jobs.

Example:

```yaml
jobs:
  - deployment: DeployStaticWebsiteProd
    environment: "agent-docs-prod"
```

For production deployments, configure approval checks in Azure DevOps environments when required by the team.

Do not bypass approvals in YAML.

Do not replace a deployment job with a normal job if environment approvals are required.

## Conditions

Use conditions to protect production deployment.

Recommended production condition:

```yaml
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
```

For deployment stages, ensure they depend on successful validation stages.

Avoid conditions that accidentally deploy from feature branches.

Bad:

```yaml
condition: succeededOrFailed()
```

for production deployment.

## AzureCLI@2

Use `AzureCLI@2` when the pipeline needs to execute Azure CLI commands using an Azure Resource Manager service connection.

Recommended Linux agent example:

```yaml
- task: AzureCLI@2
  displayName: Deploy with Azure CLI
  inputs:
    azureSubscription: "$(azureServiceConnection)"
    scriptType: "bash"
    scriptLocation: "inlineScript"
    inlineScript: |
      set -euo pipefail
      az account show
```

Rules:

- use `set -euo pipefail` in Bash;
- avoid echoing secrets;
- use clear variables;
- fail fast;
- prefer idempotent Azure CLI commands;
- show deployment target before making changes.

Use normal precise language when explaining destructive Azure CLI commands.

## Static website hosting on Azure Storage

For simple static documentation hosting, Azure Storage static website hosting is a good default.

Typical deployment flow:

1. generate static documentation site;
2. publish generated site as a pipeline artifact;
3. create or verify resource group;
4. create or verify storage account;
5. enable static website hosting;
6. clear `$web` container when intended;
7. upload site files;
8. set content types for `llms.txt`, `llms-full.txt`, `.md` and `.html`;
9. output static website endpoint.

Production deployment should clearly state when the `$web` container is deleted before upload.

Example warning:

```text
This deployment deletes all existing blobs in the `$web` container before uploading the new site. Confirm the storage account and artifact path before running it.
```

Use normal precise language for this warning.

## Static docs pipeline pattern

Recommended stages for production-only static docs:

```text
Build
Deploy_Prod
```

Build stage:

- checkout repository;
- install Node;
- run `scripts/generate-llms.mjs`;
- verify generated files;
- publish artifact.

Deploy stage:

- download artifact;
- create or verify Azure resources;
- enable static website hosting;
- delete old static site content when intended;
- upload new static site content;
- set content types;
- output endpoint.

## .NET build pattern

Typical .NET build stage:

```yaml
- script: dotnet restore
  displayName: Restore

- script: dotnet build --configuration Release --no-restore
  displayName: Build

- script: dotnet test --configuration Release --no-build
  displayName: Test
```

For real repositories, prefer solution-specific paths where useful:

```bash
dotnet restore ./Company.Product.Component.sln
dotnet build ./Company.Product.Component.sln --configuration Release --no-restore
dotnet test ./Company.Product.Component.sln --configuration Release --no-build
```

Do not hardcode solution names without reading the repository.

## Nuxt build pattern

Typical Nuxt build stage:

```yaml
- task: NodeTool@0
  inputs:
    versionSpec: "$(nodeVersion)"

- script: npm ci
  displayName: Install

- script: npm run lint
  displayName: Lint

- script: npm run typecheck
  displayName: Typecheck

- script: npm run test
  displayName: Test

- script: npm run build
  displayName: Build
```

Use the package manager already used by the repository.

Do not switch between `npm`, `pnpm` and `yarn` without approval.

## Path filters

Use path filters to avoid unnecessary pipeline runs.

Example:

```yaml
trigger:
  branches:
    include:
      - main
  paths:
    include:
      - docs/ai/**
      - scripts/generate-llms.mjs
      - azure-pipelines/FrontisRAD-AgentDocs-Website-release-shared.yml
```

Be careful not to exclude files that affect the build.

If the pipeline depends on shared templates, include those paths too.

## YAML safety

YAML is whitespace-sensitive.

When editing YAML:

- preserve indentation;
- preserve quotes around values that need them;
- avoid tabs;
- keep scripts readable;
- prefer block scalars for scripts;
- do not hide complex logic in one-liners;
- avoid unreviewed copy-paste from unrelated pipelines.

Use exact variable syntax.

Common runtime variables:

```text
$(Build.ArtifactStagingDirectory)
$(Pipeline.Workspace)
$(Build.SourceBranch)
$(System.DefaultWorkingDirectory)
```

Common expression syntax:

```yaml
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
```

Do not mix runtime variables and template expressions unless needed.

## Templates

Use pipeline templates when multiple repositories share the same pipeline pattern.

Good template candidates:

- .NET build;
- Nuxt build;
- static docs build;
- Azure Storage static website deploy;
- test result publishing;
- artifact publishing.

Avoid templates for one-off logic that would be clearer inline.

Template changes affect all consumers and require extra review.

## Validation

Before claiming a pipeline change is complete:

- inspect YAML syntax;
- check indentation;
- check variable names;
- check stage dependencies;
- check artifact names;
- check service connection names;
- check environment names;
- check branch conditions;
- check path filters;
- check secret handling;
- check production deployment target.

If possible, validate by running the pipeline on a branch.

For Azure Repos PRs, rely on branch policy build validation.

Do not claim a pipeline passed unless the actual pipeline run passed.

## Deployment risk levels

Classify deployment changes.

Low risk:

```text
add comments
rename displayName only
add non-production validation
change docs-only build step
```

Medium risk:

```text
change build output path
change artifact name
change path filters
change Node or .NET version
change test commands
```

High risk:

```text
change production resource group
change production storage account
change service connection
change deployment condition
delete resources
modify Key Vault references
change authentication
change database migration deployment
```

High-risk changes require human approval before implementation.

## Destructive operations

Destructive commands require explicit approval.

Examples:

```bash
az group delete
az storage blob delete-batch
az resource delete
az keyvault secret delete
az sql db delete
```

Before running destructive commands, state:

```text
target
command
impact
rollback
confirmation needed
```

Do not use Caveman style for destructive operations.

## Managed identity and Key Vault

Prefer managed identity where applicable.

For applications:

- use managed identity to access Key Vault and Azure resources when supported;
- avoid connection strings with secrets;
- use Key Vault references for App Service settings when appropriate.

Pipeline service connections are for deployment automation, not application runtime identity.

Do not confuse deployment identity with application runtime identity.

## App Service deployments

When deploying App Services:

- identify target app service;
- identify slot strategy if used;
- preserve app settings unless intentionally changed;
- avoid overwriting production settings;
- use deployment slots when the project uses them;
- verify health after deployment.

Do not add secrets to YAML.

Do not change production slots without approval.

## Infrastructure pipelines

Infrastructure changes are high-risk.

Use an `infra` pipeline when provisioning or changing Azure resources.

Infra pipeline should:

- use explicit environment and resource names;
- show plan or preview where possible;
- require approval for production;
- avoid deleting resources by default;
- keep state backend secure when using IaC;
- publish useful outputs.

Do not mix unrelated application deployment and broad infrastructure changes in one hidden script.

## Release pipelines

Application or documentation release pipelines should:

- build once;
- publish artifact;
- deploy artifact;
- use environment checks;
- verify deployment result;
- not rebuild in production stage.

For static documentation, the release pipeline can both build and deploy generated static files, but deploy should consume the build artifact.

## Variable groups

Use variable groups for shared non-code configuration where appropriate.

Rules:

- keep naming clear;
- separate environments where needed;
- mark secrets as secret;
- avoid storing values that belong in source-controlled convention files;
- avoid hiding project identity in variable groups.

Project identity should stay in `.frontis/project.json`.

## Naming Azure resources

Do not invent Azure resource names.

Read:

```text
.frontis/project.json
.frontis/azure-conventions.yaml
```

before creating or changing Azure resources.

When deriving names, show the derived names before creating resources for non-trivial changes.

Example summary:

```text
Derived Azure names:
- resource group: ...
- storage account: ...
- app service: ...
- application insights: ...
```

Ask for approval before creating production resources.

## Pull request checklist

Before opening or finishing a PR with pipeline changes:

```text
[ ] AGENTS.md and .frontis files were followed
[ ] pipeline filename follows naming convention
[ ] YAML syntax and indentation reviewed
[ ] triggers and path filters reviewed
[ ] branch policy impact understood
[ ] service connection names checked
[ ] variable groups checked
[ ] no secrets added
[ ] artifact names and paths checked
[ ] production condition checked
[ ] environment approval behavior preserved
[ ] destructive commands documented and approved
[ ] validation run or limitation documented
```

## Recommended prompts

### Start pipeline change

```text
Use Superpowers workflow for this Azure DevOps pipeline change.

Read:
- AGENTS.md
- .frontis/project.json
- .frontis/project-context.yaml
- .frontis/naming-convention.yaml
- .frontis/azure-conventions.yaml
- existing pipeline YAML files

Summarize the current pipeline structure.
Then propose the smallest safe change and validation plan.
Do not add secrets to YAML.
Do not change production deployment behavior without approval.
```

### Debug failing pipeline

```text
Use Superpowers systematic debugging.

Do not guess.

First:
1. identify failing stage, job and task;
2. quote the relevant error message exactly;
3. identify the expected behavior;
4. inspect the YAML and scripts involved;
5. propose likely causes;
6. choose the smallest verification step.

Do not make trial-and-error YAML edits.
```

### Add static docs hosting

```text
Create an Azure DevOps multi-stage YAML pipeline for Frontis agent docs static hosting.

Use only production.
Use Build and Deploy_Prod stages.
Generate docs with scripts/generate-llms.mjs.
Publish the generated site as a pipeline artifact.
Deploy to Azure Storage static website hosting.
Use AzureCLI@2 with a service connection.
Do not add secrets to YAML.
```

### Review pipeline

```text
Review this Azure DevOps pipeline.

Check:
- Frontis naming conventions;
- triggers and path filters;
- branch policy assumptions;
- service connection usage;
- secrets;
- artifact flow;
- production deployment conditions;
- destructive commands;
- validation gaps.

Return findings by severity.
```

## Anti-patterns

Avoid:

- changing production deployment behavior as a side effect;
- using personal credentials;
- putting secrets in YAML;
- relying on YAML PR triggers for Azure Repos PR validation;
- rebuilding artifacts in deployment stages;
- using vague pipeline filenames;
- using `succeededOrFailed()` for production deployment;
- deleting `$web` without stating impact;
- changing service connection names without approval;
- hiding deployment logic in opaque scripts;
- copying YAML from another client without adapting conventions;
- claiming a pipeline passed without a real run;
- over-templating simple one-off pipelines;
- under-documenting high-risk Azure CLI commands.

## Reference links

Official documentation:

- AzureCLI@2 task: https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-cli-v2
- Pipeline artifacts: https://learn.microsoft.com/en-us/azure/devops/pipelines/artifacts/pipeline-artifacts
- YAML `pr` schema: https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/pr
- Azure Repos branch policies: https://learn.microsoft.com/en-us/azure/devops/repos/git/branch-policies
- Azure Storage static website hosting CLI: https://learn.microsoft.com/en-us/cli/azure/storage/blob/service-properties

## Completion summary format

At the end of an Azure DevOps task, use:

```text
Summary
- ...

Changed
- ...

Verified
- ...

Not verified
- ...

Deployment impact
- ...

Risks
- ...

Next steps
- ...
```

For production or destructive changes, use normal precise language and include exact caveats.
