Developing a Powerapps Portal is one thing, managing it across multiple environments is another - but it should not be!


In September 2019 I had the privilege to discuss this topic with Nick Doelman and Colin Vermander on their Refresh the Cache podcast and been promising them a write up of my Portal ALM process, so here goes (I intend to break down each topic into a seperate write up).

Over the last few months I have noticed a lot of interest in Powerapps Portals ALM and I feel that with the tools available it does not have to be made out to be as complicated.

In fact deploying a Powerapps Portal should be no different from deploying a Powerapps/Dynamics 365 CE Solution in that it has the same components ie. customization, configuration and possibly a few bits of code all packaged up as Solution.

The main difference with Portals is that it has a lot more configuration (data) that needs to be moved from your development environment to a target. 

I do not split my portal configuration from my portal solution. Its unpacked and exported, versioned, packed and imported all together. This approach also allows me to deploy my project solutions in layers and keep the Portal seperate from the rest of my project.

This is important as Portal development usually trail your core features in your project and you might not deploy a portal solution to all your target environments. For example you might deploy portal solution to Staging, Test, QA and Production but not to Training. 

 

Configuration data is the heart of a Powerapps Portal and traditionally we would use the Configuration Migration tool to lift and shift this data across instances. But until recently you could not filter in the tool which made exporting the annotations related to your Web Files tricky.


Yes, you can use the Configuration Migration tool for Portal deployment but why do this manually each time you need to update your Portal?


How then do you automate this process and how can you manipulate environment specific configuration such as Site Settings?


With the Export-CrmDataFile and Import-CrmDataFile Cmdlets available in the Microsoft.Xrm.Tooling.ConfigurationMigration powershell package you can easily export and import your Portal configuration, either as a standalone process or part of your deployment strategy. All you need is a configuration schema that you can generate with Configuration Migration Tool or use the samples provided by Microsoft here , or even beter the ones Colin Vermander published over here

The CmdLet usage is fairly straight forward and you can run them on their own or as part of your build process.

Export-CrmDataFile -SchemaFile "D:\Configuration\schema.xml" -DataFile "D:\Configuration\export.zip" -CrmConnection $conn -Verbose

 

But my preferred option is to use the Microsoft.Xrm.DevOps.Data Powershell package maintained by Andrew Vogel


This package allows you to generate filtered data that is compatible with the Configuration Migration Tool. It is a much cleaner approach than working with the configration schema and eliminates the need to manually import and export data.

$packages = Get-CrmDataPackage -Conn $conn -Fetches @("<fetch><entity name='adx_contentsnippet'>
<attribute name='adx_contentsnippetid' />
<attribute name='adx_contentsnippetlanguageid' />
<attribute name='adx_display_name' />
<attribute name='adx_name' />
<attribute name='statecode' />
<attribute name='statuscode' />
<attribute name='adx_type' />
<attribute name='adx_value' />
<attribute name='adx_websiteid' />
    <filter type='and'>
      <condition attribute='statecode' operator='eq' value='0' />
    </filter>
</entity></fetch>") -DisablePluginsGlobally $true 


$packages.Data.InnerXml | Out-File -FilePath  (Join-Path $PSScriptRoot "..\ReferenceData\data.xml")
$packages.Schema.InnerXml | Out-File -FilePath (Join-Path $PSScriptRoot "..\ReferenceData\data_schema.xml")

 

 

You can easily add this script as part of your Solution Export process and store the results in source - I dont like exporting solutions or configuration data as part of my Build or Release steps in Azure DevOps. I execute this from Visual Studio, commit and push my changes from where the build trigger and takes care of the rest. 

My approach is to not only unpack the Solution but also the configuration data required, both Portal configuration and standard reference data that is required across all environments.

My portal solution is now versioned per export and can easily be configured for a new environment and deploy with specific settings only for that environment. Sample of unpacked Solution.xml with updated Version number.

<ImportExportXml version="9.1.0.18341" SolutionPackageVersion="9.1" languagecode="1033" generatedBy="CrmLive" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SolutionManifest>
    <UniqueName>PortalConfiguration</UniqueName>
    <LocalizedNames>
      <LocalizedName description="Portal Configuration" languagecode="1033" />
    </LocalizedNames>
    <Descriptions>
      <Description description="Upgrade of Portal Configuration" languagecode="1033" />
    </Descriptions>
    <Version>3.20156.0.1848</Version>
    <Managed>2</Managed>
    <Publisher>

 

and corresponding deployed Portal Configuration Solution deployed as managed solution in the target environment.

 

With the data and solution unpacked it can easily be bundled up with the Package deployer tool. Once the changes are commited and pushed to the repository my build and release processes does the rest. This is all wired up in Visual Studio and packaged from the ImportConfig.xml used in the PackageDeployer project template for Visual Studio.

<?xml version="1.0" encoding="utf-16"?>
<configdatastorage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   installsampledata="false"
                   agentdesktopzipfile=""
		               agentdesktopexename=""
                   crmmigdataimportfile="ImportFiles/data.zip">
  
  <solutions>
    <configsolutionfile solutionpackagefilename="ImportFiles/PortalConfiguration_managed.zip" overwriteunmanagedcustomizations="true"/>
  </solutions>
  </configdatastorage>



You can import the configuration with powershell or either of the Powerapps DevOps build tasks available in Azure DevOps to import the solution or just the data configuration.

Import-CrmPackage -CrmConnection $CRMConn -PackageDirectory $PackageDirectory -PackageName $PackageName -LogWriteDirectory $LogsDirectory -EnabledAsyncForSolutionImport -SolutionBlockedRetryDelay 120 -SolutionBlockedRetryCount 10 -Timeout "00:15:00" -RuntimePackageSettings $RuntimeSettings -Verbose

 

Sample of a release stage into a new environment.

  - stage: Staging01
    
    displayName: Staging01
    dependsOn: Build
    condition: and(succeeded(), notIn(variables['Build.Reason'], 'Schedule'))
    jobs:
    - deployment: DeployJob      
      displayName: Staging01 
      environment: Staging01
      timeoutInMinutes: 120
      variables:
        - group: D365.D365CDEnvironment
        - group: Secrets-Staging01-AUE
      strategy:
        runOnce:
          deploy:
            steps:
            - task: PowerShell@2
              displayName: Deploy Solution
              inputs:
                targetType: filePath
                filePath: $(Pipeline.Workspace)/drop/Solutions/bin/Release/Scripts/SolutionDeploy.ps1
                arguments: -DeployServerUrl $(d365url) -UserName $(d365username) -Password $(d365password) -PipelinePath $(Pipeline.Workspace)

 


The next question is how do you manipulate settings specific to each target environment, for example your Dev portal use Local authentication where your Test and Production portal use Azure AD B2C, how do you update these Site Settings?

 

You could manipulate the exported XML data before packaging it up with the Package Deployer tool or after importing it into your target environment with a post deployment step. For example I usually remove the Administrator Webrole in my production portal and then also update Site Settings and Web Files per environment via a Post deployment action or by extending the PackageTemplate.cs class from the PackageDeployer project template for Visual Studio.

    [Export(typeof(IImportExtensions))]
    public class PackageTemplate : ImportExtension
    {
        public static string EnvironmentAttributeName;

        public override void InitializeCustomExtension()
        {
            // Do nothing. 
        }

        public override bool AfterPrimaryImport()
        {
            UpdateSiteSettings();
            return true;
        }

 

I prefer to deploy first and then update settings as part of my release process thus keeping my configuration in source as close to the state of the portal in development. This has come in handy a few times when I had to rebuild my development portal when accidentally deleted a component. 

 

The tools that are available allow you to automate the deployment of your Powerapps Portal. No need for manual data export and import nor having to manipulate configuration by hand between your deployments. From a single Visual Studio project template I have all the scripts and processes available to achieve full automated Powerapps Portals ALM. 

 



References:

https://github.com/dylanhaskins/PowerPlatformCICD

https://github.com/abvogel/Microsoft.Xrm.DevOps.Data

https://docs.microsoft.com/en-us/power-platform/alm/package-deployer-tool

https://docs.microsoft.com/en-us/power-platform/admin/manage-configuration-data

https://docs.microsoft.com/en-us/powerapps/maker/portals/admin/migrate-portal-configuration

https://www.powershellgallery.com/packages/Microsoft.Xrm.Tooling.ConfigurationMigration/1.0.0.44