Having different VM sizes per environment for Azure Cloud Service roles

I’m working on a project running on Azure, with the project being deployed as a Cloud Service.

In this cloud service is:

One little annoyance I’ve had with Azure Cloud Services for a while was the lack of a(n easy) way to run different VM sizes for the roles for different environments, as I want to save money when my test environments are running.

In production, I want a more beefy VM to handle a higher number of concurrent users than in test environments. Sure you can scale the number of instances per role, but I wanted to change instance size.

I wanted to have the following VM sizes for the roles…


Test Environments (e.g. UAT, RC etc):

I found this page after some Googling, and there was clearly some hope.

After following the instructions in the post above (and a related post I found here), it wasn’t working for me. According to the author of that post, this approach was still working with Azure SDK v2.9, which is the same version I’m running - so it should work.

Not knowing MSBuild amazingly well, it took me a while to find an alternative approach. I ended up with my cloud service .ccproj file looking like this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 ... (removed the non-interesting parts for brevity) ...
  <PropertyGroup Condition=" '$(Configuration)' == 'Production' ">
  <PropertyGroup Condition=" '$(Configuration)' == 'UAT' ">
  <PropertyGroup Condition=" '$(Configuration)' == 'ReleaseCandidate' ">
  <PropertyGroup Condition=" '$(Configuration)' == 'UAT2' ">
  <!-- Define extra Service Definition files - to allow us to have different VM sizes in different environments (http://fabriccontroller.net/apply-xdt-transforms-to-your-servicedefinition-csdef-file/). -->
    <Content Include="ServiceDefinition.UAT.csdef" />
    <Content Include="ServiceDefinition.UAT2.csdef" />
    <Content Include="ServiceDefinition.ReleaseCandidate.csdef" />
  <Target Name="TransformCsdef" AfterTargets="CopyServiceModel" Condition="Exists($(ServiceDefinitionTransform)) And '$(Configuration)' != 'Production'">
    <Message Text="Running CSDEF transforms (TargetProfile=$(Configuration))" Importance="High" />
    <Exec Command="&quot;$(TransformRunnerExecutable)&quot; @(TargetServiceDefinition) $(ServiceDefinitionTransform) @(TargetServiceDefinition)" />
  <Import Project="$(CloudExtensionsDir)Microsoft.WindowsAzure.targets" />

Notice that it targets the CopyServiceModel Target, so runs after the ServiceDefinition file has been copied to the output. It then runs the XML transform, and you end up with the .csdef file in the desired state with the vmsize attributes changed depending on your environment.

Here’s an example of one of the XML transform files:

<?xml version="1.0"?>
<ServiceDefinition  xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Locator="Condition(@name!='' and @schemaVersion!='')">  
  <WebRole name="MyProj.Admin.UI" vmsize="Small" xdt:Transform="SetAttributes" xdt:Locator="Match(name)"></WebRole>
  <WebRole name="MyProj.Frontend.UI" vmsize="Medium" xdt:Transform="SetAttributes" xdt:Locator="Match(name)"></WebRole>
  <WorkerRole name="MyProj.WorkerRole" vmsize="ExtraSmall" xdt:Transform="SetAttributes" xdt:Locator="Match(name)"></WorkerRole>

Hopefully this helps somebody else who stumbles on those posts, and experiences the same problems that I did.

Thanks to Rob Head for his original post and letting me know it’s still possible with Azure SDK v2.9!


Now read this

Musings on Polyglot Programming

Looking from the outside, I’m verging on being a Microsoft fan boy (* ducks and hides), though I constantly fight the battle to only use what I’m comfortable with. .NET makes development easy (in my view), and it can be difficult to... Continue →