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 “ ” 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. Landing Zones for Power Platform 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 , we create a new release on GitHub, and end-to-end testing [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; once you leave the form. you will not be able to view the secret again Create an application user To deploy solutions as part of a pipeline, an “Application user” needs access to the environment. CI/CD 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 “ ” to manage our environments. Yes, as we need several to work safely and professionally. Please register this Application User in every environment you deploy (dev, staging, testing, and production). GitHub Actions environments 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 solution in the environment. unmanaged 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 directory of your repository. .github/workflow 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/who-am-i@0.4.0 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/export-solution@0.4.0 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/unpack-solution@0.4.0 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/pack-solution@0.4.0 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/import-solution@0.4.0 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/export-solution@0.4.0 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/import-solution@0.4.0 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/pack-solution@0.4.0 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/who-am-i@0.4.0 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/import-solution@0.4.0 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 — a Reference Architecture Landing Zones for Power Platform — ALM for Developers Power Platform GitHub Actions Featured Image by on Unsplash Andy Li Also published here