How to properly setup a multi-environment release pipeline in Azure YAML pipelines? We have just migrated from TFS 2018 to Azure DevOps Services and we're having a hard time trying to figure out the new Azure YAML pipelines.

We want to be able to:


Run a build/test pipeline when a PR is pushed to develop.
We've already accomplished this by creating a dedicated YAML pipeline that only builds and runs the tests (no publish artifact or anything). This pipeline is triggered by a "Build Validation" branch policy on the develop branch.
Run the full CI/CD pipeline that will build, test and deploy to DEV and QA environments when a commit is pushed to the develop branch.
We've already accomplished this by using a multi-stage pipeline triggered by any change in the branch. Although we're not entirely happy about the duplicated tasks here (this has exactly the same tasks to build and test the project as the above pipeline).
Deploy to our production-staging environment when a commit is pushed to the master branch.
We're stuck here. We're not sure how to do this without creating yet another new pipeline that will do exactly the same as the previous one, but targeting our staging environment. We want to "build once, deploy many". This was easy using TFS build and release pipelines (I guess this is what Azure DevOps calls "Classic Pipelines", right?)


How we can accomplish this workflow without needing to create several similar YAML pipelines, but also following the "build once, deploy many" pattern?
Assuming that deploy to DEV and QA steps look a lot like your deploy to production-staging environments, put the code that does that work into a yaml template and take advantage of parameters to allow you to pass in differing environment settings for DEV, QA, and Staging-Production. 

Then in one pipeline create a stage for each environment.  The stage should have a job which references your template.  You should use conditions on each stage to ensure your code goes to the environment when you want it.  Here's a pseudocode example:

stages:
- stage: MyDevStage #No condition so this stage always runs
  jobs:
    - template: '../MyTemplate.Yaml
      parameters:
        ServerName: MyDevServer
- stage: MyProdStage
  condition: and(
    succeeded() # if my dev deploy doesn't go well don't deploy to PRD
    , eq(variables['Build.SourceBranch'], 'refs/heads/master') # confirm this is a commit to master
    )
  jobs:
    - template: '../MyTemplate.yaml'
      parameters:
        ServerName: MyProdServer


At the top of your pipeline yaml file you'll need to specify a trigger which may look something like this:

trigger:
  batch: true
  branches:
    include:
    - master