Azure Devops release pipeline

Hi!

We are currently setting up a CI/CD pipe from Azure Devops to Litium on Demand cloud solution and are having some diffuculties.
Hoping anyone has done a similair solution, this is our challenge:

There are two ways to go about this; using the sftp or msdeploy.

The sftp works to the extend that devops is not whitelisted and thus cannot transfer the files. The solution is to have a server running a deploymentgroup agent within our network and/or VPN. This would work but adds more complexity and we are not that keen on having more environments to manage.

So, the msdeploy is whats left and thatā€™s what we have been working on. The problem seems to be that we cannot quite get it to work using the task 'MSDeploy Package Sync: ā€™ and was wondering if any of you have been encounter the same problem or maybe have a totally different solution to the problem?

Have a nice day and hoping you would throw us a helping hand!

//Kristian

Have not experimented in this area but see if this document can help you out.

Do you have the website query correct? It differs if the site is on shared or dedicated hosting
For example using:

Dedicated

Shared (note the query param)

Not sure if you using the classic or yaml version of the pipeline, the tasks are the same but it is setup in different ways.

I switched years ago to execute the msdeploy.exe from a powershell script as below due to all the problems with the inbuilt task in DevOps.

param(
    [string]$Source = $Env:Source,
    [string]$PublishName = $Env:PublishName,
    [string]$PublishServer = $Env:PublishServer,
    [string]$PublishUsername = $Env:PublishUsername,
    [string]$PublishPassword = $Env:PublishPassword,
    [string]$ManageAppPool = $Env:ManageAppPool
)

$auth = "username=$PublishUsername,password=$PublishPassword"
$msdeploy =  (get-childitem "HKLM:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy" | Select -last 1).GetValue("InstallPath") + "msdeploy.exe"

if ($ManageAppPool -ieq "True") {
    # stop app pool
    &$msdeploy "-verb:sync" "-source:recycleApp" "-dest:recycleApp=`"$PublishName`",recycleMode=StopAppPool,ComputerName=$PublishServer,$auth" "-allowUntrusted"
}

# deploy
&$msdeploy "-verb:sync" "-source:contentPath=`"$Source\`"" "-dest:contentPath=$PublishName,wmsvc=$PublishServer,$auth" "-allowUntrusted" -enableRule:AppOffline

if ($ManageAppPool -ieq "True") {
    # start app pool
    &$msdeploy "-verb:sync" "-source:recycleApp" "-dest:recycleApp=`"$PublishName`",recycleMode=StartAppPool,ComputerName=$PublishServer,$auth" "-allowUntrusted"
}

Not sure why but getting MSdeploy to work from azuredevops is not an easy task. Deploying with from visualstudio is not problem but something different is happening i azuredevops deploys.

We had to resolve to a special build and a powershellscript to add a parameterfile to the deploypackage to get webdeploy working against litium sharedhostingenvironment. Using exakt samt process as for deployment to a service agent results in 1. wrong files deployed 2. deployment refused because of work setting in generated deployment parameter files.

But perhaps patric script solves this in a different manner

The error about ā€œunauthorizedā€ is from our experience due to that you try to deploy to another web application or forder than you think you are because or settings in the parameter files.

Sorry for the absence, my answears got hidden, prob because my signature was on it since I responded by email.

@steve.redstorm Iā€™ve looked at that and that solution uses the sftp, so no go unfortunately.

@Artur Itā€™s shared, and the query is in place. Thanks for the heads up!

ā€¦ to be continued

ā€¦continued

@patric.forsgard I will take a look into some powershellscripting, thank you for posting your solution!

@Spotonkastebo Yes, it is really annoying, it should be easy right! In what way was your build special?

Thank you for your responses! Looks like the problem is in the way msdeploy is executed and the parameterfile. Will look in to your solutions and have a go at it!

Can you put the error log here so we can check?

We use this build arguments

/p:DeployOnBuild=true 
/p:WebPublishMethod=Package 
/p:PackageAsSingleFile=false 
/p:SkipInvalidConfigurations=true 
/p:OutDir=$(Build.ArtifactStagingDirectory)
/p:AutoParameterizationWebConfigConnectionStrings=False

and and an extra step to create a zip of the archivefolder and that zip is that we deploy with msdeploy.

$(Build.ArtifactStagingDirectory)\_PublishedWebsites\"your_web_project_project_filee_name"_Package\Archive

There are probably better solutions but we got it working anyway

Hello!
Lately some have had a problem with tls negotiation not working for the deployment step so you have to force TLS 1.2.

@steve.redstorm the server simply responds with an Unauthorized, I think itā€™s due to the parameterfile giving the wrong folder information, as previously mentioned.

@Spotonkastebo thanks for posting, Iā€™ll take a look at how that could work out!

@PontusKindh thanks for the headsup on TLS, will look into it. How would you go about and force TLS?

Iā€™ll have some time off now and will continue this matter on tuesday, thank you all so much for your responses!

1 Like

When building with msbuild we using this parameters to get all the files into the artifacts folder and then using the above script that I posted to use the artifacts folder as source.

/p:DeployOnBuild=true 
/p:WebPublishMethod=FileSystem 
/p:SkipInvalidConfigurations=true 
/p:PackageLocation="$(build.artifactstagingdirectory)\\"
/p:AutoParameterizationWebConfigConnectionStrings=false 

That will not include web.config transformation that I think you got in the solution from @Spotonkastebo.

We using the Visual Studio Team System extension Ā· magic-chunks/magic-chunks-dotnetcore Wiki (github.com) to transform the web.config before invoking the deployment script so what we have in the source (artifacts) folder should be exactly what we want on the web-server.

yes transformation of configs is also important. We have transformation-config for the development- test and productionenvironment but they are not set at ā€œsubfileā€ for the web.config because then the build pipelinw will consume them.

The build pipeline does a web.release.config transformation and then we do the environments specific transformation in the release pipeline. When using the deployment agent we donā€™t have to think about them at all but when using msdeploy we use a pipeline task called ā€œFile transformā€ to do the transforms on the config in the deployment zip. For some reason neither the build or release pipeline performs transformation of the connectionstring so we also have added variable replacement to the release pipeline to handle that setting in the deployed web.config.

Ok, so, finnally got it working :smiley:

Thank you all for contributing!

Here is the final result (I was going to add a file but since Iā€™m ā€˜newā€™ I cannot :-/)

Note that links are broken on purpose to avoid limitation on links allowed per post.


Deploy Litium via Azure Devops (classic, no YAML)

Prerequises

Create a service connection in your Azure Devops project to handle the nuget feed:

  • Service connection name: LitiumNuget
  • Authentication method: Basic Authentication
  • Feed URL: http_s://nuget.litium.com/nuget
  • Username: [Your Litium Doc username]
  • Password: [Your Litium Doc password]
  • Grant access permission to all pipelines: check

Modify Web.Release.config:

    <connectionStrings>
      <add name="FoundationConnectionString"
        connectionString="#{connectionString}#"
        xdt:Transform="Replace" xdt:Locator="Match(name)"/>
    </connectionStrings>

Add License.config file to Library/Secure files in your Azure Devops project

Build pipeline (Use the classic editor to create a pipeline without YAML.)

Tasks

  • Pipeline

    • Agent pool: Azure Pipelines
      • Agent Specification: windows-2019
  • Get Sources

    • Azure Repos Git
    • Pick a branch and leave the rest to default
  • Agent job

    • default
  • Tasks

    • Use Nuget (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/tool/nuget?view=azure-devops)
      • default (leave version empty)
    • Nuget restore (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/package/nuget?view=azure-devops)
      • Feeds to use: Feeds in my NuGet.config
      • Path to Nuget.config: Resources/NuGet.config
      • Credentials for feeds outside this organization/collection: LitiumNuget (see service connection)
    • Build solution **/*.sln (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build?view=azure-devops)
      • Solution: **/*.sln
      • Visual Studio Version: Visual Studio 2019
      • MSBuild Arguments:
        /p:Configuration=Release
        /p:DeployOnBuild=true
        /p:DeployDefaultTarget=WebPublish
        /p:WebPublishMethod=FileSystem
        /p:SkipInvalidConfigurations=true
        /p:DeleteExistingFiles=True
        /p:publishUrl="$(Build.ArtifactStagingDirectory)"
      • Platform: $(BuildPlatform)
      • Configuration: $(BuildConfiguration)
    • Publish Artifact: $(Build.BuildNumber) (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/publish-build-artifacts?view=azure-devops)
      • Path to publish: $(Build.ArtifactStagingDirectory)
      • Artifact name: $(Build.BuildNumber)
      • Artifact publish location: Azure Pipelines

Variables

  • Pipeline variables
    • BuildConfiguration: release
    • BuildPlatform: any cpu

Triggers

  • Continuous integration
    • enabled
    • Branch filters: Include same branch as source

Release pipeline

Artifacts (Add)

  • Select ā€˜Buildā€™

  • Project: [select your project name]

  • Source (build pipeline): [select name of build pipeline]

  • Default version: Latest (default)

  • Source alias: _[your selected build pipeline] (default)

  • Continuous deployment trigger

    • Enabled

Stages (Add)

  • Choose ā€˜Empty jobā€™
  • Trigger after release

Stage

  • Agent job
    • Agent Specification: vs2017-win2016
  • Tasks
    • Replace tokens in **/*.config (http_s://github.com/qetza/vsts-replacetokens-task#readme)
      • Root directory: $(System.DefaultWorkingDirectory)/[your artifact source alias]/$(Build.BuildNumber)
      • Target files: **/*.config
      • Token prefix: #{
      • Token suffix: }#
    • Download secure file (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/download-secure-file?view=azure-devops)
      • Secure File: [select your secure file]
    • Copy Files (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/copy-files?view=azure-devops&tabs=yaml)
      • Source Folder: $(Agent.TempDirectory)
      • Contents: [Name of your license file]
      • Target Folder: $(System.DefaultWorkingDirectory)/[your artifact source alias]/$(Build.BuildNumber)
    • PowerShell Script (http_s://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/powershell?view=azure-devops)
      • Type: Inline
      • Script:
$msdeploy =  (get-childitem "HKLM:\SOFTWARE\Microsoft\IIS Extensions\MSDeploy" | Select -last 1).GetValue("InstallPath") + "msdeploy.exe"
# Auth
$auth = "username=$env:PublishUsername,password=$env:PublishPassword"
# Deploy
&$msdeploy "-verb:sync" "-source:contentPath=`"$env:Source\`"" "-dest:contentPath=$env:PublishName,wmsvc=$env:PublishServer,$auth" "-allowUntrusted" -enableRule:AppOffline
      • Environment Variables:
        • Source: $(System.DefaultWorkingDirectory)/[your artifact source alias]/$(Build.BuildNumber)
        • PublishName: [Name of the folder in Litium cloud (ex: nnn-Test)]
        • PublishServer: https://[Litium environment name].litiumdrift.se:8172/msdeploy.axd
        • PublishUsername: LITIUMDRIFT\[your Litium drift username]
        • PublishPassword: [your Litium drift password]
  • Variables

    • Pipeline variables
      • connectionstring: Pooling=true;Database=[your Litium db];Server=[your Litium server];Integrated security=true;MultipleActiveResultSets=True
2 Likes

Thanks for sharing! :+1: