Scott Hanselman

Deploying a MSDeploy-packaged Web application to a Linux Azure App Service with Azure DevOps

August 30, 2019 Comment on this post [9] Posted in Azure
Sponsored By

For bizarre and unknown historical reasons, when using MSDeploy to make a ZIP package to upload a website to a web server you get a massively deep silly path like yada/yada/C_C/Temp/package/WebApplication1/obj/Release/Package/PackageTmp. I use .NET Core so I usually do a "dotnet publish" and get a sane path for my build artifacts in my CI/CD (Continues Integration/Continuous Deployment) pipeline.

I'm using the original pipeline editor on free Azure DevOps (I'm still learning DevOps YAML for this, and this visual pipeline editor IMHO is more friendly for getting started.

However, I'm using a "Visual Studio Build" task which is using MSDeploy and these MSBuild arguments.


Azure Dev OpsLater on in the process I'm taking this package/artifact - now named "" and I'm publishing it to Azure App Service.

I'm using the "Azure App Service Deploy" task in the DevOps release pipeline and it works great when publishing to a Windows Azure App Service Plan. Presumably because it's using, again, MSDeploy and it knows about these folders.

However, I wanted to also deploy to a Linux Azure App Service. Recently there was a massive (near 35%) price drop for Premium App Services. I'm running an S1 and I can move to a P1V2 and get double the memory, move to SSDs, and get double the perf for basically the same money. I may even be able to take TWO of my S1s and pack all my websites (19 at this point) into just one Premium. It'll be faster and way cheaper.

Trick is, I'll need to move my Windows web apps to Linux web app. That's cool, since I'm using .NET Core - in my case 2.1 and 2.2 - then I'll just republish. I decided to take my existing Azure DevOps release pipeline and just add a second task to publish to Linux for testing. If it works I'll just disable the Windows one. No need to rebuild the whole pipeline from scratch.

Unfortunately the Linux Azure App Service has its deployment managed as a straight ZIP deployment; it was ending up with a TON of nested folders from MSDeploy!

NOTE: If I'm giving bad advice or I am missing something obvious, please let me know in the comments! Perhaps there's a "this zip file has a totally bonkers directory structure, fix it for Linux" checkbox that I missed?

I could redo the whole build pipeline and build differently, but I'd be changing two variables and it already works today on Windows.

I could make another build pipeline for Linux and build differently, but that sounds tedious and again, a second variable. I have a build artifact now, it's just in a weird structure.

How did I know the build artifact had a weird folder structure? I remember that I could just download any build artifact and look at it! Seems obvious when you say it but it's a good reminder that all these magical black box processes that move data from folder to folder are not black boxes - you can always check the result of a step. The output of one step becomes the input to the next.

downloading an artifact from Azure DevOps

I should probably have a Windows Build and Linux Build (two separate build agents) but the site isn't complex enough and it doesn't do anything that isn't clearly cross-platform friendly.

Anthony Chu suggested that I just remove the folders by restructuring the zip file (unzipping/zipping it). Could be a simple way to get both Windows and Linux publishing from a single artifact. I can fix it all up with a fresh build and release pipeline another time when I have the energy to learn this YAML format. (Speaking of the Azure DevOps YAML which doesn't have a friendly editor or validator, not speaking of YAML as a generic concept)

Unzipping and zipping up MSDeploy mess

I unzip the weird folder structure, then zip it back up from a new root. It then cleanly deploys to the Linux Azure App Service from the same artifact I made built for the Windows App Service.

Ironically here's a YAML view of the tasks, although I build them with the visual editor.

- task: ExtractFiles@1
displayName: 'Extract files - MSDeploy Crap'
destinationFolder: linuxdrop
- task: ArchiveFiles@2
displayName: 'Archive linuxdrop/Content/D_C/a/1/s/hanselminutes.core/obj/Release/netcoreapp2.2/PubTmp/Out'
rootFolderOrFile: 'linuxdrop/Content/D_C/a/1/s/hanselminutes.core/obj/Release/netcoreapp2.2/PubTmp/Out'
includeRootFolder: false
- task: AzureRmWebAppDeployment@4
displayName: 'Azure App Service Deploy: hanselminutes-core-linux'
azureSubscription: 'Azure MSDN)'
appType: webAppLinux
WebAppName: 'hanselminutes-linux'
packageForLinux: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
RuntimeStack: 'DOTNETCORE|2.2'

Just to be clear, this isn't standard and it's a pretty rare edge case and it may not work for everyone but isn't it nice to google for a super rare edge case and instead of feeling all alone you find an answer?

Sponsor: Looking for a tool for performance profiling, unit test coverage, and continuous testing that works cross-platform on Windows, macOS, and Linux? Check out the latest JetBrains Rider!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
September 01, 2019 8:54
I've used Option 2 from this post from Microsoft's own Sayed Hashimi to remove the intermediate paths:
September 01, 2019 11:44
Changing the structure of your WebDeploy package can cause problems if you're using parameters, I wrote this up here:
September 01, 2019 11:45
Changing the structure of your WebDeploy package can cause problems if you're using parameters, I wrote this up here:
September 01, 2019 12:00
In our builds I use the package which tweaks the msbuild tooling to create a nice short -and most importantly stable- path in the zip file.
September 01, 2019 14:57
You could just do a webdeploy deploy to filesystem (make an output folder) and zip that yourself. It's an extra step in the pipeline, but everything is clean.
September 04, 2019 19:15
What is the best PHP encoder in terms of security, I heard PHPGuarder is pretty good. To know more visit
September 06, 2019 22:39
Greetings each! [url=]business[/url]
September 07, 2019 2:40
Have you tried to deploy multiple apps to a single linux app service instance?

I recently tried switching from Windows to Linux for my aspnet core app. Similar to you, I tried to reuse my existing pipeline in DevOps but I ended up creating a new one. With windows, I can deploy multiple sites using virtual directories. Would I need a multi-container docker instance to use a single Linux app service to host multiple Web Api's (microservices)?
September 09, 2019 16:41
How about using these MS Build parameters?

/p:DeleteExistingFiles=True /p:publishUrl="$(build.stagingDirectory)\$(BuildConfiguration)\web_package"

Then I just zip the content of web_package folder and deploy it via "run as package". This has clear folder structure, I believe you can use it for Linux too.

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.