YAML templates - is there a way to control where deployments go without flagging?

I am new to YAML deployment templates in Azure DevOps. Sorry if this question is very basic.

I just stared working with a team that has the YAML like the snippet shown below. The team changes the isDeployStageName: values such as isDeploySandbox or isDeployProduction from false to true to indicate which environment to deploy to. It results in a lot of modification of the azure-pipelines.yml being changed with pretty much every build and it feels error prone (if someone forgets to set a value back to false for example).

My question:
Is there a better way to do this with YAML deployment templates? I’m wondering if there’s a way to control where deployments go without the flags technique.

  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'
  isBuild: true
  isDeploySandbox: false
  isDeployDevelopment: true
  isDeployTest: true
  isDeployStage: true
  isDeployProduction: false
  isPublish: true
  isSonar: false
  #ETC ...
- stage: BUILD
  displayName: BUILD
    vmImage: 'windows-latest'
  - template: templates/build/REDACTED.yml

- stage: SANDBOX
  displayName: SANDBOX
  condition: and(succeeded(), eq(variables.isDeploySandbox, true), eq(variables.isFeature, true))
  dependsOn: BUILD```

Maybe this https://github.com/keboola/processor-strip-null/blob/main/azure-pipelines.yml#L16 could be an inspiration? The test stage runs on every push, the deploy stage runs only when a tag is pushed. You can further refine this i.e. a stage XY runs when tag XY-whatever is pushed

Build once & deploy many, then you can get rid of most of those booleans. You can set a single variable that determines how far a build goes into the pipeline, and set a conditional on each stage to check if the previous stage succeeded and if the stage sufficiently meets that flag criteria

, so that technique uses tags rather than committed variables?

, that’s a good idea … use a single variable rather than multiple flags.

Yes - and like says - the tag controls how far the pipeline goes

I have a stage called CI, a deploy “setup” stage, then a stage for each environment’s deployment.

    displayName: Release Setup
    condition: and (succeeded(), eq(variables['DoNotPublish'],false), or ( eq(variables['Build.SourceBranchName'], 'main'), eq(variables['PublishFeature'], true) ) )
    dependsOn: CI
- stage: CD_DV
    - name: deploymentDateTime
    displayName: 'DV - Continuous Deployment'
    dependsOn: CD_Setup```

This one is simple, but somewhere else (can’t remember where) we have it like:
• anything - tests run
• test-anything - tests run & deployed to testing
• test-1.2.3 - test run & deployed to testing & staging
• 1.2.3 - test run & deployed to testing & staging & production

I do like that idea, it feels cleaner than committing the a flag over and over again.

My situation is also simple and is resolved with one boolean (either it builds or it builds & deploys). However, it could be changed to a different indicator. We have other checks around the production stage to not allow feature branches enter that stage regardless of what the user enters into the pipeline parameters.

That’s really interesting, do you do the checks for feature branches within YAML?

We use environments with approvals as well as branch conditions. Prod deployment stages only trigger on master branch and only after manual approval. Dev deployment stages also trigger on pull requests and can optionally have manual approval on the environment if necessary. No need for tags, bools or anything.

We do have approvals on the environments, but I’ve not looked at the branch conditions before. Is that a branching policy?

We use both branching policies on the environments as well as branch conditions on the stages themselves