Converting to Modern YAML Pipeline: Application Lifecycle Management in Azure DevOps for Power Platform
By Reshmee Auckloo
Converting to Modern YAML Pipeline: Application Lifecycle Management in Azure DevOps for Power Platform
There are loads of posts explaining Application Lifecycle Management for the Power Platform using Azure DevOps most using the graphical classical pipeline. The latest post I read on this is Application Lifecycle Management for the Power Platform using Azure DevOps by Luise Freese which is brilliant.
Please read article covering difference between YAML and Classic UI to explain why you may want the modern YAML pipeline. The main reason for me is to have the CI/CD pipeline checked in the repository as code.
The current post will focus on converting the classic pipeline into YAML separated into two categories : build and release pipeline
Build pipeline
You might have a graphical classic pipeline as below for the build or CI (continuous integration). There is an option to view YAML.
You can copy the generated YAML.
Follow the steps below to get the build pipeline as YAML stored in your repository
Create a yaml file with extension .yml in your chosen repository
Paste the copied yaml code and commit to your repository Edit the YAML to remove “microsoft-IsvExpTools.PowerPlatform-BuildTools.”
Click on Pipelines from the left navigation and click on “New Pipeline”
Clik on new Pipeline and select “Azure Repos Git” from option “Where is your code?”
Select the repository where the YAML is
Pick the option Existing Azure Pipelines YAML file, pick the branch and Path where the build yaml file is
Modify the yaml file as appropriate adding any trigger actions ensuring right formatting and indentation is used
trigger:
branches:
include:
- main
- develop
pool:
name: Azure Pipelines
#Your build pipeline references a secret variable named ‘TEST_PAT’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab, and then select the option to make it secret. See https://go.microsoft.com/fwlink/?linkid=865972
variables:
- name: SolutionName
value: TEST
- name: SolutionConnectorName
value: domainnameBasicDisplay
steps:
- task: PowerPlatformToolInstaller@2
displayName: 'Power Platform Tool Installer '
- task: PowerPlatformExportSolution@2
displayName: 'Power Platform Export TEST Unmanaged Solution'
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-d-connection'
Environment: 'https://domainname-dev.crm11.dynamics.com'
SolutionName: $(SolutionName)
SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionName).zip'
- task: PowerPlatformExportSolution@2
displayName: 'Power Platform Export CustomConnector Unmanaged Solution'
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-d-connection'
Environment: 'https://domainname-dev.crm11.dynamics.com'
SolutionName: $(SolutionConnectorName)
SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionConnectorName).zip'
- task: PowerPlatformUnpackSolution@2
displayName: 'Power Platform Unpack TEST Unmanaged Solution '
inputs:
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionName).zip'
SolutionTargetFolder: '$(Build.SourcesDirectory)/PowerPlatformSolution/$(SolutionName)'
- task: PowerPlatformUnpackSolution@2
displayName: 'Power Platform Unpack CustomConnector Unmanaged Solution '
inputs:
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionConnectorName).zip'
SolutionTargetFolder: '$(Build.SourcesDirectory)/PowerPlatformSolution/$(SolutionConnectorName)'
- task: PowerPlatformExportSolution@2
displayName: 'Power Platform Export TEST Managed Solution'
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-d-connection'
Environment: 'https://domainname-dev.crm11.dynamics.com'
SolutionName: $(SolutionName)
SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionName).zip'
Managed: true
- task: PowerPlatformExportSolution@2
displayName: 'Power Platform Export CustomConnector Managed Solution'
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-d-connection'
Environment: 'https://domainname-dev.crm11.dynamics.com'
SolutionName: $(SolutionConnectorName)
SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionConnectorName).zip'
Managed: true
- task: PowerPlatformUnpackSolution@2
displayName: 'Power Platform Unpack TEST Managed Solution '
inputs:
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionName).zip'
SolutionTargetFolder: '$(Build.SourcesDirectory)/PowerPlatformSolution/$(SolutionName)_Managed'
SolutionType: Managed
- task: PowerPlatformUnpackSolution@2
displayName: 'Power Platform Unpack CustomConnector Managed Solution '
inputs:
SolutionInputFile: '$(Build.ArtifactStagingDirectory)/PowerPlatformSolution/$(SolutionConnectorName).zip'
SolutionTargetFolder: '$(Build.SourcesDirectory)/PowerPlatformSolution/$(SolutionConnectorName)_Managed'
SolutionType: Managed
- script: |
echo Commit Solution
git config user.email srv_d_TEST@domainname.co.uk
git config user.name "Service Account (Dev)"
git switch -c feature/-listview
git pull
git add --all
git commit -m"Checked in by Power Platform Release"
echo Push Solution to Repo
git push --all https://$(TEST_PAT)@dev.azure.com/domainname-it/M365/_git/$(SolutionName)
displayName: 'Check in files'
Let’s break down the different sections and their meanings:
branches: This section specifies the branches that will trigger the build pipeline. In this case, the build will be triggered when changes are made to the main and develop branches.
pool: The pool section defines the agent pool to use for the build. In this case, the build will use the “Azure Pipelines” agent pool.
variables: This section defines the variables used in the pipeline. Two variables are defined: SolutionName with a value of “TEST” and SolutionConnectorName with a value of “domainnameBasicDisplay”. These variables can be referenced later in the pipeline using the syntax $(VariableName).
steps: The steps section contains the sequence of tasks to be executed in the pipeline.
The first task is PowerPlatformToolInstaller@2, which installs the Power Platform tools
The next task is PowerPlatformExportSolution@2, which exports an unmanaged solution from the Power Platform. It exports a solution named $(SolutionName) using the specified authenticationType which is PowerPlatformSPN, and Environment. In the above example, there are two tasks for PowerPlatformExportSolution@2 for two different solution
PowerPlatformUnpackSolution@2 tasks, which unpack the previously exported solutions. They extract the solution files to specific target folders based on the solution names.
There are two more PowerPlatformExportSolution@2 tasks, similar to the previous ones, but these export managed solutions instead of unmanaged solutions.
There are two tasks are PowerPlatformUnpackSolution@2 tasks that unpack the managed solutions to target folders with a “_Managed” suffix in their names.
The final task is a script task that performs a series of Git commands. It configures the user email and name, creates a new branch, pulls the latest changes, adds all the files, commits the changes with a specific message, and pushes the changes to a remote repository. The repository URL is constructed using the TEST_PAT secret variable definied as a variable. The PAT is the Azure DevOps personal access token which can be generated from your account settings.
To use this YAML, you would need to create or edit the build pipeline in Azure DevOps, define the necessary secret variable, and configure the required resources and connections accordingly.
8.Create a variable for PAT with the same name referenced in the YAML script
10.Review Pipeline and click on Run
Grant permission if prompted
If all successful
Release Pipeline
In Classic pipeline, there is no option to view full YAML
In the example above there are 4 tasks within each stage.
Unfortunately the view YAML is only available on each task and not on the entire release pipeline.
You can go through each task to copy the generated YAML editing as appropriately e.g. Edit the generated YAML to remove “microsoft-IsvExpTools.PowerPlatform-BuildTools.”.
Repeat the above steps 1 - 6 from the Build Pipeline section to create the new release pipeline. Amend the YAML based on your scenerio.
trigger:
- none
pool:
name: Azure Pipelines
variables:
- name: SolutionName
value: TEST
stages:
- stage: deploytest
displayName: Deploy to Test
jobs:
- deployment: deploy_to_Test
environment: ST Intranet
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: PowerPlatformToolInstaller@2
inputs:
DefaultVersion: true
- task: PowerPlatformPackSolution@2
inputs:
SolutionSourceFolder: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed'
SolutionOutputFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed.zip'
SolutionType: Managed
- task: PowerPlatformImportSolution@2
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-t-connection'
Environment: 'https://domainname-st.crm11.dynamics.com'
SolutionInputFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed.zip'
UseDeploymentSettingsFile: true
DeploymentSettingsFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/settings-test.json'
ConvertToManaged: true
PublishCustomizationChanges: true
MaxAsyncWaitTime: "60"
- task: PowerPlatformPublishCustomizations@2
inputs:
authenticationType: 'PowerPlatformSPN'
PowerPlatformSPN: 'powerplatform-t-connection'
- stage: deployUAT
displayName: Deploy to UAT
jobs:
- deployment: deploy_to_Uat
environment: UAT Intranet
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: PowerPlatformToolInstaller@2
inputs:
DefaultVersion: true
- task: PowerPlatformPackSolution@2
inputs:
SolutionSourceFolder: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed'
SolutionOutputFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed.zip'
SolutionType: Managed
- task: PowerPlatformImportSolution@2
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-u-connection'
Environment: 'https://domainname-uat.crm11.dynamics.com'
SolutionInputFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed.zip'
UseDeploymentSettingsFile: true
DeploymentSettingsFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/settings-uat.json'
ConvertToManaged: true
PublishCustomizationChanges: true
MaxAsyncWaitTime: "60"
- task: PowerPlatformPublishCustomizations@2
inputs:
authenticationType: 'PowerPlatformSPN'
PowerPlatformSPN: 'powerplatform-u-connection'
- stage: deployPROD
displayName: Deploy to PROD
jobs:
- deployment: deploy_to_Prod
environment: Intranet
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: PowerPlatformToolInstaller@2
inputs:
DefaultVersion: true
- task: PowerPlatformPackSolution@2
inputs:
SolutionSourceFolder: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed'
SolutionOutputFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed.zip'
SolutionType: Managed
- task: PowerPlatformImportSolution@2
inputs:
authenticationType: PowerPlatformSPN
PowerPlatformSPN: 'powerplatform-p-connection'
Environment: 'https://org7b3c6721.crm11.dynamics.com'
SolutionInputFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/$(SolutionName)_Managed.zip'
UseDeploymentSettingsFile: true
DeploymentSettingsFile: '$(System.DefaultWorkingDirectory)/PowerPlatformSolution/settings-prod.json'
ConvertToManaged: true
PublishCustomizationChanges: true
MaxAsyncWaitTime: "60"
- task: PowerPlatformPublishCustomizations@2
inputs:
authenticationType: 'PowerPlatformSPN'
PowerPlatformSPN: 'powerplatform-p-connection'
The provided YAML represents a deployment pipeline with multiple stages (deploytest, deployUAT, deployPROD) that deploy a Power Platform solution to different environments (ST Intranet, UAT Intranet, and Intranet). Let’s break down the structure and flow of the YAML code:
trigger: This section defines the conditions that trigger the pipeline. In this case, the trigger is set to “none,” meaning the pipeline won’t be automatically triggered by source code changes. It will need to be manually triggered or scheduled.
pool: The pool section specifies the agent pool to use for running the pipeline. In this case, the “Azure Pipelines” agent pool is selected.
variables: This section defines the pipeline variables. The only variable defined here is SolutionName, with a value of “TEST”. This variable can be referenced later in the pipeline using the syntax $(SolutionName).
stages: The stages section represents the different deployment stages in the pipeline : deploytest, deployUAT, deployPROD
For each of the stage there is a single job for deploying to the respective environment, i.e deployment with strategy set to runOnce to deploy the solution only once.
The deploy : The deploy section specifies the steps to perform for the deployment.
- checkout: The checkout step checks out the source code from the repository
- PowerPlatformToolInstaller@2: The PowerPlatformToolInstaller@2 task installs the Power Platform tools.
- PowerPlatformPackSolution@2: The PowerPlatformPackSolution@2 task packs the solution files into a zip file. It specifies the source folder, output file location, and that the solution is of type “Managed”.
- PowerPlatformImportSolution@2: The PowerPlatformImportSolution@2 task imports the solution into the “ST Intranet” environment. It uses the specified authentication type, connection details, solution input file, deployment settings file, and other settings.
- PowerPlatformPublishCustomizations@2: The PowerPlatformPublishCustomizations@2 task publishes the customizations of the solution in the environment.
deployUAT: This stage is named “Deploy to UAT” and contains a single job for deploying to the “UAT Intranet” environment. The structure and tasks within this stage are similar to the previous stage, with environment-specific settings.
deployPROD: This stage is named “Deploy to PROD” and contains a single job for deploying to the “Intranet” environment. It follows the same structure as the previous stages but with environment-specific settings for the PROD environment.
The pipeline follows a similar pattern for each stage, where it checks out the source code, installs the Power Platform tools, packs the solution, imports the solution into the respective environment, publishes the customizations, and performs any necessary configuration or conversion steps.
Please note that this YAML code describes the pipeline’s structure and tasks, but additional configurations and resources such as service connections, variable groups, and agent pool settings may be required for a fully functional deployment pipeline in Azure Pipelines.
Define environment as appropriate
Approvals on each environment
You can just the whole release pipeline to run all stages sequentially
Run by specifying stages
If all successful, there will be a green tick identifying all went fine
Well done for tranforming your powerplatform application life cycle as code into your repository.
Classic UI versus YAML
Classic UI 💗 No DSL (domain specific language) to learn, which enables super fast on-boarding, especially for those that come from a SysAdmin background rather than a Developer background 💗 Very easy to make quick changes, which encourages experimentation 😐 Contrary to popular belief, it does have versioning, and you can revert to a previous one easily 👎 It’s being deprecated. This will become lower and lower in Microsoft’s priority list when it comes to new features
YAML 💗 It is code, and managed as a source file, so it will go through a standard code review / pull request process 💗 Because it is in the repo, when you need to revert the source to an early commit, the pipeline will be reverted together as well 💗 Like all text files, it is easy to manipulate and change multiple values in one go. If needed it can even be generated from a script 💗 Comparing changes is much easier compared to the Classic UI versioning, which means it’s easier to identify root cause if build breaks 💗 Encourages collaboration — it’s much easier to code snippets through Slack or whatever than cropped screenshots 💗 Supports Container Jobs, which is quite important to some teams 😐 Azure DevOps does have assistant to help building a YAML file, so it’s not as daunting as it first looks for inexperienced users 👎 Not as mature as the Classic UI — some features are still on the roadmap