Tuesday, 23 February 2021

Quick Continuous Integration/Deployment (CI/CD) workflow for Episerver DXP using Azure DevOps

There are a lot of questions on Episerver World on how to quickly deploy the code on Episerver DXP or more specifically how to set up Continuous Integration/Deployment (CI/CD) workflow for Episerver DXP. 

Episerver recommends using Development API to support a CI/CD workflow. You can generate the API credential through the DXP Service management portal (https://paasportal.episerver.net) and while we are focusing our effort around Azure DevOps you can use the EpiCould Powershell module to wraps the functionality to integrate with a CI/CD pipeline. 


This feature from Episerver is game-changing and powerful which gives you the ultimate power to set up your CI/CD workflow as per your project requirements. With all the Development API greatness it comes with a catch, the deployment takes forever.  This is roughly the same time the deployment takes from Integration to Preproduction on DXP when you do the code deployment manually from the DXP Service management portal. 



This is sometimes not acceptable when developers are doing frequent pull requests against the development branch (or you don't bother with PRs and directly pushing your code in the development branch anyway) and the change needs to be immediately available in the integration environment for testing. Waiting for 20-40 minutes for the deployment to complete its not an option in this case, so how can we do this more quickly? 

There is another approach that is not common knowledge and using this approach we can be done with deployment within 5 minutes or less. Sound interesting? let's explore this and here is my take on it how to set up CI/CD workflow properly.  

I named this approach "Configuring CI/CD Pipelines as Code with YAML using Service Connection" (you can call this whatever you like). I find this method most convenient and resourceful for my CI/DI workflow against serval of my projects. The drawback of this approach is the deployment can only be done in the integration environment. You still have to rely on Deployment API or manual deployment through the DXP Service management portal for preproduction and production environments.  

The reason is we to rely on Service connections for this approach to work and for that, we need service principal credentials from Episerver. Episerver only provides this information for the integration environment. 

I have set up this process against serval of my projects and deployment to integration maximum take five minutes (including running the unit tests).

Now we have a method lets set it up against the Alloy project with 5 easy steps.

Step 1: Get Credentials

Send an email to Episerver Support (support[at]episerver.com) and ask them to generate the following information for the integration environment. Don't forget to include the project name in the email. 

  • Subscription Id
  • Subscription Name
  • Service Principal Id
  • Service principal key
  • Tenant Id
Once you have this information we proceed with step 2. 

Step 2: Setup Service Connection in DevOps

In Azure DevOps go to  Project Setting and click "Service Connections"


In the top right corner click "New service connection"


Select "Azure Resource Manager" and click next


Select "Service principal (manual)" and click next 


Fill in all the information provided by Episerver Support. Name your service connector name and click verify and save.  


Step 3: Changes in  Visual Studio Project 


After setting up the service connector, we have to make some changes to our Visual Studio Project for the correct web.config transformation. In this example project, we have three environments specific configuration files as per Episerver documentation
  • web.integration.config
  • web.preproduction.config
  • web.production.config
and they are set up as follow in the web project file

  <Target Name="DXCConfigs">
    <Delete Files="web.integration.out.dxc.config;web.preproduction.out.dxc.config;web.production.out.dxc.config" />
    <TransformXml Source="web.config" Transform="Web.integration.config" Destination="web.integration.out.dxc.config" />
    <TransformXml Source="web.integration.out.dxc.config" Transform="web.preproduction.config" Destination="web.preproduction.out.dxc.config" />
    <TransformXml Source="web.preproduction.out.dxc.config" Transform="web.production.config" Destination="web.production.out.dxc.config" />
  </Target>

Now add the following at the end of the project file so we have the correct web.config after the transformation and ready for our CI/CD pipeline and we name this adobuild.
  <Target Name="adobuild">
    <TransformXml Source="web.config" Transform="Web.integration.config" Destination="web.config" />
    <CallTarget Targets="Build" />
  </Target>


Step 4: The YAML Pipeline

Everything is ready for ur pipeline, let's create one in DevOps. We are going to trigger this pipeline against the development branch. It means every time a code check-in will happen against the development branch this pipeline will automatically run and deployment the code on the integration environment.

In DevOps click Pipelines 

Click "New pipeline" in the top right corner 

After going through the wizard by selecting

  • Connect 
  • Select
  • Configure 
you will end up with the example YAML 

Let's replace the content of the YAML file with the following 

# ASP.NET

# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4

trigger:
  batch: true
  branches:
    include:
    - development
  
pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:

- task: NuGetToolInstaller@1

- task: NuGetCommand@2
  inputs:
    command: 'restore'
    restoreSolution: '**/*.sln'
    feedsToUse: 'config'
    nugetConfigPath: './nuget.config'

- task: VSBuild@1
  inputs:
    solution: '$(solution)'
    msbuildArgs: '/target:adobuild /p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'
    clean: true

- task: VSTest@2
  inputs:
    testAssemblyVer2: |
     **\*Test*.dll
     !**\obj\**
    platform: '$(BuildPlatform)'
    configuration: '$(BuildConfiguration)'

- task: AzureRmWebAppDeployment@4
  inputs:
    ConnectionType: 'AzureRM'
    appType: 'webApp'

    packageForLinux: '$(build.artifactStagingDirectory)/WEB-PROJECT-NAME.zip'
    azureSubscription: 'SERVICE-CONNECTOR-NAME'
    WebAppName:  INTEGRATION-ENVOIRMENT-NAME
    ResourceGroupName: INTEGRATION-ENVOIRMENT-NAME
    deployToSlotOrASE: true
    SlotName: 'production'
    enableCustomDeployment: true
    RemoveAdditionalFilesFlag: true

As you can see I have made some amendments to the example YAML syntax generated by DevOps.

So as you can see in the Trigger task I have set up a development branch, which means this pipeline will automatically trigger for the development branch if there are any code commits happens. 

In the VSBuild task, I have added the adobuld that we set up in step 3.

The task AzureRmWebAppDeployment is the main task where actually deployment is done on the Episerver DXP integration environment. 

There are certain values need to be filled in this section that I highlighted in bold.

  • WEB-PROJECT-NAME : This is the web project name in your Visual Studio solution.
  • SERVICE-CONNECTOR-NAME : The name of the service connector we created in step 2
  • INTEGRATION-ENVOIRMENT-NAME: The integration environment hostname you can get from DXP Service management portal  e.g alloy01mstr2bx1u3inte

Step 5: Test

Now we have everything we need its time to test this process. Just commit a code change in your development branch and the pipeline will take care of the rest.

0 comments :

Post a comment