Recently, I shifted a project of mine onto my company’s main build pipeline system on Azure DevOps. Doing so had a ton of benefits. The centralized build system runs tons of security checks for free. It’ll even sign your code for you.
That last part, however, sent me into something of a mad scramble. My app consists primarily of several Azure Functions written in C# using Visual Studio. And up until this point, I was building them and packaging them in a single MSBuild command-line argument:
- task: VSBuild@1 inputs: solution: ‘$(solution)’ msbuildArgs: ‘/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation=”$(build.artifactstagingdirectory)\\”’
This worked fine at the time. The parameter /p:WebPublishmethod=Package did the magic of packaging my web publish packages up into a ZIP files.
Problem is, the pipeline’s code signing was a step that took place after my build was complete. I needed to separate out the building and the packaging of my code.
Break Your Package Task Apart
Turns out this was easier than I expected. The trick is that a Web Publish package is just a ZIP file of the folder structure output by MSBuild. That means you can accomplish the same thing as the single line above by:
- Creating the web publish package as part of your MSBuild task; and
- Adding an ArchiveFiles task that ZIPs up the entire directory structure later in the build process.
To accomplish the first part, I change my MSBuild task slightly. First, I change it from building an entire solution to building a single project — one new msbuild entry in my Pipeline YAML for every Azure Function project I want to build:
- task: MSBuild@1 inputs: solution: '$(Build.SourcesDirectory)\Project1\Project1.csproj'
Next, I change the msbuildArgument property WebPublishMethod to Filesystem. I also specify the publishUrl parameter to be a local file path so I know exactly where my package web deploy package is:
msbuildArguments: '/p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:publishUrl=$(Build.ArtifactStagingDirectory)\Project1'
Finally, further down in my YAML Pipeline after the signing task, I specify an ArchiveTasks task in my Pipeline YAML that zips up the whole shebang for me:
- task: ArchiveFiles@2 inputs: rootFolderOrFile: '$(Build.ArtifactStagingDirectory)\Project1Package' includeRootFolder: false archiveType: 'zip' archiveFile: '$(Build.ArtifactStagingDirectory)\Project1.zip' replaceExistingArchive: true
MSBuild has been around for so long and has so many options that searching for the configuration you need can be frustrating. Hopefully, this article helps those of you who are looking for a way to decouple building and deployment packaging of Web apps in your Azure DevOps Pipelines.