Continuous integration and build automation continue to remain the core tenants of a successful DevOps and Application Lifecycle Management (ALM) process. Regardless of the type of software system you are working with, you should always make reasonable endeavours within both of these areas, so that you can meet the following objectives:
- Store all software artefacts within a shared code repository, that provides a full history of all changes and the ability to determine a last, known good configuration or version.
- Provide the mechanism to store and then quickly deploy compiled code artefacts into any target environment.
- Automate all aspects of the previous steps, and more besides, to reduce the amount of human intervention required.
Thankfully, although a lot of this may sound tricky to implement on the face of it, we have a plethora of tools at our disposal to help speed us along. Azure Pipelines is a competent tool in this regard and, via the use of YAML build definitions, we can achieve the above objectives and more. For example, using YAML, we can very quickly put together a library of deployment templates that are suitable for use across multiple projects, and subject to versioning/change control too. In today’s post, we’ll see how we can use YAML to automate the extraction of Dynamics 365 Online / Power App Solution files.
We can be thankful, therefore, that we live in more enlightened times these days; and, in particular, that we have some effective tools at our disposal to help us build something quickly, without having to resort to writing much custom code. Using the example YAML definition below and, courtesy of the Power Platform Build Tools, we can perform a daily extract of a solution file at 8 PM each day, expand out its contents and then push all changes into a chosen Git branch:
name: $(TeamProject)_$(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) trigger: none schedules: - cron: "0 20 * * *" displayName: Daily Build branches: include: - MyArea/MyBranch always: true jobs: - job: ExtractMySolution pool: vmImage: 'windows-latest' steps: - task: PowerPlatformToolInstaller@0 inputs: DefaultVersion: true - task: PowerPlatformSetSolutionVersion@0 inputs: authenticationType: 'PowerPlatformEnvironment' PowerPlatformEnvironment: 'My Environment' SolutionName: 'MySolution' SolutionVersionNumber: '1.0.0.$(Build.BuildID)' - task: PowerPlatformExportSolution@0 inputs: authenticationType: 'PowerPlatformEnvironment' PowerPlatformEnvironment: 'My Environment' SolutionName: 'MySolution' SolutionOutputFile: '$(Build.ArtifactStagingDirectory)\MySolution.zip' AsyncOperation: true MaxAsyncWaitTime: '60' - task: PowerPlatformUnpackSolution@0 inputs: SolutionInputFile: '$(Build.ArtifactStagingDirectory)\MySolution.zip' SolutionTargetFolder: '$(Build.SourcesDirectory)\JJG.MyProject\MySolution' - task: CmdLine@2 inputs: script: | echo commit all changes git config user.email "firstname.lastname@example.org" git config user.name "Automatic Build" git checkout MyArea/MyBranch git add --all git commit -m "Latest solution changes." echo push code to new repo git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" push origin MyArea/MyBranch
Let’s talk through exactly what this YAML file is doing:
- To begin, we must install all of the pre-requisite components needed by the Power Platform Build Tools. This is a mandatory step and will prevent any nasty errors further down the line.
- We then update the version of our solution, using a combination of a fixed version number and the unique build ID from Azure DevOps.
- Next, we perform an export of the unmanaged solution from the tenant, using an Asynchronous operation to process this. The pipeline then stores the resulting .zip file within a local directory on the build agent.
- Then, the pipeline unpacks the entire contents of the solution file into a new directory within the sources directory; which, in this instance, will be a direct copy of the contents of our MyArea/MyBranch branch.
- Finally, we run a series of Git commands via a command prompt to push all of the extracted solution contents back into our remote repository. As part of this, we define a custom user name and commit message as part of these changes.
So in less than six steps, we’ve been able to extract out every single component of our solution, and automate the entire process of getting these changes back into our repository. And all without requiring a single manual step - nice!
While this YAML file is relatively self-contained in terms of its functionality, there’s also a couple of things you’ll need to set up around this, to get it working as intended.
- The branch in question (in this case, MyArea/MyBranch) will need to exist in your target repository before running the pipeline for the first time.
- Make sure the Project Collection Build Service account has been granted Contribute privilege over the repository you are working with. You can verify this by navigating to Project Settings -> Repositories, selecting the repository you are working with and ensuring that the Contribute privilege is to set to Allow**.** This account will typically contain the name of your DevOps organisation in the title.
- Ditto above, but this time for the Build Service account for your project. This account will typically have the naming format of Build Service ().
- Create a generic service connection, which will store the details of the Dynamics 365 / Common Data Service environment hosting your solution file. For this, you will need the URL of your instance and the username/password value of an account with sufficient privileges to extract solutions from the environment.
Altogether then, by using the solution outlined in this post, or a variant thereof, developers no longer need to worry about manually extracting and checking in their solution changes each day. The MyArea/MyBranch branch can then remain open for all incoming changes, which we can then push where they need to go as part of a Pull Request further down the line. And, finally, we can assure the business that we are meeting the three objectives outlined at the start of this post…and, hopefully, preventing some poor individual from completing repetitive, manual tasks at the end of each day too. 🙂