Power Platform & Tech Thoughts

Power Platform
&
Tech Thoughts

Azure DevOps ALM – Releases

Dec 4, 2023 | ALM

Now you have your pipeline setup, it’s time for next major step and 3rd in the series, which is releasing your exported solutions to multiple environments.

This guide will also include information such as backing up the solutions, linking work items and others, which are not critical but long term will prove valuable to your team. I will briefly touch on those, to limit the length of this post and give the general idea, but I would highly recommend you use this to research your own preferred extras to have in your ALM strategy.

Terminology of “ADO”, “pipelines” and “pipelines service” are as per previous post.

Preparation

There are a few steps to cover before you can start to create your release, as they will become critical to the whole process and need to be maintained. These are:

  • Deployment settings JSON file
  • Environment variable values
  • Connection IDs

Deployment Settings

In your git repository, you should already have created a pipelines folder in the previous blog post, with a folder for each solution within that. In the first solution folder, create a file named deployment_settings.json and copy the following sample. This name and sample must be exact.

{
"EnvironmentVariables": [
{
"SchemaName": "myorg_VarName",
"Value": "#{EnvVar-myorg_VarName}#"
}
],
"ConnectionReferences": [
{
"LogicalName": "myorg_O365Outlook",
"ConnectionId": "#{ConRef-myorg_O365Outlook}#"
"ConnectorId": "/providers/Microsoft.PowerApps/apis/shared_office365"
}

The above sample reflects the structure of your file, but the content should match the exact number of environment variables and connection references that are in your solution. If there are none of one, then remove everything between the {} and simply keep the []. This means the structure is maintained, preventing errors, but it is an empty array and the release will now there is nothing to use. If you have none of both, then make them both empty.

  • SchemaName – This is the “name” of your environment variable in full, as displayed when you edit it. Not the display name.
  • Value – Ensure the format is exactly as above and between the EnvVar– and }# enter the exact same text from SchemaName.
  • LogicalName – This is the “name” of your connection reference in full, as displayed when you edit it. Not the display name.
  • ConnectionId – Ensure the format is exactly as above and between the ConRef– and }# enter the exact same text from LogicalName.
  • ConnectorId – This is the type of connector you are using. Go to your connection (which you should already have in dev), open it and in the URL, copy the value after apiName and replace shared_office365 with this.

You will now have a complete settings file for your solution. Check it in and repeat the above for every one of your remaining solutions, then pull request them all into your main branch (such as develop, depending on your git strategy).

Environment Variables

For each solution that contains environment variables, open the variable and note down the name, value and solution somewhere safe. Once this is done, determine what value each variable should have in your target environments. This may legtimately be the same for every environment, or may be different.

It is best practice to never use default value unless you know the value will never change and you never need to set it. If you do need to set it, ensure default is empty and you use current value in all environments.

Connection IDs

For each solution that contains a connection reference, you will already be aware these point to connections. Therefore, for all the connections you know you use, carry out the following steps. Note: this may not be equal, you may have 3 Outlook references, 2 Dataverse, 1 SharePoint, but the connections themselves may only be 3.

  • Go to your connections.
  • Open each individually.
  • In the URL, copy the text after connectionName as well as noting down the connector and connection reference that uses it.

Setup & Artifacts

Select Pipelines then Releases. From here, click the folder icon and create a new folder under All pipelines. Finally, select that folder then New release pipeline.

The first main stage within your release will be artifacts. In a typical code based project, there will be only one, which will a CI build triggered by a pull request completion (not Connor MacLeod from the Highlander). However, as you may have multiple solutions containing your customisations, you will need to link to all pipelines created in the previous article.

The first will be your git repository, to allow backups of the solutions to be added.

  • Next to Artifacts, click Add
  • Source type = Azure Repos Git
  • Project = the main project you’re working within (viewable in the top left as the second item in the path).
  • Source = repository you used or created in the previous articles.
  • Default version = latest
  • Keep everything else as is.

Next, add your first pipeline (representing your first solution).

  • Source type = build
  • Source = name of pipeline from previous article.
  • Source alias = same text as your solution internal name and global variable in pipeline.
  • All other options remain the same as above.

Finally, ensure your triggers are as below.

  • Click lightning bolt in top right of artifact.
  • Enabled
  • No filter
  • PR trigger disabled.

Repeat the above 2 steps (not the first git repo) for all remaining solutions/pipelines you created in the previous article.

Rename

For the first part of our stages, we will be renaming the release to something more meaningful and backing up the solution to your git repo.

  • Next to stages, click +Add then New stage, then Empty job
  • Stage name = something meaningful to you
  • Owner = defaults to yourself, but change account if you wish. Notifications will go to this account.

Within the stage, click 1 job, 0 task and you will see the basic structure of a release stage. This is where your steps will exist to carry out many different actions, such as importing, renaming, backing up, etc. On Agent job:

  • Agent pool = Azure Pipelines
    • This means the stage will run on your default ADO agent. If you are using different custom ones, select those.
  • Agent Spec = windows-latest
  • Artifact download = click every item and deselect Select all artifacts
    • This ensures artifacts are not downloaded for this stage, reducing execution time as they are not required.

You are now ready to add a step, by clicking on the + icon next to Agent job. You will see there are many steps available, just like in your pipeline. For this stage, we will use PowerShell.

  • Display name = Something meaningful of your choosing.
  • Type = Inline
  • Script = Write-Host “‘##vso[release.updatereleasename]”$(ReleaseCustomName)
    • This is the command to rename your release. The ##vso command is built into ADO and you can search online for many more. ReleaseCustomName is a variable we will shortly set to contain the text and as we progress, you will see all variables have to be referred to in this format.

Git Backup

Next, we will create another stage for git backups. As understanding how this script works is quite detailed and will not be covered here, feel free to skip this section. Otherwise, this can serve as a basic first step to get it working, but I would highly recommend researching it to maintain it in the future.

  • Add a new stage with agent settings as above.
  • Add a step with Power Platform Tool Installer
  • Add a step with Power Platform Unpack Solution
    • Solution Input File = $(System.DefaultWorkingDirectory)/$(Release.TriggeringArtifact.Alias)/SolutionArtifacts/solution_unmanaged.zip
      • This points to where ADO uses files during pipelines and the directories within it where your artifact from the pipeline was published.
    • Target Folder to Unpack Solution = $(System.DefaultWorkingDirectory)/$(Release.TriggeringArtifact.Alias)/unpack/$(ReleaseCustomName)
      • This points to your solution directory within the ADO directory and a new unique folder within that to place the unzipped files from the solution.
    • Type = Unmanaged
  • Add a step with PowerShell
    • Name = your choosing
    • Type = Inline
    • Script = copy the entire text below.
Write-Host "** Setup **"
$branchName = "dataverse/release-unmanaged_solution_backup"
git config --global user.email "xxx@uk.teams.ms"
git config --global user.name "Release_Dataverse-ImportSolutions"

# Check branch exists
if (git ls-remote --heads origin $branchName)
{
Write-Host "** Branch exists - checking out **"
git checkout $branchName
git pull
}
else
{
Write-Host "** Branch does not exist - creating **"
git checkout -b $branchName
}

if (Test-Path -Path './$(Release.TriggeringArtifact.Alias)')
{
}
else
{
New-Item -Path . -Name "$(Release.TriggeringArtifact.Alias)" -ItemType "directory"
}

Write-Host "** Copying unpacked files **"
Copy-Item -Recurse -Force $(System.DefaultWorkingDirectory)/$(Release.TriggeringArtifact.Alias)/unpack/$(ReleaseCustomName)/* ./$(Release.TriggeringArtifact.Alias)

Write-Host "** Checking for changes **"
$changedFiles = $(git status --porcelain | Measure-Object | Select-Object -expand Count)

if($changedFiles -gt 0)
{
Write-Host "** Changes exist **"
Write-Host "** Staging files **"
git add .
git status

Write-Host "** Committing changes **"
git commit -m "Automated Release - $(Release.TriggeringArtifact.Alias)_$(Release.ReleaseId) #9999"

Write-Host "** Pushing to repository **"
git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" push origin $branchName
}
else
{
Write-Host "** No changes **"
}

I will not go into all the detail of this script, but ultimately it is creating a new branch or using one if it already exists, taking the unzipped solution files, checking if there’s changes and commiting them to the branch.

Environment Import

This section will cover importing a solution to a single environment. This stage should be repeated for all other environments, with changing elements detailed at the end.

  • Add another stage, but this time ensure agent settings has Select all artifacts ticked for all artifacts apart from your git repo.
  • Task: Replace Tokens (this is a 3rd party step, ensure you use the one by Guillaume Rouchon)
    • Root directory: $(ArtifactPath)
    • Target files: $(DeploySettingsFile)
    • Token pattern: #{…}#
      • You will see this reflects the format specified in the deployment settings json created in the preparation section of this guide.
  • Task: Power Platform Tool Installer
    Task: Power Platform Import Solution
    • Auth type: Service Principal
    • Service connection: Connection for relevant environment you created in the first setup ADO guide.
    • Environment Url: $(BuildTools.EnvironmentUrl)
    • Solution Input File: $(System.DefaultWorkingDirectory)/$(Release.TriggeringArtifact.Alias)/SolutionArtifacts/solution_managed.zip
    • Use deployment settings file: ticked
    • Import solution as asynchronous operation: ticked
    • Import as a holding solution: ticked
      • This is equivalent to the “stage” option, as a straight upgrade is not possible in DevOps.
    • Activate plugins: ticked if you want flows, etc to be on, unticked if not.
  • Task: Power Platform Apply Solution Upgrade
    • Auth type: Service Principal
    • Service connection: Connection for relevant environment you created in the first setup ADO guide.
    • Environment Url: $(BuildTools.EnvironmentUrl)
    • Solution Name: $(Release.TriggeringArtifact.Alias)

This will now import and upgrade your managed solution into your target environment. You will need to repeat all steps from this section for the remaining environments, only changing the service connection to point to a different location.

Pre-Deployment Rules

This section will cover how to place your stages in a specific order, as well as approvals for each. It is key to ensure you are releasing solutions in a specific order and not jumping to live before QA for example. It also ensures you undertake peer reviews and approvals prior to import.

In each stage, click the lightning bolt and person icon to the left, then follow these recommended settings:

Rename

  • Triggers: After release
  • Pre-deployment approvals: disabled

Backup

  • Triggers: After stage, rename
  • Pre-deployment approvals: disabled

QA

  • Triggers: After stage, rename
  • Pre-deployment approvals: enabled
  • Approvers: Enter names or groups of people you wish to approve to this step.
  • Approval order: Any order
  • Policies: The user requesting a release or deployment should not approve it

UAT

  • Triggers: After stage, QA
  • Pre-deployment approvals: enabled
  • Approvers: Enter names or groups of people you wish to approve to this step.
  • Approval order: Any order
  • Policies: The user requesting a release or deployment should not approve it

Live

  • Triggers: After stage, UAT
  • Pre-deployment approvals: enabled
  • Approvers: Enter names or groups of people you wish to approve to this step.
  • Approval order: Any order
  • Policies: The user requesting a release or deployment should not approve it

Variables

Environment Variables

This section will cover how we will use your deployment settings file to populate the environment variable and connection reference settings you created at the start of this article.

Click Variables at the top, you will an option to add and you can provide a name, value and scope.

  • Name: This should be exactly the same as the Value text of your environment variable in the file. For example EnvVar-MyVariable
  • Value: This should be the value you intend to put into the variable.
  • Scope: This is the stage in the release where this will be used. This means for each variable, you will need to add multiple versions if the values are to be different between environments and select the relevant stage. Otherwise, if it is to be the same, just select Release.

This is important – you now need to add a variable for every single environment variable you have in all your deployment settings files. Then in the future, if any are added or removed, they should also be added or removed from here. This means if a value is to change, you don’t need to edit the settings file, but it can be done here and is therefore a lot easier to manage. It also provides better visibility of the values.

Connection References

  • Click Variable groups
  • Click Manage variable groups
  • Add a new group
  • Give the group a meaningful name, such as Dataverse Connection Refs – QA
  • Add all your connection references from all settings files and values from Connection IDs section earlier in this article in exactly the same way as environment variables.
  • When done, save.
  • Return to your release and click Link variable group and select the group you just created.

The above separates these out from variables, as the values will rarely change, unless you add or remove connections, or change which account you use. Therefore it provides security in not mistakingly changing values within the release.

Repeat the above for all remaining environments, then apply a scope to determine which stage will use each group, just like environment variables.

Summary/Next…

This is a lot of information to take in, so I would recommend reading it first before attempting to create your builds and releases. Once the concepts have fully sunk in, you can then start applying ALM options that suit your organisations needs, such as linking work items, different approvals and more. But this will serve as a solid foundation to build off.

The next and final guide will, you will be happy, be a shorter one to explain how to run these and a summary of general concepts to continue on with.

Please feel free to reach out in the comments, on Reddit or on Discord if you have any questions. I am very active there and happy to help, as well as many other community members.

2 Comments

  1. Hi there, really enjoying this guide. the variable ReleaseCustomName doesnt seem to be set anywhere, what or where are you meant to set this variable?
    Thank you!

    Reply
    • Hey Jack – very sorry for the incredibly late reply, it was a long year! In the varibles tab of your release, it’s there with that name and a value of your choosing. We use a text value passed from the build, plus the solution name (alias of the build) and unique build number.

      Reply

Submit a Comment

Your email address will not be published. Required fields are marked *

Share This