Skip to content

Product1 publisher3 min readPublished

A run parameter and a build tag replace two duplicated Azure DevOps definitions

A devops.com write-up cuts a five-definition Azure DevOps delivery chain to three by passing the module name at queue time. Per-module deployment control now sits in stage conditions the interface does not display in full.

The Product Desk · Product desk

Illustration accompanying A run parameter and a build tag replace two duplicated Azure DevOps definitions

What happened

  • Two modules in one repository ran near-identical Azure DevOps build and release definitions, and onboarding a third would have required another matching build and release pair.
  • The delivery chain used five definitions, being one change decider, two builds and two releases, a count that excludes package and pull request validation.
  • The decider now holds the only continuous integration trigger, checks changed paths, and queues one generic build with the module and service passed as run parameters.
  • Azure DevOps rejected the tag module:module-b because of the colon in the request path, so the generic build tags each run with a hyphenated value such as module-b.
  • A single release definition carries one deployment stage per onboarded module, and the build's tag decides which of those stages runs.

Compiled by The Product DeskSomething wrong?How this is made

Why it matters

  • constraint A change to the shared build or deployment steps is now one edit. Under the old layout the same change meant matching edits in two build definitions and two release definitions, with nothing to catch a missed one.
  • exposure Whoever is on call inherits a chain where a correctly skipped stage and a broken condition produce the same green summary, so proving the right module deployed means opening the stored condition.
  • cost The next person to clone one of these definitions can silently drop the queue-time flag, and the breakage surfaces at handoff rather than at edit time.
  • decision Teams with two modules and a third coming choose now between cloning a fourth and fifth definition or moving the module name into a parameter before onboarding.

The two tag filters available here do different jobs, and picking the wrong one changes what the audit trail looks like afterwards. A `module-a` filter on the release definition's continuous deployment trigger stops a `module-b` build from creating a release at all: the build stays green and the chain ends with no release record [8]. The author left the trigger filter empty and put `module-a` and `module-b` conditions on the stages instead, so every qualifying completion of the generic build on the configured branch creates one release and the tag picks the matching stage [9].

The stage that does not match skips with `Artifact conditions not met`, which is the expected result [10]. The same status appears when the condition itself is malformed, so the run summary alone does not prove the routing worked [11]. There is a second ambiguity in the interface: it displays a short branch name while the stored condition holds the full `refs/heads/...` value. The author checked the stored condition directly [12].

The parameter handoff has its own failure mode. The Build REST API takes the values as a JSON-encoded string [19], and the variables that receive them have to keep Azure DevOps's Settable at queue time option, stored as `allowOverride: true`. A cloned definition can lose that flag, and the decider then identifies the right change and fails when it hands the values to the child build [13].

An earlier design combined the decider and the build, and it triggered releases from dispatcher runs that contained no artifact. After the split, every successful generic build produced an artifact for the release [14].

Each child run is pinned to the branch and commit the decider inspected, so a later push cannot change what that child builds [15]. The decider compares the current commit with its first parent because the protected branch takes changes through merges; a repository that accepts multi-commit direct pushes needs the before and after commit IDs from the build event instead [16].

Two modules cost five definitions before the change and three after [2][3]. A third module would have taken the old layout to seven, being one decider, three builds and three releases, while the consolidated chain stays at three definitions and gains a stage [20].

The test for whether two definitions can share one is whether the difference between them is values or tasks. These two repeated the same image and chart tasks with different module values, and the same deployment tasks with different variable groups, all of which the decider can pass in [17]. Definitions that run genuinely different tasks do not collapse this way, and a parameter that switches a task on and off puts the duplication back inside a single file. The second test is whether the routing decision can be read where it is stored, because a green run with one skipped stage looks identical whether the condition is correct or broken [11]. The account covers one repository and one trial, a Module B change in a nonproduction environment [18]. It does not report how much maintenance time the change saved.

What to watch

  • Whether a third module onboards as a deployment stage alone, with no new build or release definition.
  • Whether the consolidated chain behaves the same on a production release as it did on the nonproduction Module B run.
  • Whether Azure DevOps surfaces queue-time settability during cloning, which would remove the allowOverride failure mode.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories