Windows Azure Web Sites Programmatic Web Deploy

I was setting up a continuous integration / deployment pipeline last night for my team at GovHack Perth and I had a lot of trouble getting the web deploy deployment working against an Azure Web Sites site.

I had downloaded the .publishsettings file and copied out the username, password and server to pass through to the .cmd file that is generated by MSBuild when creating a web deploy package. I do this because I create the package separately as part of the CI build and attach it as a build artifact and then separately do the deployment with msdeploy as another CI build step.

The problem I came across was that I kept getting the following error when running the deployment:

C:\Path\To\Project\obj\Release\Package>projectname.deploy.cmd /T /M:https://waws-prod-hk1-001.pu
blish.azurewebsites.windows.net:443/msdeploy.axd /U:$sitename /P:password /A:Basic
SetParameters from:
"C:\Path\To\Project\obj\Release\Package\projectname.SetParameters.xml"
You can change IIS Application Name, Physical path, connectionString
or other deploy parameters in the above file.
-------------------------------------------------------
Start executing msdeploy.exe
-------------------------------------------------------
"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -source:package='C:
\Path\To\Project\obj\Release\Package\projectname.zip' -dest:auto,computerName="https://waws-prod
-hk1-001.publish.azurewebsites.windows.net:443/msdeploy.axd",userName="$sitename"
,password="password",authtyp
e="Basic",includeAcls="False" -verb:sync -disableLink:AppPoolExtension -disableL
ink:ContentExtension -disableLink:CertificateExtension -setParamFile:"C:\Path\To\Project\obj
\Release\Package\projectname.SetParameters.xml" -whatif
Info: Using ID '744a18b1-51c2-4d05-a674-6c407a7f80c5' for connections to the rem
ote server.
Error Code: ERROR_USER_UNAUTHORIZED
More Information: Connected to the remote computer ("waws-prod-hk1-001.publish.a
zurewebsites.windows.net") using the Web Management Service, but could not autho
rize. Make sure that you are using the correct user name and password, that the
site you are connecting to exists, and that the credentials represent a user who
has permissions to access the site. Learn more at: http://go.microsoft.com/fwl
ink/?LinkId=221672#ERROR_USER_UNAUTHORIZED.
Error: The remote server returned an error: (401) Unauthorized.
Error count: 1.

It turns out the problem was I needed to include the ?site=sitename parameter in the msdeploy destination otherwise (I assume) the load balancer on the publish server can’t authenticate your username and password properly. This meant I needed to switch to using msdeploy.exe directly like so:

"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -source:package='projectname.zip' -dest:auto,computerName="https://waws-prod-hk1-001.publish.azurewebsites.windows.net:443/msdeploy.axd?site=sitename",userName="$sitename",password="password",authtype="Basic",includeAcls="False" -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile:"projectname.SetParameters.xml"

Hopefully this post helps other people stuck in the same situation (I couldn’t find anything on Google to help with this)!

Running AspNetCompiler after creating web deploy package using VisualStudio 2012 with round-tripping to 2010

I’ve previously blogged (as a side-note of an unrelated problem I was having) the approach that I use to overcome the problem that the AspNetCompiler has when you have the extraneous web.config files in the obj folder after creating a web deploy package.

The usual way you will come across this problem is running MvcBuildViews in a subsequent build after creating the package. The solution that I previously posted included a few MSBuild targets that can be called prior to running MvcBuildViews to clean up the web deploy files.

If you have tried using these targets after upgrading your project to Visual Studio 2012 then you will have noticed that some of the targets no longer work because they have been renamed. The following code is what I now use and it is compatible with the Visual Studio 2010 round-tripping support.

  <Target Name="CleanWebDeploy" BeforeTargets="MvcBuildViews" Condition="'$(MvcBuildViews)'=='true'">
    <Message Text="Running Clean Web Deploy Target" />
    <CallTarget Targets="CleanWebsitesPackage" />
    <CallTarget Targets="CleanWebsitesPackageTempDir" Condition="'$(VisualStudioVersion)'=='10.0'" />
    <CallTarget Targets="CleanWebsitesTransformParametersFiles" Condition="'$(VisualStudioVersion)'=='10.0'" />
    <CallTarget Targets="CleanWebsitesWPPAllFilesInSingleFolder" Condition="'$(VisualStudioVersion)'!='10.0'" />
    <CallTarget Targets="CleanWebPublishPipelineIntermediateOutput" Condition="'$(VisualStudioVersion)'!='10.0'" />
  </Target>

Web Deploy Package Connection String Parameterisation Problem

I recently came across a problem using web deploy packages, and more specifically with connection string parameterisation. I was generating a web deploy package using the “Package” MSBuild command and then deploying to a remote server using MSDeploy. The problem I came across was that the parameterised connection strings weren’t being replaced.

That resulted in the deployed web.config file looking something like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  ...
  <connectionStrings>
      <add name="SomeConnectionString" connectionString="$(ReplacableToken_SomeConnectionString-Web.config Connection String_0)" />
  </connectionStrings>
  ...
</configuration>

I used a variety of approaches – DeployOnBuild MSBuild command, Visual Studio GUI interface, the generated .cmd file and direct msdeploy call – to deploy the package to check whether the problem was simply that I was somehow deploying it incorrectly, but they all resulted in the same problem.

Most of the Google results that came up simply said to turn off parameterisation, but in this instance I wanted to use parameterisation. While I was using XDT transformations, I also wanted to use connection string parameterisation so I could substitute the connection strings for entries in a password safe as part of an automated deployment step.

The build output wasn’t terribly helpful, so I passed the verbose option to msdeploy and got the following interesting output:

...
[16:08:33][Step 2/2] Verbose: Parameter entry 'SomeConnectionString-Web.config Connection String/1' could not be applied anywhere.
[16:08:33][Step 2/2] Verbose: The dependency check 'DependencyCheckInUse' found no issues.
[16:08:33][Step 2/2] Verbose: The synchronization completed in 2 pass(es).
[16:08:33][Step 2/2] Total changes: 10 (0 added, 0 deleted, 10 updated, 0 parameters changed, 1004778 bytes copied)
[16:08:33][Step 2/2] Process exited with code 0

This didn’t really make a huge amount of sense and after some sanity checking to make sure parameterisation does actually work (created a new ASP.NET website and deployed it successfully) I started comparing all the differences in my .csproj configuration to a blank project. After some tedious trial and error I managed to narrow the errant configuration to the following (seemingly) innocuous line:

<BaseIntermediateOutputPath>$(ProjectDir)..obj</BaseIntermediateOutputPath>

You might be asking why I had that line in there in the first place? It is certainly a bit weird and you would normally expect it to be set to “obj”. In this case it was there so that when MvcBuildViews was next executed after building a package it didn’t choke with the following error (on the intermediate web.config files in the obj folder):

It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level.  This error can be caused by a virtual directory not being configured as an application in IIS.

When I initially came across that problem I changed the location of the obj folder since it seemed the best option compared to the other options such as running clean before all builds or deleting the obj folder, both of which would slow down the build and be tedious and annoying. Also, I didn’t want to turn MvcBuildViews off since I want assurance on the build server that the views all compiled fine.

Since this was no longer an option (up until this point all web deploys were done manually so we hadn’t actually come across this new problem!) I came up with the following configuration as a solution (props to my Senior Dev, Matt Davies, for finding the relevant StackOverflow post for the inspiration):

  <Target Name="CleanPackage" BeforeTargets="MvcBuildViews" Condition="'$(MvcBuildViews)'=='true'">
    <CallTarget Targets="CleanWebsitesPackage" />
    <CallTarget Targets="CleanWebsitesPackageTempDir" />
    <CallTarget Targets="CleanWebsitesTransformParametersFiles" />
  </Target>
  <Target Name="MvcBuildViews" AfterTargets="Build" Condition="'$(MvcBuildViews)'=='true'">
    <AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
  </Target>

Basically, if you have MvcBuildViews set to true then it will run MvcBuildViews directly after the build and before running MvcBuildViews it will run the CleanWebsitesPackage, CleanWebsitesPackageTempDir and CleanWebsitesTransformParametersFiles targets which clean up the web deploy package files and thus the web.config files that mess with MvcBuildViews.

Update (17 Feb 2013)

If you are trying to use the above MSBuild with Visual Studio 2012 then it won’t work. I have blogged a solution that works in both Visual Studio 2012 and Visual Studio 2010 though.