Luise Freese

How to get from Dev? Ooops! 🤭 to proper (Azure) DevOps for Power Platform


Watch out - this is part 2 of a series around very good practices in Power Platform


Why would we care?

There are Power Platform solutions, that are purely meant to be for personal productivity. They only impact the user who built them and it’s ok (for me) to have them as an unmanaged solution in the default environment.

For solutions though, that impact more people or that even drive mission-critical processes, it’s a good idea to

  • develop them in a different environment than they are used in
  • log the changes into a source control system
  • publish the solution as a managed solution for users
  • automate this process

If we don’t do that, it leads to solutions being subjects to change for end-users all the time - so that they can’t rely on a feature to be tested and working as planned. It also means that developers can’t revert/undo changes and it eventually leads to lots of manual and chaotic ad-hoc work, which puts unneccesary stress on admins and developers:

  • We operate at open heart when developing solutions while users keep using them
  • We can’t roll back to previous versions
  • Code only exists as opaque zip files in an environment

How do we care?

Basics

Let’s first cover some basics so that everyone is on the same page: All Power Platform components are packaged and distributed across environments using solutions There are 2 types of solutions, unmanaged and managed solutions. Unmanaged solutions are a collection of references to components, there are no restrictions on what can be added, removed, or modified, and they are recommended only during development of the solution. Managed solution’s components can’t be added or removed or modified. They are recommended when a solution is not actively being customized. Source control should be your source of truth for storing and collaborating on your components.

How do we do this? There are several options:

  • Power Platform Pipelines (built-in, super easy to use, super limited as well.)
  • GitHub actions
  • Power Platform Build Tools for Azure DevOps

I will explain how this works for Azure DevOps, as I use this in nearly all my projects at my customers.

What is Azure DevOps Build Tools for Power Platform

  • Azure DevOps Pipelines automatically build code projects and ship them to any target
  • They support both GitHub and Azure repos (but there is no explicit value for me to store the codein GitHub when I use Azure DevOps)
  • Azure DevOps Build Tools for Power Platform are a collection of build and release tasks related to Power Platform

Prerequisites / little bit of prep work

As we do not want to develop the solution in the same environment as we want users to use, we’d need at least 2 environments.

I’m a big fan of a 4 environment setup

Overview of environments

  • DEV - only for development purposes, only developers and maintainers have access to it
  • BUILD - only used to store the exported unmanaged solution and export it as managed solution (this separates the solution from the solution that is currently and ongoing developed in DEV). I want a separate (and new!) environment to build the solutions so that no Gremlins (coined by J. Rapp) could interfere (connection references, environment variables, etc.)
  • TEST - only for User Acceptance Testing - A subset of endusers can test functionality of this release.
  • PROD - production use of apps in the organization

💡 Obviously, if there is a requirement for a new environment in the organization, it’s a good idea to then have this set of environments.

Process of building and releasing versions

Every new feature, all bug fixes are implemented in DEV. Once the release passed the technical tests in DEV, it is build in BUILD and then finally released to TEST, where end users can try out and test the release. If they approve it, the release gets published to PROD. If they don’t approve it, development starts again in DEV and bug fixes/features get implemented in the next sprint.

pipeline overview

Setup instructions

You will obviously need to have an Azure DevOps organization, and within it a new project. In this project

  • Create a repository to hold your code
  • Optional, but a good idea: Clone this repo so you can work locally in VS Code
  • Optional, but also a good idea: invite co-workers to the project
  • Open Project Settings (lower left corner) –> Repositories –> Security and select Contribute permissions for both (!)
    • Project Collection Service Accounts permissions
    • Build Service permissions

Create environments

As a first step, we need to ensure that we have all environments in place:

  • Open aka.ms/ppac
  • Create environments for DEV, BUILD, TEST, PROD - make sure all of them have a Dataverse database

Authentication

To authenticate in Azure DevOps against actions in Dataverse, you will need to

Let’s get our hands dirty.

Create an app registration

You can either create the app registration with a single line command in CLI for Microsoft 365. I posted a detailed blog post here.

m365 aad app add --name 'myApp001' --withSecret 

or create it in the UI:

  • Open portal.azure.com
  • Select Microsoft Entra ID
  • Select App registrations –> New app registration
  • Give it a name, select Register
  • Select Certificates and secrets, create a secret, take note of it. (No I mean for real - you will not be able to se the value again if you navigate away.)
  • Also copy the values of the App id and Tenant id - we need them later.

💡 If you think that you should grant API permissions for Dynamics CRM -> user_impersonation, please don’t be fooled. This is not necessary, as access to Dataverse exclusively handled by Security Roles. You can read more about it in this blog post:Why your Power Platform service principal doesn’t need a Dynamics user_impersonation scope

Create an Application user

Now we need to make sure that we create an Application user in all of our 4 environments.

  • Open aka.ms/ppac
  • Select your environment
  • Select Settings
  • Select Application users under Users & permissions
  • Select new app user
  • Select add an app
  • Select the app registration from Microsoft Entra Id
  • Select the business unit
  • Select the pen icon next to Security roles and add the System Administrator role
  • Select Save

💡Remember to do this for all environments!

Create Service connections in Azure DevOps

To be able to use the Power Platform Build Tools in Azure DevOps, we first need to install them. You can find the download here

  • Return to Azure DevOps, select Project settings –> Pipelines –> Service connections
  • Select new service Connection
  • Select Power Platform
  • Select Application Id and secret as authentication method
  • Obtain the Instance URL from your DEV environment ( Open make.powerapps.com, Select the DEV environment, Select the gear icon ⚙️ in the top right corner, Select Settings, copy the Instance URL)
  • Paste this value to Server URL
  • Paste Tenant id, App id, and App secret from your app registration into the respective fields
  • Save the connection under name DEV Service Principal
  • Repeat this steps for BUILD, TEST, and PROD

Create Build pipeline 1 - Export from DEV

This Build Pipelines objective is to export a solution from DEV into source control (Azure DevOps repos)

It’s a good practice to write your pipelines in YAML - This way you can also log the code that describes your pipeline into source control. Here is the snippet:

Please note that

  • I have a variable called SolutionName so that I can reuse this pipeline for any other Power Platform solution
  • I like to have my zip file in the solution folder and the unpacked files in the sourcecode folder
pool:
  name: Azure Pipelines
#Your build pipeline references the ‘SolutionName’ variable, which you’ve selected to be settable at queue time. 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 settable at queue time. See https://go.microsoft.com/fwlink/?linkid=865971

steps:
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.tool-installer.PowerPlatformToolInstaller@2
  displayName: 'Power Platform Tool Installer '

- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.export-solution.PowerPlatformExportSolution@2
  displayName: 'Power Platform Export Solution  - unmanaged'
  inputs:
    authenticationType: PowerPlatformSPN
    PowerPlatformSPN: 'DEV Service Principal'
    SolutionName: '$(SolutionName)'
    SolutionOutputFile: 'solution/$(SolutionName).zip'

- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.unpack-solution.PowerPlatformUnpackSolution@2
  displayName: 'Power Platform Unpack Solution '
  inputs:
    SolutionInputFile: 'solution/$(SolutionName).zip'
    SolutionTargetFolder: '$(Build.SourcesDirectory)\sourcecode'
    ProcessCanvasApps: true

- script: |
   echo commit all changes
   git config user.email "<my email>
   git config user.name "<my name>"
   git checkout -B main
   git add --all
   git commit -m "adds source code files from DEV"
   git push --set-upstream origin main   
  displayName: 'Command Line Script'

If you prefer to build the pipeline in a visual editor (and later on copy/paste the yaml), this is how it works: First, enable classic editor in the projects settings.Then,

  • Select Pipelines –> New Pipeline –> Use the Classic Editor
  • Select the Source as Azure Repos Git, select your Project, Repository and Branch and select continue
  • Select Start with empty job
  • Select Agent Job 1 and check Allow Scripts to access OAuth token
  • Add task Power Platform Tool Installer
  • Add task Power Platform Export Solution
  • As Service Connection select Dev Service Principal
  • Provide the solution name (not the displayName) and the folder for the output - select the … menu for that. You can also use variable for that.
  • Uncheck the Export as Managed solution checkmark
  • Add task Power Platform Unpack solution
  • Specify Input file (its the output file location from previous task)
  • Specify target folder to output th unpacked files (can sit at the root of the project - use the … menu)
  • Add a task Command Line script, and paste the below script

echo commit all changes
git config user.email “<your git email>”
git config user.name "<your git user name>"
git checkout -B main
git add --all
git commit -m "code commit"
git push --set-upstream origin main
  • Save and queue the pipeline and wait for it to run successfully

Create Build pipeline 2 - Build Managed Solution

Now onto building pipeline 2

pipeline overview

Objective here is to deploy the unmanaged solution to our BUILD environment, so that we can still continue to modify code in DEV.

pipeline 2

Here is the yaml view:

pool:
  name: Azure Pipelines
variables:
  SolutionName: '$(SolutionName)'

steps:
- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.tool-installer.PowerPlatformToolInstaller@2
  displayName: 'Power Platform Tool Installer '

- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.pack-solution.PowerPlatformPackSolution@2
  displayName: 'Power Platform Pack Solution '
  inputs:
    SolutionSourceFolder: sourcecode
    SolutionOutputFile: '$(Build.ArtifactStagingDirectory)\$(SolutionName).zip'

- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.import-solution.PowerPlatformImportSolution@2
  displayName: 'Power Platform Import Solution '
  inputs:
    authenticationType: PowerPlatformSPN
    PowerPlatformSPN: 'BUILD Service Principal'
    SolutionInputFile: '$(Build.ArtifactStagingDirectory)\$(SolutionName).zip'

- task: microsoft-IsvExpTools.PowerPlatform-BuildTools.export-solution.PowerPlatformExportSolution@2
  displayName: 'Power Platform Export Solution - managed'
  inputs:
    authenticationType: PowerPlatformSPN
    PowerPlatformSPN: 'BUILD Service Principal'
    SolutionName: '$(SolutionName)'
    SolutionOutputFile: '$(Build.ArtifactStagingDirectory)/$(SolutionName)_managed.zip'
    Managed: true
    OverwriteLocalSolution: false

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact: drop'
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)\'

For the classic editor, use the following

Now onto building pipeline 2

  • Add tasks
    • Power Platform Tool Installer
    • Power Platform Pack Solution
    • Power Platform Import Solution
    • Power Platform Export Solution
    • Publish Artifact drop

Power Platform Pack Solution

This task will pack the unpacked files that are currently sitting in DEV as an unmanaged solution:

Build pipeline 2 - pack

Power Platform Import Solution

This task will now import the unmanaged solution into our BUILD environment.

Build pipeline 2- import

Power Platform Export Solution

This task exports the solution as a managed solution:

Build pipeline 2 - export

Publish Artifact Drop

As a last step, we will now publish the build artifacts

Build pipeline 2

Release pipeline to TEST

Now that we have our managed solution in the BUILD environment, we will want to release it to TEST or to PROD (depends on requirements of User Acceptance Testing)

ALM overview

  • Under Pipelines, select Releases
  • Under Artifacts, select your project and the source (its the Build pipeline 2 - Build Managed Solution)

Release Overview

  • For Stage 1, add 2 tasks
    • Power Platform Tool Installer
    • Power Platform Import Solution

Release import to TEST

  • Create a Release
  • Deploy the Release

Release pipeline to PROD

Create another Release pipeline for PROD

Process of CI/CD

The process of Continuos Improvement/Continuos Deployment (CI/CD) describes how we can now iterate in a secure way to improve our app, while making sure that users are not affected by changes happening in DEV, have the chance to exactly test in TEST what they will get in PROD and developers can feel at ease as all code is stored in source control and commits can be easily tracked.

For our Build pipeline 2 - Build Managed Solution we can turn on a setting enable continuos integration which means, that every time, we push code to our main branch this job runs and builds a Managed solution in BUILD.

We do push code to our main branch by letting our Build pipeline 1 - Export from DEV run 💡 - which we trigger manually in Azure Devops.

continuos integration

For the Release pipeline, there is a similar setting:

continuos integration in release

Once it is enabled, it triggers automatically, if a new Build is available, aka when our Build pipeline 2 - Build Managed Solution ran successfully. This means, that with these settings being switched on, we start a chain reaction every time, we run Build pipeline 1 - Export from DEV - and it results in a new release of a managed solution in TEST.

Users can now do the user acceptance training, log experienced issues or change requests for the next release and then we can manually trigger the Release to PROD pipeline so that we export the solution to the PROD environment. In case of severe issues with the release, which only get discovered during user acceptance testing, release to PROD will be deferred, changes to the app are being addressed in DEV, and then the pipelines are triggered again manually to run the entire process again.

Conclusion

With this basic setup, we solve the issues described at the beginning of the post - and deployments now don’t take up human time anymore - as noone needs to manually import and export zip files. Also we safely separated developing solutions from using them and took care that the solution in the PROD environment is a managed one.

From here, you can now expand your pipeline. I will cover some examples in one of my next blog posts - stay tuned!

You May Also Like

Want to work with me?