A Step-by-Step Kickstarter to building automated software development lifecycle workflows for Power Virtual Agent and the Power Platform
We will use at least four environments within this guide:
- âdevelopmentâ per developer to build your bot,
- âbuildâ to generate a build artifact,
- âtestâ to test your solution as a managed solution and
- âproductionâ to run the bot as a managed solution.
A GitHub repository to store your solution and all your workflow files.
Table of Contents
- Excursus: words on governance
- The Idea behind the workflows
- Authentication basics and solution
- Workflows
- Assets
- Additional Information
Excursus: words on governance
Please consider a governance solution like âLanding Zones for Power Platformâ to create and manage your environments (Role-based Access Control [RBAC], apply policies, including data loss prevention, allowing connectors, or denying some), primarily if you work with fusion teams of pro and low-code developers. âLanding Zones for Power Platformâ is Microsoft Reference architecture to govern Power Platform Deployments.
The idea behind the workflows
[1] We will export the solution from your build environment to a new branch of your GitHub repository with the first workflow and a manual trigger. After inspecting the changes on GitHub, we will create a pull request and merge the pull request.
[2] After merging a second workflow, we automatically build a managed solution within your build environment and import the managed solution into your test environment.
After end-to-end testing, we create a new release on GitHub, and
[3] the third workflow will automatically import the solution into your production environment.
Authentication basics and solution
Create a service principal account
To create a service principal account, we need to set up an App Registration in Azure Active Directory.
In the Azure portal, select âAzure Active Directoryâ in the left pane and select âApp registrationsâ, and click on âNew registrationâ.
On the âRegister an applicationâ page, enter your applicationâs registration information:
- In the Name section, enter a meaningful application name like âBotplatformâ
- Select âAccounts in any organizational directoryâ option from the Supported account types section.
- Click on âRegisterâ to create the application.
Upon creation of the application registration, please note and save the âDirectory (tenant) IDâ and the âApplication (client) IDâ of the application.
On the navigation panel of the Overview page, select âAPI permissionsâ.
-
Choose â+ Add a permissionâ, and in the âMicrosoft APIsâ tab, choose âDynamics CRMâ.
-
In the âRequest API permissionsâ form, select âDelegated permissionsâ, check âuser_impersonationâ, and then choose âAdd permissionsâ.
-
Back in the âRequest API permissionsâ form, choose âPowerApps Runtime Serviceâ, select âDelegated permissionsâ, check âuser_impersonationâ, and then choose âAdd permissionsâ again.
-
For the third time in the âRequest API permissionsâ form, choose âAPIs my organization usesâ. Search for âPowerApps-Advisorâ using the search field and select âPowerApps-Advisorâ in the results list. Select âDelegated permissionsâ, check âAnalysis.Allâ rights, and then choose âAdd permissionsâ for the last time.
Next, proceed to create a client secret. In the navigation panel, select âCertificates & secretsâ:
-
Below âClient secretsâ, select â+ New client secretâ.
-
In the form, enter a description and select âAddâ. Record the secret string; you will not be able to view the secret again once you leave the form.
Create an application user
To deploy solutions as part of a CI/CD pipeline, an âApplication userâ needs access to the environment.
An âApplication userâ represents an unlicensed user that is authenticated using the application registration completed in the previous steps.
- Navigate to your environment on Power Platform Admin Center
- Navigate to âSettingsâ > âUsers + permissionsâ > âApplication Userâ.
- Select â+ new app userâ. A panel will open on the right-hand side of the screen.
- Select â+ Add an appâ. A list of all the application registrations in your Azure AD tenant is shown. Proceed to select the application name from the list of registered apps.
- Under âBusiness unitâ, in the dropdown box, select your environment as the business unit.
- Under âSecurity rolesâ, select System Administrator, and then select âCreateâ. This will allow the service principal access to the environment.
With the âApp registrationâ and the âApplication Userâ, we can use âGitHub Actionsâ to manage our environments. Yes, environments as we need several to work safely and professionally. Please register this Application User in every environment you deploy (dev, staging, testing, and production).
Create a Solution
A solution is a kind of âapplication containerâ. In this âboxâ, you will find everything related to your project. Within the solution, you will find all your bots, flows, connector references, and other stuff a developer produced.
- Navigate to https://make.powerapps.com and sign in with your credentials. Click the âenvironment selector dropdownâ in the header and select your development environment.
- Click the âSolutionsâ area in the left navigation and click the âNew solutionâ button to create a new solution.
- In the side panel that appears, enter the applicationâs name, and click the âAdd Publisherâ option.
- Select the publisher you just created on the new solution panel and click âCreateâ to create a new unmanaged solution in the environment.
- Select your solution in the solutions list and click the âEditâ button.
- Your new solution will be empty, and you need to add your bot. Click the â+ Add existingâ and select your bot.
- After adding your bot use the tree dots menu item and â+ add required objectsâ to get everything into the solution.
Create a new GitHub secret for Service Principal Authentication
- Navigate to your repository and click âSettingsâ, then expand âSecretsâ, and then âclick Actionsâ.
- On the Secrets page, name the secret âPOWERPLATFORMSPNâ. Use the client secret from the application registration created in Azure Active Directory, enter it into the Value field, and then select âAdd secretâ. The client secret will be referenced in the YML files used in your GitHub actions.
Workflows
Create four yml-files in the .github/workflow
directory of your repository.
Export from dev, unpack, prepare, commit, and push to a new branch
please update CLIENT_ID and TENANT_ID with your id´s and exchange the value for solution_name. Or store them as a secret in GitHub and integrate the secret here like the âPowerPlatformSPNâ secret.
export-branch-solution.yml
name: export-and-branch-solution
name: export-and-branch-solution
# Export solution from DEV environment
# unpack it and prepare, commit, and push a git branch with the changes
on:
workflow_dispatch:
inputs:
# Change this value
solution_name:
description: 'name of the solution to worked on from Power Platform'
required: true
default: DemoEnvironment
#Do Not change these values
solution_exported_folder:
description: 'folder name for staging the exported solution *do not change*'
required: true
default: out/exported/
solution_folder:
description: 'staging the unpacked solution folder before check-in *do not change*'
required: true
default: out/solutions/
solution_target_folder:
description: 'folder name to be created and checked in *do not change*'
required: true
default: solutions/
DEV_ENVIRONMENT_URL:
description: 'Development environment url.'
type: string
required: true
env:
#edit your values here
CLIENT_ID: 'your_client_id'
TENANT_ID: 'your_tenant_id'
jobs:
export-from-dev:
runs-on: windows-latest
# or you can say runs-on: ubuntu-latest
env:
RUNNER_DEBUG: 1
steps:
- uses: actions/checkout@v2
with:
lfs: true
- name: who-am-i action
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.DEV_ENVIRONMENT_URL}}
app-id: ${{env.CLIENT_ID}}
client-secret: ${{ secrets.PowerPlatformSPN }}
tenant-id: ${{env.TENANT_ID}}
- name: export-solution action
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.DEV_ENVIRONMENT_URL}}
app-id: ${{env.CLIENT_ID}}
client-secret: ${{ secrets.PowerPlatformSPN }}
tenant-id: ${{env.TENANT_ID}}
solution-name: ${{ github.event.inputs.solution_name }}
solution-output-file: ${{ github.event.inputs.solution_exported_folder}}/${{ github.event.inputs.solution_name }}.zip
- name: unpack-solution action
uses: microsoft/powerplatform-actions/[email protected]
with:
solution-file: ${{ github.event.inputs.solution_exported_folder}}/${{ github.event.inputs.solution_name }}.zip
solution-folder: ${{ github.event.inputs.solution_folder}}/${{ github.event.inputs.solution_name }}
solution-type: 'Unmanaged'
overwrite-files: true
- name: branch-solution, prepare it for a PullRequest
uses: microsoft/powerplatform-actions/branch-solution@v0
with:
solution-folder: ${{ github.event.inputs.solution_folder}}/${{ github.event.inputs.solution_name }}
solution-target-folder: ${{ github.event.inputs.solution_target_folder}}/${{ github.event.inputs.solution_name }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
allow-empty-commit: true
To start this manual flow, go to âActionâ, select the flow, click âRun workflowâ and insert your values into the fields.
Convert the solution to managed, upload the solution to the GitHub artifacts and deploy to the xxx environment
reusable basic flow
release-solution-to-xxx-reusable.yml
name: release-solution-to-xxx-reusable
# Reusable workflow
# convert solution to managed (using a build PowerPlatform environment for the conversion)
# upload the solution to the GitHub artifacts and deploy to the xxx environment
on:
workflow_call:
inputs:
#Do Not change these values
#Values are set by the caller
#caller sample: release-action-call.ymnl
solution_name:
description: 'The solution name.'
type: string
default: DemoEnvironment
solution_shipping_folder:
description: 'folder name for staging the exported solution *do not change*'
type: string
default: out/ship/
solution_outbound_folder:
description: 'staging the unpacked solution folder before check-in *do not change*'
type: string
default: out/solutions/
solution_source_folder:
description: 'folder name to be created and checked in *do not change*'
type: string
default: solutions/
solution_release_folder:
description: 'folder where the released binaries are going to be hosted *do not change*'
type: string
default: out/release
BUILD_ENVIRONMENT_URL:
description: 'Build environment url.'
type: string
required: true
TARGET_ENVIRONMENT_URL:
description: 'target environment url.'
type: string
required: true
CLIENT_ID:
description: 'The client id'
type: string
required: true
TENANT_ID:
description: 'The tenant id'
type: string
required: true
secrets:
envSecret:
description: 'The secret value for authentication using SPN'
required: true
jobs:
convert-to-managed:
runs-on: windows-latest
# or you can say runs-on: ubuntu-latest
env:
RUNNER_DEBUG: 1
steps:
- uses: actions/checkout@v2
with:
lfs: true
- name: Pack solution
uses: microsoft/powerplatform-actions/[email protected]
with:
solution-folder: ${{ inputs.solution_source_folder}}/${{ inputs.solution_name }}
solution-file: ${{ inputs.solution_outbound_folder}}/${{ inputs.solution_name }}.zip
solution-type: Unmanaged
- name: Import solution as unmanaged to build env
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.BUILD_ENVIRONMENT_URL}}
app-id: ${{inputs.CLIENT_ID}}
client-secret: ${{ secrets.envSecret }}
tenant-id: ${{inputs.TENANT_ID}}
solution-file: ${{ inputs.solution_outbound_folder}}/${{ inputs.solution_name }}.zip
force-overwrite: true
publish-changes: true
- name: Export solution as managed
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.BUILD_ENVIRONMENT_URL}}
app-id: ${{inputs.CLIENT_ID}}
client-secret: ${{ secrets.envSecret }}
tenant-id: ${{inputs.TENANT_ID}}
solution-name: ${{ inputs.solution_name }}
managed: true
solution-output-file: ${{ inputs.solution_shipping_folder}}/${{ inputs.solution_name }}.zip
- name: Upload the ready to ship solution to GH artifact store
uses: actions/upload-artifact@v2
with:
name: managedSolutions
path: ${{ inputs.solution_shipping_folder}}/${{ inputs.solution_name }}.zip
release-to-target:
needs: [ convert-to-managed ]
runs-on: windows-latest
env:
RUNNER_DEBUG: 1
steps:
- uses: actions/checkout@v2
with:
lfs: true
- name: Fetch the ready to ship solution from GH artifact store
uses: actions/download-artifact@v2
with:
name: managedSolutions
path: ${{ inputs.solution_release_folder}}
- name: Import solution to target env
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.TARGET_ENVIRONMENT_URL}}
app-id: ${{inputs.CLIENT_ID}}
client-secret: ${{ secrets.envSecret }}
tenant-id: ${{inputs.TENANT_ID}}
solution-file: ${{ inputs.solution_release_folder}}/${{ inputs.solution_name }}.zip
force-overwrite: true
Create a starter flow for âdeploy to testâ and âdeploy to productionâ
please update CLIENT_ID, TENANT_ID, BUILD_ENVIRONMENT_URL and TARGET_ENVIRONMENT_URL with your id´s and exchange the value for solution_name. Or store them as a secret in GitHub and integrate it here like the âPowerPlatformSPNâ secret.
release-to-test.yml
name: Release action to test
# Call the reusable workflow release-solution-to-xxx-reusable.yml
# Release your solution to staging.
on:
pull_request:
types:
- closed
workflow_dispatch:
jobs:
Release-solution-staging:
uses: ./.github/workflows/release-solution-to-xxx-reusable.yml
with:
#You can specify the solution name here
solution_name: DemoEnvironment
#Update your values here
BUILD_ENVIRONMENT_URL: 'yourvalue'
TARGET_ENVIRONMENT_URL: 'yourvalue'
CLIENT_ID: 'yourvalue'
TENANT_ID: 'yourvalue'
secrets:
envSecret: ${{ secrets.PowerPlatformSPN }}
release-to-production.yml
name: Release action production
# Call the reusable workflow release-solution-to-xxx-reusable.yml
# Release your solution to staging.
on:
release:
types: [published]
workflow_dispatch:
jobs:
Release-solution-staging:
uses: ./.github/workflows/release-solution-to-xxx-reusable.yml
with:
#You can specify the solution name here
solution_name: DemoEnvironment
#Update your values here
BUILD_ENVIRONMENT_URL: 'yourvalue'
TARGET_ENVIRONMENT_URL: 'yourvalue'
CLIENT_ID: 'yourvalue'
TENANT_ID: 'yourvalue'
secrets:
envSecret: ${{ secrets.PowerPlatformSPN }}
Both actions are triggered automatically (merge or release) and can triggered manually (workflow_dispatch). For a manual run, go to âActionâ, select the flow, click on âRun workflowâ and insert your values into the fields
Optional: Deploy a solution to a new development environment
deploy-development.yml
name: deploy development environment
# Reusable workflow
# deploy solution to a development environment
on:
workflow_dispatch:
inputs:
#Do Not change these values
solution_name:
description: 'The solution name.'
type: string
default: DemoEnvironment
solution_outbound_folder:
description: 'staging the unpacked solution folder before check-in *do not change*'
type: string
default: out/solutions/
solution_source_folder:
description: 'folder name to be created and checked in *do not change*'
type: string
default: solutions/
DEV_ENVIRONMENT_URL:
description: 'Development environment url.'
type: string
required: true
CLIENT_ID:
description: 'The client id'
type: string
required: true
TENANT_ID:
description: 'The tenant id'
type: string
required: true
jobs:
convert-to-unmanaged:
runs-on: windows-latest
# or you can say runs-on: ubuntu-latest
env:
RUNNER_DEBUG: 1
steps:
- uses: actions/checkout@v2
with:
lfs: true
- name: Pack solution
uses: microsoft/powerplatform-actions/[email protected]
with:
solution-folder: ${{ inputs.solution_source_folder}}/${{ inputs.solution_name }}
solution-file: ${{ inputs.solution_outbound_folder}}/${{ inputs.solution_name }}.zip
solution-type: Unmanaged
- name: who-am-i action
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.DEV_ENVIRONMENT_URL}}
app-id: ${{inputs.CLIENT_ID}}
client-secret: ${{ secrets.PowerPlatformSPN }}
tenant-id: ${{inputs.TENANT_ID}}
- name: Import solution as unmanaged to dev env
uses: microsoft/powerplatform-actions/[email protected]
with:
environment-url: ${{inputs.DEV_ENVIRONMENT_URL}}
app-id: ${{inputs.CLIENT_ID}}
client-secret: ${{ secrets.PowerPlatformSPN }}
tenant-id: ${{inputs.TENANT_ID}}
solution-file: ${{ inputs.solution_outbound_folder}}/${{ inputs.solution_name }}.zip
force-overwrite: true
publish-changes: true
To start this manual flow, go to âActionâ, select the flow, click âRun workflowâ and insert your values into the fields.
Assets
Please find sample scripts in my GitHub repository ALM-for-PVA
Additional Informationâââa curated list
Landing Zones for Power Platformâââa Reference Architecture
Power Platform GitHub ActionsâââALM for Developers