Skip to content
Connect an agent

CI/CD

Diagrams in Azure Pipelines

How to check, redraw, and regenerate Plotwork diagrams in Azure Pipelines with the Plotwork extension for Azure DevOps and its task PlotworkDiagrams@1: fail a pull request whose pictures are stale or whose diagrams have drifted from their files, mark each problem on its line, and open a pull request with the new diagrams. It does what the GitHub Action does, for anyone who keeps Plotwork diagrams in a repository built by Azure Pipelines, in Azure Repos or on GitHub.

For
  • Azure Pipelines

TL;DR

QuestionAnswer
What is the extension?Plotwork diagrams for Azure DevOps. It adds the task PlotworkDiagrams@1 and a Plotwork service connection type. Until its public Marketplace listing opens, it is shared privately on request.
Is it released?Not yet as a stable release. Its first stable version, 0.1.0, brings the task PlotworkDiagrams@1; until then, run the command line in a script step, as CI usage shows.
What does the task run?The pinned release of the command line, npx -y @buun_group/plotwork@0.3.0, on the agent. It is the same check you run on your machine.
Which modes are there?check, validate, sync, render, drift, and generate, the commands of the GitHub Action.
Where do problems show?Each one is an issue on its file and line, in the run and on the pull request, and the run gets a Plotwork summary tab.
What does the agent need?Node 22 or later (add UseNode@1 first), git, and access to the npm registry. Microsoft-hosted agents have all three.
Does it need a key?No. Reporting drift, publishing, and an organisation's style rules need a key from a service connection or a secret variable, or no key at all once the pipeline is a trusted publisher.
Does it send my files anywhere?No. The command line reads the checkout and makes no request. reportDrift sends a run's metadata, and publish the generated diagram source, never a file.
How does it connect to my organisation?With a Plotwork service connection that holds an organisation API key, or with no key as a trusted publisher (section 6).
What if I cannot install extensions?Run the command line in a script step, as CI usage shows.

1. Install the extension

The extension is shared privately with each Azure DevOps organisation that asks for it until its public listing in the Visual Studio Marketplace opens. To get it now, ask us for access (opens in a new tab) with the name of your Azure DevOps organisation, or run the command line in a script step as CI usage shows.

Once the extension is shared with your organisation or listed, an organisation administrator installs it (Organization settings, Extensions, Shared or Browse marketplace, Plotwork diagrams, Install). Every project of the organisation can then use the task and the Plotwork service connection type.

2. Check every pull request

yaml
trigger: nonepr:  branches:    include: [main]
pool:  vmImage: ubuntu-latest
steps:  - task: UseNode@1    inputs:      version: 22.x  - task: PlotworkDiagrams@1    inputs:      mode: check      paths: |        README.md        docs/**/*.md      diagrams: |        diagrams/**/*.diagram

For a repository in Azure Repos, a pr trigger in the file does nothing: add the pipeline as a build validation policy of the branch (Project settings, Repositories, Policies, Branch policies) instead.

check runs plotwork sync --check on the Markdown and plotwork validate on each .diagram file. Each stale picture and each mistake is an issue on its line, which the run's summary and the pull request show, and the Plotwork tab of the run lists every file with how to fix it. To fix a stale picture, run npx -y @buun_group/plotwork@0.3.0 sync on the same files and commit the Markdown and the SVGs it writes.

InputWhat it changes
failOnerror (the default) fails on errors and stale pictures, warning also on warnings, never only reports.
convertMermaidtrue reads each Mermaid fence that converts to Plotwork as a diagram with no picture yet.
rulesOrgApplies an organisation's saved style rules instead of the rules of the settings file.

A rules section in the settings is checked too, with plotwork check.

3. Validate, redraw, and render

ModeWhat it does
validateReports the errors and warnings of the Markdown and .diagram files, and draws nothing.
syncRedraws the stale pictures of the Markdown in the workspace and commits nothing; with convertMermaid, replaces each Mermaid fence that converts first. files lists what it wrote, for a later step.
renderDraws each .diagram file light and dark into outDir: docs/flow.diagram becomes plotwork-svg/docs/flow.svg and flow.dark.svg.

4. Fail a pull request on drift

When diagrams are made from Terraform, OpenAPI, Compose and other files, name them in sources of plotwork.config.json, as Diagrams as code describes. Then run the task in drift mode:

yaml
steps:  - task: UseNode@1    inputs:      version: 22.x  - task: PlotworkDiagrams@1    inputs:      mode: drift

Each drifted diagram is an issue on its file, and the task publishes a test run in which each generated diagram is a test, failed when it drifted. On a pull request build in Azure Repos it writes one comment with the change table, the owners, and how to fix it, and edits that comment on later runs, to say so when everything is in step again (comment: auto, or always, or never). The comment needs Contribute to pull requests for the build service.

On a large repository, check only the sources whose files changed on the branch. The check needs the history up to the ref:

yaml
steps:  - checkout: self    fetchDepth: 0  - task: UseNode@1    inputs:      version: 22.x  - task: PlotworkDiagrams@1    inputs:      mode: drift      since: origin/main

5. Write the diagrams again in a pull request

This pipeline runs every night on main, makes every diagram again from its files, and opens or updates one pull request with the ones that drifted:

yaml
trigger: nonepr: none
schedules:  - cron: '0 2 * * *'    displayName: Regenerate diagrams    branches:      include: [main]
pool:  vmImage: ubuntu-latest
steps:  - checkout: self    fetchDepth: 0  - task: UseNode@1    inputs:      version: 22.x  - task: PlotworkDiagrams@1    name: plotwork    inputs:      mode: generate      onDrift: pr

Azure Pipelines skips a scheduled run when the branch has no new commit since the last successful scheduled run. Add always: true under the schedule to run every night anyway, for example so the drift dashboard always shows a recent run.

The task makes every diagram again in the workspace and uploads plotwork.patch as the pipeline artifact plotwork. Then onDrift says what to do with the drifted diagrams:

onDriftWhat happens
pr (default)One commit on top of the run's branch, pushed to prBranch (plotwork/diagrams), and one pull request into the run's branch, opened or updated. When nothing drifts any more, the task abandons its pull request and deletes the branch. It never replaces a branch whose last commit is not Plotwork's.
commitOne commit on the branch the run is on. A commit of its own (it carries Plotwork-Generated: true) never starts another.
failThe step fails, and the files and the patch stay.
patchThe files and the patch stay, and the step passes.

pr and commit push with the job's own access token, and only in Azure Repos. The task reads the token itself, so no env: mapping is needed. A pull request build runs on a merge commit, so pr and commit run on a push, a schedule, or a manual run. In a GitHub or Bitbucket repository the task cannot push: it says why, and the files and the patch stay for a later step of yours.

Build service permissions

Open Project settings, Repositories, the repository, Security, and choose the user <project> Build Service (<organisation>). Set these to Allow:

PermissionNeeded for
ContributeonDrift: pr and commit: pushing the commit
Create branchonDrift: pr: creating plotwork/diagrams the first time
Contribute to pull requestsonDrift: pr: opening, updating, and abandoning the pull request; comment in drift and generate

A branch policy on prBranch refuses the push too, so leave that branch free of policies. A pipeline whose job authorization scope is the whole collection runs as Project Collection Build Service (<organisation>) instead, so grant that user.

With publish: true, each source of the settings that has a publish section is published to Plotwork after the files are written, as Diagrams as code describes.

6. Connect to your organisation

check, validate, sync, render, and drift without reporting need no Plotwork account. Reporting drift, publishing, and an organisation's style rules (rulesOrg) do. The task takes the first credential it finds, in this order:

CredentialWhen to choose it
A Plotwork service connectionYou keep credentials in Azure DevOps service connections, or Azure DevOps Server runs your pipelines.
A secret variable, PLOTWORK_API_KEYYou keep secrets in a variable group or a key vault.
No key: a trusted publisherYou want no secret anywhere. Pro and Enterprise, on Azure DevOps Services.

With a service connection and an organisation key

  1. In the Plotwork portal, open the organisation, choose Keys & CI, and make a key that can do diagrams ("Diagrams" or "Diagrams and the audit log"). A key made "Audit log only" cannot report or publish, and a personal key is refused.
  2. In Azure DevOps, open Project settings, Service connections, New service connection, and choose Plotwork.
  3. Leave the API address as it is, paste the key into Organisation API key, name the connection (such as plotwork), and save. Grant it to the pipelines that use it, or approve it on the first run.
  4. Set plotworkConnection to the connection's name:
yaml
  - task: PlotworkDiagrams@1    inputs:      mode: drift      reportDrift: true      plotworkConnection: plotwork      organization: acme

To keep the key in a secret variable instead, map it into the step, because Azure Pipelines never puts a secret in the environment by itself:

yaml
  - task: PlotworkDiagrams@1    inputs:      mode: drift      reportDrift: true    env:      PLOTWORK_API_KEY: $(PLOTWORK_API_KEY)

With no key, as a trusted publisher

  1. In Azure DevOps, open Pipelines and choose the pipeline. Its address ends in ?definitionId=42: that number is the pipeline id. Renaming the pipeline keeps it.
  2. In the Plotwork portal, open the organisation, choose Keys & CI, then CI/CD. Under Trusted publishers, choose Add trusted publisher, then Azure Pipelines.
  3. Fill in the Azure DevOps organisation (acme in https://dev.azure.com/acme), the project, and the pipeline id. With the Azure DevOps connector connected, choose each from a list instead, and Branch suggests the pipeline's default branch.
  4. Optionally name a branch (main, or a ref pattern such as refs/heads/release/*). Leave it blank for any branch. A pull request build's ref is refs/pull/<n>/merge, so an entry limited to main refuses pull request runs.
  5. Choose what its runs can do: Publish diagrams to the library, Report drift to the Drift tab, or both. Give it a name you will recognise in the usage and the audit log, and save.
  6. Run the pipeline with no service connection and no PLOTWORK_API_KEY. Choose Workflow beside the entry to copy a pipeline made for it.

The task asks Azure Pipelines for the job's OpenID Connect token with the job's own access token, which needs no service connection, and exchanges it for a Plotwork token that lasts 15 minutes. The first run that passes every other check pins the ids of the Azure DevOps organisation and project, and the entry shows a pin until then. The output signedInAs names the organisation and the trusted publisher. Trusted publishers says what is checked.

When more than one Plotwork organisation trusts the pipeline, set organization to the slug of the one to sign in to.

7. Report drift to the dashboard

An organisation on a plan with the drift dashboard sees every repository's drift in one place. This pipeline checks every pull request and every push to main, and sends each run to the dashboard:

yaml
trigger:  branches:    include: [main]pr:  branches:    include: [main]
pool:  vmImage: ubuntu-latest
steps:  - checkout: self  - task: UseNode@1    inputs:      version: 22.x  - task: PlotworkDiagrams@1    inputs:      mode: drift      reportDrift: true

It signs in as a trusted publisher; add plotworkConnection to use a key instead. To report from the nightly pipeline of section 5, add reportDrift: true, and publish: true to publish too, to its generate step.

What reportDrift sendsWhat is never sent
The repository, the branch, the commit, the build reason, the run and pull request linksA diagram, a file, or a name of what changed
Per source: its name, out path, status, counts of what was added, removed, and changed, errors, and CODEOWNERS ownersThe key or the token, which the task registers as secrets so the log masks them

A refusal, such as a plan with no dashboard, is a note and fails nothing. The dashboard names a repository as owner/name: a GitHub repository as it is, and an Azure Repos repository as project/repository. A project whose name has a space is not sent yet, and the note says so.

8. Inputs

InputDefaultMeaning
modecheckcheck, validate, sync, render, generate, or drift.
paths**/*.mdcheck, validate, sync: the Markdown files, one glob, file, or folder per line; a line that starts with ! leaves out a match.
diagrams**/*.diagramcheck, validate, render: the .diagram files.
outDirplotwork-svgrender: the folder the SVGs go to.
failOnerrorerror, warning, or never.
convertMermaidfalsecheck and sync: treat each Mermaid fence that converts as a diagram.
sinceemptydrift and generate: a git ref; only the sources whose files changed since it run.
publishTestResultstruedrift: publish the JUnit report as a test run.
onDriftprgenerate: pr, commit, fail, or patch.
prBranchplotwork/diagramsgenerate: the branch the task owns.
prTitledocs: regenerate diagrams from their sourcesgenerate: the pull request's title.
commitMessagedocs: regenerate diagrams from their sourcesgenerate: the commit message.
publishfalsegenerate: publish each source that has a publish section.
commentautodrift and generate: the pull request comment.
rulesOrgemptycheck and validate: an organisation whose saved style rules apply.
reportDriftfalsedrift and generate: send the run's metadata to the drift dashboard.
plotworkConnectionemptyThe Plotwork service connection with the organisation key. Empty reads PLOTWORK_API_KEY, then signs in as a trusted publisher.
organizationemptyThe organisation to report to and sign in to. Empty is rulesOrg, then brand.organization of the settings, then the key's.
configemptyThe settings file. Empty finds plotwork.config.json, then .plotwork/config.json.
workingDirectory.The folder to run in, from the sources folder.
summarytrueAdd the Plotwork tab to the run's summary.
apiUrlhttps://api.plotwork.devThe Plotwork API.
cliVersionthe release the extension was tested withThe release of @buun_group/plotwork the task runs.
Output variableMeaning
stale, errors, warningsThe counts the run found.
filesA JSON array of paths from the repository root: what to sync (check), what has an error (validate), or what the run wrote (sync, render, generate).
changesdrift and generate: one entry per drifted diagram, with a sentence naming its changes.
pullRequestgenerate: the pull request it opened or updated.
publishedgenerate with publish: what was published, with links.
commentUrlThe pull request comment it wrote or edited.
driftUrlreportDrift: the drift dashboard the run was sent to.
signedInAsThe organisation and trusted publisher a run with no key signed in as.
junitFiledrift: the JUnit report.
patchFile, changedFilesgenerate: the patch, and a JSON array of the files the run changed.

Give the task a name, such as plotwork, and a later step reads an output as $(plotwork.errors).

9. Versions and agents

The task is PlotworkDiagrams@1; its major version moves only with a change that breaks a pipeline. Each release of the extension pins a stable release of the command line in cliVersion, so an update of the extension never changes your pictures by itself. To try a newer command line first, set cliVersion, run sync with that release on your machine, and commit the pictures in the same change.

A self-hosted agent needs Node 22 or later with npx on its path, git, and the npm registry or a mirror of it (set NPM_CONFIG_REGISTRY on the agent). Signing in as a trusted publisher needs Azure DevOps Services; Azure DevOps Server sets no System.OidcRequestUri.

10. Troubleshooting

The run saysWhy, and what to do
Unable to locate executable file: 'npx'The agent has no Node. Add UseNode@1 with version: 22.x before the task, or install Node 22 or later on a self-hosted agent.
The agent did not set System.OidcRequestUriAzure DevOps Server gives no ID token. Use a Plotwork service connection or PLOTWORK_API_KEY instead of a trusted publisher.
reportDrift is on, but there is no Plotwork keyNo service connection, no PLOTWORK_API_KEY in the step's environment, and no trusted publisher sign-in. A secret variable reaches the task only through env:; check its name, and that the pipeline may read its variable group.
oidc.no_trusted_publisherNo entry trusts this run. Check the entry's organisation, project, pipeline id and branch. The audit log's orgs.trusted_publisher.refused event says which did not match.
oidc.organization_requiredMore than one Plotwork organisation trusts this pipeline. Set organization to the one to sign in to.
Azure Repos refused it: grant the build serviceThe build service lacks Contribute, Create branch, or Contribute to pull requests, or a branch policy covers prBranch. See Build service permissions.
The repository is on GitHub, not Azure ReposonDrift: pr and commit, and the pull request comment, need Azure Repos. The new diagrams stay in the workspace and the patch is the artifact plotwork.
onDrift pr needs a branch, and a pull request build runs on a merge commitRun generate with pr or commit on a push, a schedule, or a manual run, and drift on pull requests.
The pipeline never runs on a pull requestIn Azure Repos a pr trigger in the file does nothing. Add the pipeline as a build validation policy of the branch (Project settings, Repositories, Policies, Branch policies).
No pull request commentThe build service lacks Contribute to pull requests, or the run is not a pull request build. The run's Plotwork tab has the report either way.
auth.key_network_refusedThe organisation accepts its keys only from some networks, and Microsoft-hosted agents are not in them. Run on a self-hosted agent in an allowed network.

See also

PageWhy
CI usageThe same check as a script step, on Azure Pipelines and every other CI
Diagrams as codeSources, overrides, publishing, and the drift check
Keep diagrams in sync in CIThe GitHub Action
Trusted publishersReporting drift and publishing with no key
Diagrams in GitLab CI/CDThe same check as a GitLab component
Repository settingsDefaults for every header, and rules a pull request must pass
The drift dashboardWhere a reported run shows
Enterprise rolloutThe check across many repositories