Microsoft has documented the process for building and deploying ASP.Net 5 applications to Azure Web Apps using Visual Studio Online build, but accomplishes the deployment using a PowerShell step in the build pipeline. We want to make use of VSO Release Management vNext to be able to stage releases through different environments. This post shows how we finally got this up and running.

Build

Microsoft’s documentation gave us the starting point for this step. The PreBuild.ps1 script provided takes care of bootstrapping the DNX runtime onto the build agent and selecting the right runtime based on the solution’s global.json and the Visual Studio build step builds and packages the web project (using dnu publish under the covers). However, the first problem we encountered was how to run the XUnit unit tests and failing the build if they don’t all pass.

Test

Our test projects are using xUnit and have a “test” command defined in the project.json, allowing them to be executed using dnx test.

"commands": { 
  "test": "xunit.runner.dnx" 
}

At least they could be if only this didn’t result in The term 'dnx' is not recognized as the name of a cmdlet, function, script file, or operable program.. Despite the presence of the -p argument to dnvm install the dnx command was still not available to a subsequent PowerShell execution task.

The solution proved to be a slight modification to the PreBuild.ps1 to set an alias for the DNX runtime version installed.

& $env:USERPROFILE\.dnx\bin\dnvm install $dnxVersion -p
& $env:USERPROFILE\.dnx\bin\dnvm alias default $dnxVersion

The test script then calls dnvm use default before it tries to run dnx test. In addition, the RunTests.ps1 script passes the -xml argument to dnx test which tells the xUnit test runner to output test results to an xml file that VSO can use to present nicely formatted details of test execution by adding a Publish Test Results build step.

Param([string]$version)
dnvm use default
Get-ChildItem -Path $PSScriptRoot\test -Filter project.json -Recurse |% { 
	& dnu restore $_.FullName 2>1
	& dnx -p $_.FullName test -xml "TEST-$version.xml"
}

Publish

The final build step added to the build definition in VSO was to package the output of dnu publish into a Zip file suitable for deployment with the Publish-AzureWebSiteProject cmdlet. Another PowerShell script calling out to the .Net System.IO.ZipFile class checked into source control is run by a PowerShell build step, passing in the path to the publish folder as the source parameter.

Param([string]$source, [string]$destination)
Write-Host "Zipping $source to $destination"
if ( Test-Path $destination ) {
    Write-Host "Deleting existing destination file $destination"
    Remove-Item $destination
}

Add-Type -AssemblyName "System.IO.Compression.FileSystem"
[IO.Compression.ZipFile]::CreateFromDirectory($source, $destination)

Release

Deploy

With a VSO Release vNext definition created and linked to the build definition set up above, we automatically have access to the Zip file artifact containing the output of dnu publish. Passing this file to the Publish-AzureWebSiteProject cmdlet by adding an Azure Web App Deployment task handles connecting to our Azure subscription, uploading the package to the web app and unzipping. But accessing the website results in

You do not have permission to view this directory or page.

Using the excellent Kudu diagnostic console (yourwebsite.scm.azurewebsites.net) we can look at the folder layout and spot our prolem.

D:\home>tree \A
Folder PATH listing for volume Windows
Volume serial number is 002FEB7C FEFD:D936
D:.
+---data
|   \---aspnet
|       \---CompilationSnapshots
+---LogFiles
|   \---kudu
|       +---deployment
|       \---trace
\---site
    +---deployments
    |   \---tools
    +---diagnostics
    +---locks
    \---wwwroot
        +---approot
        |   +---packages
        |   +---runtimes
        |   \---src
        |       +---MyProject.Web
        |           +---App_Data
        |           +---Controllers
        |           +---Models
        |           +---Properties
        |           \---Views
        |               +---Home
        |               \---Shared
        \---wwwroot
            +---css
            +---img
            +---js
            \---lib

dnu publish packs up the web application with web assets and a web.config configuring the httpPlatformHandler in a wwwroot folder, and all runtimes, packages and assemblies in an approot folder. The problem? Publish-AzureWebSiteProject uploads and extracts our Zipped artifact under /site/wwwroot folder of the site, not directly into /site as we need.

Reconfigure

So how to resolve this? I started digging around in the source code for Publish-AzureWebSiteProject to see if there might be a configuration option but without any joy. So the next step was to try reconfiguring the web app configuration. This SO thread pointed to this being a working solution and a quick test by changing the configuration in the Azure Preview Portal proved it worked.

Virtual Directory Config

I wasn’t keen on leaving this as a pre-requisite of the site configuration for the deployment to work, especially as our reason for using VSO Release is to be able to deploy to multiple environments. A bit of hunting around found David Ebbo explaining how to use Azure Resource Management PowerShell to script this sort of update. An Azure PowerShell script task in VSO makes sure this is done with each deployment. This isn’t entirely as straightforward as it might sound given that the underlying ARM REST API doesn’t support management certificate authentication, so we need to use an AD Service User with the PowerShell commands.

Param(
  [string]$user, 
  [string]$password, 
  [string]$resourceGroupName, 
  [string]$siteName
)
$securepassword = ConvertTo-SecureString -String $apssword -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($user, $securepassword)
Add-AzureAccount -Credential $cred

Switch-AzureMode -Name AzureResourceManager

$PropertiesObject = @{
	"virtualApplications" = @(
        @{
            "virtualPath" = "/";
            "physicalPath" = "site\wwwroot\wwwroot";
        }
    )
}
Set-AzureResource -PropertyObject $PropertiesObject -ResourceGroupName $resourceGroupName -ResourceType Microsoft.Web/sites/config -ResourceName "$siteName/web" -OutputObjectFormat New -ApiVersion 2015-08-01 -Force

Conclusion

The documentation was mostly there for us to implement our automated build strategy with just a few minor pieces of the puzzle missing, mainly around running unit tests. Guidance on how to use VSO Release vNext to manage releasing an ASP.Net 5 application to Azure was tricky to come by, but a bit of trial and error has got us there. Hopefully this blog post will help anyone else that finds themselves facing similar challengs.

Modifying the virtual directory configuration of the web app feels dirty but I’m hoping this is a temporary fix until Publish-AzureWebSiteProject or similar has better support for uploading the package into /site instead of /site/wwwroot. If it does prove to be a longer term solution, then as our solution matures to the level where we have an ARM template for our Azure architecture then we should be able to incorporate the virtual directory configuration into the web app definition and not require the separate PowerShell step.