Deploying ASP.NET 5 application to Azure Web App using Git or Mercurial
I haven’t found any official docs about publishing ASP.NET 5 applications to Azure using source control (continuous deployment), but I thought I’ll give it a try. I failed at first and I asked whether it’s even possible on Twitter:
Hi @michaldudak that's a very good question. We have taken the liberty of creating a forum for you: http://t.co/8C58Vewj3C
— Azure Support (@AzureSupport) June 16, 2015
The answer I got shown that it’s not something that everybody has already tried to do, so I put on my adventurer’s hat and started exploring.
I knew it should be possible somehow – recently an article about asp.net web app deployment appeared on MSDN. It suggests using Visual Studio Online or TFS. However, I wanted the deployment process to be simpler – Azure supports deployment from a source control system, so let’s figure out how to deploy an ASP.NET application to Azure using Git or Mercurial.
Simple web application
Lets’s start with something very simple – a single-project application at the root of the repository:
Fortunately and surprisingly, deploying it to Azure using git (or hg) just works out of the box. There is a lot of articles on the internet about setting up git deployment, so I’ll describe it very briefly.
- Log in to the Azure Portal.
- Create a Web App (New → Web Apps → Web App) or use an existing one.
- On the app’s blade find the Set up continuous deployment button (it’s in Deployment section).
- Choose your repository, authenticate, choose a branch you’d like to deploy from and wait for a while.
- Success, your ASP.NET 5 application should now be deployed and available!
This was easy, wasn’t it? Of course if our application had more classes or static files, this approach would also work.
npm, bower, grunt/gulp, etc.
Speaking of static files – most likely at some point you’d need to run some tasks during deployment, be it LESS to CSS compilation, script bundling, minification, image optimization, etc. These are commonly defined as Grunt or Gulp tasks. I’m not going to explain how to make these (a lot has been written about these topics), just how to run them as part of the deployment process. Again, this isn’t hard at all. The project.json file may contain a scripts section that specifies things to run during different stages of build process. Let’s take advantage of it, and run our tasks this way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
{ "version": "1.0.0-*", "authors": [ "Michał Dudak" ], "dependencies": { "Microsoft.AspNet.Server.IIS": "1.0.0-beta4", "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4", "Microsoft.AspNet.StaticFiles": "1.0.0-beta4", "Microsoft.AspNet.Diagnostics": "1.0.0-beta4" }, "scripts": { "postrestore": [ "npm install", "grunt build" ] }, "frameworks": { "dnx451": { }, "dnxcore50": { } } } |
Here I hooked into the postrestore event, which means my commands will be run after the project’s NuGet packages are restored. These packages are being restored automatically by the deployment engine (more on this later in this article), so we can be sure this code will execute. It’s important to run npm install
first, to download the necessary npm packages. Then I’m running the build
Grunt task (which I defined in the gruntfile).
Running Gulp tasks or installing Bower components would look very similar (in fact you can run any command that does not require admin privileges here).
Multi-project application
Rarely our applications are so simple. More often we’ll have solutions with multiple projects (without a single project.json in the root of the repository). Let’s consider the following directory structure:
As you can see, the src folder with two projects was introduced. Here the deployment won’t Just Work™. This is why my first attempts failed. The deployment engine can’t find the web project inside the directory structure. We need to give it a hint where the main project is located. To do this, we have to create a file called .deployment (note the dot at the beginning) inside the repository root. Its syntax is described in the documentation. For now, we need to specify the project path like this:
1 2 |
[config] project = src/Deployment.App |
It means “look for my main project inside the src/Deployment.App folder”. Specifying this setting should heal our build and the website should be available on Azure.
This technique is also required when the solution consists just of one project hidden somewhere inside folder structure (e.g. if in the previous example we had just the Deployment.App project under src folder, we would have to provide the .deployment file as well).
These were the basics. Now, on to some technical details…
Deployment customization
The engine that’s handling the source-control based deployment is Kudu. It’s an open source project hosted on GitHub. It uses the deployment script generator which is a part of the Azure CLI. There are several predefined templates of this script, but developers also have the possibility to modify it or even create one from scratch.
Default deployment script
In the Azure portal you are able to see details of each deployment. Just click on the particular deployment and another panel will appear.
As you can see, one of the steps of the deployment process is script generation. Clicking on View Log opens another panel that shows the exact command used to generate the script. Wanna take at look at what’s generated? No problem. You’ll need to access the web app’s console. There is a Console button on the web app panel, but I don’t recommend it. The better way is to install the Site Admin Tools extension (its console is far better than the default one). To do this, open the All settings panel (it’s inside the Essentials section of web app panel), select Extensions, then Add and finally select Site Admin Tools from the list. Once it’s installed, it’ll appear on the Extensions list. Just select it and click Browse to open the admin panel. One of its features is the Debug console, which we’ll use to browse through the files on the web app’s machine.
The file we’re looking for is located in D:\home\site\deployments\tools\deploy.cmd. You can either download it or display its contents in the console with the help of type
command. By examining it you should be able to see what’s going on after the commit is pulled on the Azure machine:
- First, it is determined whether node.js is installed. It is required for the rest of the process, so the deployment would fail if it wasn’t there.
- Then, Kudu sync tool is installed. As per its GitHub page, it’s a “tool for syncing files for deployment, will only copy changed files and delete files that don’t exists in the destination but only if they were part of the previous deployment”.
- DNVM and DNX are installed.
- The application is bundled and copied to a temporary folder using dnu publish.
- Kudu Sync is used to copy files from the temp folder to the destination folder.
- Finally, a post-deployment action is run.
Modifying the deployment script
Developers are allowed to replace the default deployment script with a custom one. You’d usually want to generate a default one and then modify it to your needs. It should be noted that there are some restrictions, however – the script does not run under elevated privileges.
First of all, you need to install the Azure cross-platform CLI tools. It’s required to generate the deployment script. Once you install them, run azure site deploymentscript --aspNet5 <path to the project.json file>
. This will generate two files: .deployment and deploy.cmd. The deploy.cmd should look like the default one found on Azure machine (actually, currently the local script installs KRE and KVM instead of DNX and DNVM; the script running on Azure uses the new versions; hopefully they’ll update the azure CLI to use the new tools soon).
You are free to modify any of these steps, but probably most often you’d want just to add an additional one. The official docs highlight some common use cases. If Kudu sees a custom deployment script, it will use it instead of its default one.
Conclusion
It turned out that Azure can handle ASP.NET 5 applications without much effort from developers. Whether we’re trying to push a simple application or something more complex, it should be pretty simple. We also have the possibility to customize the deployment process to some degree.
Have fun playing with it and exploring!
Nice article. Thank you for sharing. Very helpful.
Thanks for this nice article. Would there be a way doing the same to Azure Windows Virtual Machine (web server) or it sounds to sci-fi? 🙂
Hey, good question. I think it should be possible. Since Kudu is freely available, you might be able to run it on a virtual machine. Also, you’d have to create a Git hook that will trigger a deployment. I may play with it a bit in the future and will post here if I manage to get this to work.
Thank you!
I followed the steps but I got : TF401174: The item ‘C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\DNX\Microsoft.DNX.targets’ could not be found in the repository ‘xxxxx’ any advise?
What kind of application are you trying to deploy? Does it consist of multiple projects?
just a simple app, one project. I even set the .deployment file to the only one project I have. Project was created from VS 2015 RC
Nice article. What about if you have 2 different applications to deploy from one solution? Say I have a WebAPI project and a separate web project. Both are in the same solution and both deployed to the same repo? How do I tell Azure which project to deploy to the given app? I think my first guess, is to not store them both in the same repo. Is that correct?
Hi Trevor. It’s possible to deploy a selected project from a repository. Scott Hanselman wrote a blog post about this: http://www.hanselman.com/blog/DeployingTWOWebsitesToWindowsAzureFromOneGitRepository.aspx. Let me know if it helps.
I was wondering if this worked. Glad to see you proved it does. Excited to start using asp.net 5 when RC1 comes out in a couple more days.
Thanks,
Matt Watson with Stackify APM (http://www.stackify.com)
Thank you very much Michal for the amazing post. If you don’t need to execute tests or something complex this is by far a better solution than configuring a build definition on VS Online