One of the major challenges we faced with the AllReady app was building a custom kudu deployment script. There is incredible power in this system, but it takes a bit of research, and a bit of help to get all the pieces working.
Let’s start with the simple goal: to make testing easier, we wanted to deploy to a live site automatically whenever we merged a pull request into master.
Azure supports this by default, including for ASP.NET 5 sites. Using Kudu was an obvious choice.
Adding Web Jobs
Life got complicated when we added a web job to the project. We added a web job because one of the actions the Allready application performs is to send messages to all registered volunteers. We don’t want to tie up a thread running in the ASP.NET worker process for the duration of sending messages through a third party service. That could take quite some time.
Instead, we want to queue up the emails on a separate webjob so that the site remains responsive.
Complicating the Deployment
That’s where deployment got complicated. You see, when we started, webjobs aren’t supported under ASP.NET 5 yet. The webjob builds using ASP.NET 4.6, using Visual Studio 2015. We also have one interface assembly that contains the types that are shared between the web application (ASP.NET 5) and the web job (ASP.NET 4.6).
So our build now includes:
- Build the assemblies that are part of the web application using DNX.
- Build the assemblies that are part of the web job using MSBuild. (Note that this means building one assembly twice)
- Deploy the web site.
- Deploy the web job.
Those extra steps require creating a custom deployment script.
Creating the Custom Deployment Script
Here are the steps we needed for adding our own custom Kudu script. There were several resources that helped create this. First, this page explains the process in general. It is a shortened version of this blog series (link is to part 1).
The first task was to create a custom build script that performed exactly the same process that the standard build script process performs. I downloaded the Azure tools for deployment, and generated a deployment.cmd that mirrored the standard process.
You need to have node installed so you can run the azure-cli tool. Then, install the azure-cli:
npm install azure-cli –g
Then, run the azure CLI to generate the script. In my case, that was:
azure site deploymentscript –aspWAP allreadyApp/Web-App/AllReady/AllReady.xproj –s allready.sln
Notice that I’m directing azure cli to generate a script based on my xproj file. But, notice that this does not build the .csproj for the web jobs.
Before modifying the script, I wanted to verify that the generated script worked. It’s a good thing I did, because the default generated script did not work right away. The script generator assumes that the root of your github repository is the directory where your .sln file lives. That’s not true for allready. We have a docs directory, and a code directory under the root of the repo.
So, the first change I had to make is to modify the script to find the solution file in the sub-directory. After that, the script worked to deploy the website (but not the webjobs). Doing that build required three changes. First, I needed to restore all the NuGet packages for the .csproj style projects:
call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\NotificationsProcessor\packages.config"
-SolutionDirectory "%DEPLOYMENT_SOURCE%" -source https://www.nuget.org/api/v2/
Getting this right got me stuck for a long time. In fact, I needed to get some product team support during our coding event from David Fowler. The version of nuget running in Azure needs to use the V2 feed when it’s restoring packages for a .csproj based project. *Huge* thanks to David for helping us find that.
Next, we needed to build the .csproj based projects:
call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\
AllReady.Models\AllReady.Models.csproj"
IF !ERRORLEVEL! NEQ 0 goto error
call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\
NotificationsProcessor\NotificationsProcessor.csproj"
IF !ERRORLEVEL! NEQ 0 goto error
The final step is to deploy the webjobs. This meant copying the web jobs into the correct location. This step happens after the build, and before the Kudu Sync process:
mkdir "%DEPLOYMENT_TEMP%\wwwroot\app_data\jobs\continuous\notificationsprocessor\"
call xcopy /S "%DEPLOYMENT_SOURCE%\NotificationsProcessor\bin\debug"
"%DEPLOYMENT_TEMP%\wwwroot\app_data\jobs\continuous\notificationsprocessor\"
The deployment script and the .deployment config are in our repository, so if you want to explore, check it out. Our repository is here: http://www.github.com/htbox/allready. And, if you want to help, check out the issues and send us a pull request.