How I got .Net 4.5 RC Running in a Windows Azure WebRole

UPDATE (28 Sep 2012):  Now Windows Azure supports .Net 4.5 officially and the dev tools are fixed with the release of the Azure SDK 1.8 this post largely deprecated.

However, if you get the following message when you try to deploy for the first time after upgrading your local build;

The feature named NetFx45 that is required by the uploaded package is not available in the OS * chosen for the deployment.

You’ll need to upgrade your CSFG with the osFamily from 2 to 3;

<code ServiceConfiguration ... osFamily="3"/>

UPDATE (7 Sep 2012): I’ve since wrapped this in a plugin and I’m now using it for .Net 4.5 RTM (includes MVC web apps, naturally) with the Azure SDK and Tools for Visual Studio 1.7 SP1 

Initially I was very excited when I saw this post which seemed to suggest that the 1.7 release of the Azure SDK would now allow me to build solutions in .Net 4.5 and Visual Studio 2012 RC.

Initially I was very excited when I saw this post which seemed to suggest that the 1.7 release of the Azure SDK would now allow me to build solutions in .Net 4.5 and Visual Studio 2012 RC.

Unfortunately, like many other announcements it was very misleading. It does NOT work out of the box. If you look carefully at the pictures, this post, like many others very subtly chooses [.Net Framework 4.0] and NOT 4.5 as the title would suggest. The comments on this post suggest there may be workarounds but I couldn’t find a single explanation for “power users” of how to “work around the blockers”. “How hard could these workarounds be to find?”, I asked myself. Well it turns out, that although the solution isn’t that complicated, the effort involved in hunting down the stoppers was way more of a time-sink than I’d have liked. If you’d like to save yourself days of pain, read on.

Blocker #1 – No .Net 4.5 Runtime on the Azure Images

So this one is pretty easy to solve once you know how to build a Windows Azure Startup Task but I chose to build mine in the end using the semi-official/semi-documented plugins feature. I started with an Azure Plugins Library as a base but wrote my own in the end. Startup Tasks have to be idempotent and error free. Any deviation from that and your role will hang seemingly indefinitely as it tries to recover running the scripts over and over again. a task like installing the .Net runtime for instance isn’t something you want to repeat endlessly because you returned a non-zero error code from your command script. By default, plugins for the 1.7 Azure SDK go here;

%ProgramFiles%\Microsoft SDKs\Windows Azure\.NET SDK\2012-06\bin\plugins

I used a junction (mklink /j) to my developer source directory under which is under git control to develop my plugin, which looks like this;


I’d read Magnus’ post about upgrading the Framework on the Azure instance and although he said he’d had trouble getting the web installer to run I didn’t fancy putting the whole 50MB installer into my .cspkg and putting it into blob storage seemed liked overkill, so the file you see above (although named ‘full’) is actually the web installer.

The csplugin simply looks like this;

<?xmlversion="1.0" ?>
    <TaskcommandLine="baseLineUpgrade.cmd"executionContext="elevated"taskType="simple" />

It runs as a “Simple” task so that it’s synchronous and the role won’t continue to setup until this task completes successfully (returns ERRORLEVEL==0 from baseLineUpgrade.cmd). Here’s that cmd file influenced by various posts in the comment fields;

@echo off
echo :start****************************************  >> baseLineUpgrade.txt
time /t >> baseLineUpgrade.txt
echo **********************************************  >> baseLineUpgrade.txt
echo REM Install .Net 4.5 RC : WARNING - this does take several minutes of startup time and then reboots >> baseLineUpgrade.txt


echo :dotNetFxCheck >> baseLineUpgrade.txt
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs\.NETFramework,Version=v4.5" >> baseLineUpgrade.txt
if %ERRORLEVEL%x==0x goto dotNetFx45_installed
echo Need to install NetFX45RC >> baseLineUpgrade.txt

echo dotNetFxRunning=%dotNetFxRUNNING% >> baseLineUpgrade.txt
if  dotNetFx_RUNNINGx==truex goto skip_dotNetFx45
set dotNetFx_RUNNING=true
echo set dotNetFx_RUNNING=true >> baseLineUpgrade.txt

echo REM Change the location of App Data for running 32 bit install tasks on Win64 (downloading installers like WebPI have trouble with this otherwise) >> baseLineUpgrade.txt
@echo on
md "%~dp0appdata"
reg add "hku\.default\software\microsoft\windows\currentversion\explorer\user shell folders" /v "Local AppData" /t REG_EXPAND_SZ /d "%~dp0appdata" /f  >> baseLineUpgrade.txt
@echo off

echo REM Run FULL setup if we have it, otherwise WEB setup - see flags /passive vs /q >> baseLineUpgrade.txt
REM Could also use WebPI Command Line tool
if     exist .\dotNetFx45_Full_x86_x64.exe echo Running FULL installer... >> baseLineUpgrade.txt
if not exist .\dotNetFx45_Full_x86_x64.exe echo Running WEB  installer... >> baseLineUpgrade.txt
if     exist .\dotNetFx45_Full_x86_x64.exe start /wait .\dotNetFx45_Full_x86_x64.exe /q /serialdownload /log "%~dp0appdatadotNetFx45_setup.log"
if not exist .\dotNetFx45_Full_x86_x64.exe start /wait .\dotNetFx45_Full_setup.exe /q /serialdownload /log "%~dp0appdatadotNetFx45_setup.log"

echo REM Restore Local AppData >> baseLineUpgrade.txt
reg add "hku\.default\software\microsoft\windows\currentversion\explorer\user shell folders" /v "Local AppData" /t REG_EXPAND_SZ /d %%USERPROFILE%%\AppData\Local /f  >> baseLineUpgrade.txt

REM no need to set dotNetFx_RUNNING=false
goto doesntAppearToNeedReboot
echo REBOOT ***************************************************  >> baseLineUpgrade.txt
REM shutdown /r /t 0 >> baseLineUpgrade.txt

echo :dotNetFx45_installed >> baseLineUpgrade.txt
echo :skip_dotNetFx45 >> baseLineUpgrade.txt


echo :exit >> baseLineUpgrade.txt

Notice there’s plenty of logging (which you need) and the script should defend itself both against being executed more than once and unnecessarily.

You’ll need add a “Cloud Project” to your solution, for which you need to have installed WindowsAzureTools.vs110 for Visual Studio on top of the 1.7 SDK.

To include this plugin, add an Import section to your ServiceDefinition.csdef as follows;

       <!-- omitted stuff -->
       <!--omitted stuff -->
      <ImportmoduleName="NetFX45RC" />
       <!-- omitted stuff –>

Blocker #2 – Visual Studio Refuses to Build a Cloud Project that contains .Net v4.5 Code

So now you have created your cloud project, add in your web project and build and you’ll get this;

Windows Azure Cloud Service projects currently support roles that run on .NET Framework version 3.5 and 4.  Please set the Target Framework property in the project settings for project ‘MyProj.www’ to .NET Framework 3.5 or .NET Framework 4.

Thanks a lot VS! Well after much deliberation, I decided to fix this by patching the targets in the 1.7 SDK;

c:\> notepad %ProgramFiles(x86)%\MSBuild\Microsoft\VisualStudio\v11.0\Windows Azure Tools\1.7\Microsoft.WindowsAzure.targets

And edited line 1784 from this;

  Condition="$(_RoleTargetFramework) == 'v3.5' Or $(_RoleTargetFramework.StartsWith('v4.0'))">True</_IsValidRoleTargetFramework>

to this;

Condition="$(_RoleTargetFramework) == 'v3.5' Or $(_RoleTargetFramework.StartsWith('v4'))">True</_IsValidRoleTargetFramework>

OK. Now it will build and package. The trouble is, it creates a broken package. And you find that your role just won’t start. It turns out this is for two reasons; Firstly, Visual Studio packages up a waIISHost.exe.config file with a missing version number whereas you actually want one that looks like this;

    <supportedRuntimeversion="v4.0"sku=".NETFramework,Version=v4.5" />
    <NetFx40_LegacySecurityPolicyenabled="false" />

It also packs a RoleModel.xml which contains an incorrect version number pointing to v3.5. Both of these cause the role to fail. It should look like this;

        <NetFxEntryPointassemblyName="MyProj.www.dll"targetFrameworkVersion="v4.5" />

But due to some trigger I never fully resolved, VS always sets this to v3.5 as soon as you try to build a v4.5 package. Yes, you read that right, it doesn’t even set it to 4.0, it falls back to some default when it can’t figure out what to do.

I couldn’t for the life of me find out how to get Visual Studio to do the right thing here. As soon as you use a v4.5 based role VS just creates a bad package. This is presumably another reason why they didn’t ship with this feature in the RC.

Workaround #1 – using csPack
You can bypass Visual Studio’s packaging with csPack, but along with having two build scripts now, you’ll also need to jump through those other hoops of specifying a physical folder for the web project on the command line AND in your ServiceDefinition.csdef;

Packaging with csPack.exe
cspack \vs11Projects\MyProj\Azure11\Azure2012\ServiceDefinition.csdef         /out:\vs11Projects\MyProj\Azure11\Azure2012\bin\Release\app.publish\MyProjDotCom.cspkg /role:MyProj.www;\vs11Projects\MyProj\www\obj\Release\AspnetCompileMerge\Source /rolePropertiesFile:MyProj.www;\vs11Projects\MyProj\AzureCommandLine\roleProperties.txt

WARNING: Make sure the physical directory you specify is NOT your project folder. It must be a published folder (I pointed mine at the pre-compiled folder) otherwise you’ll ship all your source code and developer web.configs in the package instead of the properly built web site.

The second piece of magic here is to use a roleProperties.txt file thus;


Workaround #2 – using Visual Studio
Now don’t get me wrong, I’m all for scripted builds, but I’d rather be using msbuild over my Visual Studio project file than a completely separate build script which could easily become out of sync with my project. Just so you know, I’m not proud of this next kludge, but at least it produces consistent builds that deploy and start up. As I said, I couldn’t for the life of me work out how to get VS to package up the correct files for a v4.5 role so since I was already patching the Azure Instance, so I decided to do the same for these glitches which I assume will get patched by the Azure team later.

I simply ship a correct version of the waIISHost.exe.config file with my WebRole project (remembering to set the file to ‘Copy Always’);


And then it’s simply a matter of running the other file copyWaIIShost.cmd;

copy Installation\waIISHost.exe.config \base\x64\. /Y >> Installation\copywaIIShost.txt

…and call it from your ServiceDefinition.csfg with;

      <TaskexecutionContext="elevated"taskType ="background"commandLine ="Installation\copyWaIIShost.cmd"/>

I chose to keep this kludge with the project rather than make a plugin, but either would work. Note that plugins will execute from a dynamically attached disk (initially E: but will change) in E:\plugins\<yourPluginName> whereas startup tasks will run from E:\AppRoot\bin and in my case my files will copy to E:\AppRoot\bin\Installation, but keep in mind the current path will still be E:\AppRoot\bin, so my copy command needs to reference the Installation path as well.

Blocker #3 – IIS AppPools Incorrectly configured

So when deploying to Azure this time, it’s only now that the machine is configured and the role starts that the ASP.Net site itself still fails to load. In fact for an MVC project you’ll probably get this;


Basically, routing isn’t working, because the IISConfigurator.exe or whatever calls it can’t figure out how to configure a v4.5 site and so chooses the default, which is v2.0 and a non-integrated pipeline. It’s possible that somewhere above I’ve made too many 4.5 changes and somewhere it would have been happy (since v4.5 is an in-place upgrade to v4.0) with a v4.0 framework tag, but I don’t know where.

Again, I spent hours spelunking through the Microsoft.WindowsAzure.ServiceRuntime code with ILSpy but couldn’t quite figure out where it was failing or what would be the trigger to get it to configure the site as it would for a v4.0 project. However, at this point, having spent way too much time on the problem, I was getting quite comfortable with my kludges, so I added this to the compWaIIShost.cmd file;

%windir%\system32\inetsrv\appcmd set config -section:applicationPools
-applicationPoolDefaults.managedPipelineMode:Integrated >> appcmd.txt

Basically, I changed the defaults for newly configured application pools. To my astonishment (and immense relief) two pieces of luck were on my side. Firstly, my task seemed to run before it tried to configure the site, and secondly, it actually picked up the default. I didn’t have high hopes on that second point from what I was inferring in the ServiceRuntime source which appeared to be hard coded to a V2.0 default, but fortunately, it clearly wasn’t running that line of code.


My roles now start. I can consistently, repeatedly and reliably deploy new packages when I need to, at least until the Azure team fix up the Visual Studio extensions, then I can remove the kludges. But for now, I’m happy and can move on. If you’ve felt the need to read this far, I imagine you have been in a similar position recently so I hope this post has helped you move on too.

Similarly, if somebody can see where I’ve gone wrong and can avoid Blocker #3 altogether I’d be very grateful of a pointer.