Deploying Azure WebApps with Kudu Zip API
Microsoft recently announced a new zip API to allow publishing of Azure web apps, functions and webjobs. This may seem overkill given the huge variety of existing deployment options (Local Git, GitHub, MSDeploy, FTP, Kudu Rest API, VSTS, Bitbucket, DropBox, Visual Studio, …).
However, many of the existing options assume you want to use Kudu’s CI build system. This is great for small demo apps, but in many commercial scenarios you may prefer to build the code yourself and just publish the compiled assets.
The new zip API simplifies things by letting you simply upload a zip file containing everything that should end up in the site\wwwroot
folder.
I thought I’d give it a try and wrote a PowerShell script to create a web app (using the Azure CLI of course!) and then push a zip file to it.
First lets create an app service plan and web app:
# variables
$resourceGroup = "ZipDeployTest"
$location = "westeurope"
$appName = "zipdeploytest1"
$planName = "zipdeploytestplan"
# create resource group
az group create -n $resourceGroup -l $location
# create the app service plan
az appservice plan create -n $planName -g $resourceGroup -l $location --sku B1
# create the webapp
az webapp create -n $appName -g $resourceGroup --plan $planName
Now we need to get the user name and password for deploying. We can use Azure CLI queries to get those:
# get the deployment credentials
$user = az webapp deployment list-publishing-profiles -n $appName -g $resourceGroup `
--query "[?publishMethod=='MSDeploy'].userName" -o tsv
$pass = az webapp deployment list-publishing-profiles -n $appName -g $resourceGroup `
--query "[?publishMethod=='MSDeploy'].userPWD" -o tsv
And now its just a case of calling the api/zipDeploy
endpoint for our site, with the credentials as a basic auth header (a little bit fiddly to set up in PowerShell) and the zip file passed as an -InFile
:
# basic auth with Invoke-WebRequest: https://stackoverflow.com/a/27951845/7532
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$Headers = @{
Authorization = $basicAuthValue
}
$sourceFilePath = "publish.zip" # this is what you want to go into wwwroot
# use kudu deploy from zip file
Invoke-WebRequest -Uri https://$appName.scm.azurewebsites.net/api/zipdeploy -Headers $Headers `
-InFile $sourceFilePath -ContentType "multipart/form-data" -Method Post
And that’s all there is to it. You can use this technique with web apps, function apps and webjobs.
One thing you might be wondering is – what happens to anything already in my wwwroot
folder? Suppose my app saves some files in the app_data
folder? Will they get deleted?
The answer is that zip deploy will delete everything that was uploaded by a previous zip deploy, but leave any files that got there by any other means. This is a nice approach that keeps any app created files safe, but allows dead application code to get deleted.
As an example of creating your zip package, here’s a PowerShell script I created that prepares a publish.zip file containing three .NET core azure webjobs. As you can see I’m using dotnet publish
to publish the code, and then putting it into the app_data\jobs
subfolder as is required by WebJobs as well as copying in a few other files into their appropriate places. The full GitHub project with PowerShell scripts to create the publish.zip
and deploy it with the zip API is available here.
$publishFolder = "publish"
# delete any previous publish
if(Test-path $publishFolder) {Remove-Item -Recurse -Force $publishFolder}
# publish the webjobs
dotnet publish triggered1 -c Release -o ..\$publishFolder\app_data\jobs\triggered\triggered1
Copy-Item triggered1\run.cmd publish\app_data\jobs\triggered\triggered1
dotnet publish scheduled1 -c Release -o ..\$publishFolder\app_data\jobs\triggered\scheduled1
Copy-Item scheduled1\run.cmd publish\app_data\jobs\triggered\scheduled1
Copy-Item scheduled1\settings.job publish\app_data\jobs\triggered\scheduled1
dotnet publish continuous1 -c Release -o ..\$publishFolder\app_data\jobs\continuous\continuous1
Copy-Item continuous1\run.cmd publish\app_data\jobs\continuous\continuous1
Copy-Item continuous1\settings.job publish\app_data\jobs\continuous\continuous1
# zip the publish folder
$destination = "publish.zip"
if(Test-path $destination) {Remove-item $destination}
Add-Type -assembly "system.io.compression.filesystem"
[io.compression.zipfile]::CreateFromDirectory($publishFolder, $destination)