When having multiple environments in Azure for WebRoles or WorkerRoles, it might not be neccesary to have the same performance everywhere. A testing environment, for example, usually has to handle a lot less work than an acceptance or production environment. Scaling this down can save a lot of money. One way to scale down is decreasing the amount of instances or undeploying the services outside of office hours. An additional method would be to lower the instance size. Whereas the number of instances can easily be modified in the .cscfg file for a cloud service, the instance size cannot be easily changed per environment as it is stored in the .csdef file.

Whatever your reason to change the .csdef per environment, the default Azure SDK does not facilitate this behavior. It’s pretty easy though.

ServiceDefinitionLocator.targets

Create a new textfile in the root of your solution and call it ServiceDefinitionLocator.targets. Its content should be:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- 
    Martijn Stolk - ICT Automatisering 
    Use 'ServiceDefinition.[PROFILE].csdef' if it exists. Otherwise fall back to the default 'ServiceDefinition.csdef'.
    Note that this does not use the @ServiceDefinition entries. The file only needs to exist on the file system.
  -->
  <Target Name="ResolveServiceDefinition">

    <Message Text="[ICT] Target Profile: $(TargetProfile)" />
    <Message Text="[ICT] Service Definition: @(ServiceDefinition)" />
    <Message Condition="Exists('ServiceDefinition.$(TargetProfile).csdef')" Text="[ICT] Found ServiceDefinition for profile $(TargetProfile)" />
    <Message Condition="!Exists('ServiceDefinition.$(TargetProfile).csdef')" Text="[ICT] Could not find ServiceDefinition for profile $(TargetProfile)" />

    <ItemGroup>

      <SourceServiceDefinition Condition="Exists('ServiceDefinition.$(TargetProfile).csdef')" Include="ServiceDefinition.$(TargetProfile).csdef" />
      <SourceServiceDefinition Condition="!Exists('ServiceDefinition.$(TargetProfile).csdef')" Include="ServiceDefinition.csdef" />

      <!-- The target service definition is in the output directory. -->
      <TargetServiceDefinition Include="@(SourceServiceDefinition->'$(OutDir)%(Filename)%(Extension)')" />

    </ItemGroup>

    <Message Text="[ICT] Source Service Definition: @(SourceServiceDefinition)" />
    <Message Text="[ICT] Target Service Definition: @(TargetServiceDefinition)" />
  </Target>
</Project>

Environment-specific ServiceDefinition files

Use the Manage Configuration option to create the configurations for which you also want different .csdef files. Don’t worry if you have more configurations than the amount of .csdef files you want. There’ll be a fallback to the default ServiceDefinintion.csdef.

Manage Configurations

Now we’ll need to create the ServiceDefinition files we want.

The Azure SDK doesn’t like multiple .csdef files, so you can’t copy/paste them in VS2015 itself. Open your project folder with explorer and make the neccesary copies of your ServiceDefinition file. In our example we’ll create three: ServiceDefinition.T.csdef, ServiceDefinition.A.csdef and ServiceDefinition.P.csdef.

ServiceDefinition files

We’ll add those files to the project in the next step.

Cloud project file

Unload and edit the cloud project file (.ccproj) and add the following line at the bottom, just above the “ end tag.

  <Import Project="$(SolutionDir)\ServiceDefinitionLocator.targets" />

While you’re here, add the copies of the ServiceDefinition to the project. There can only be one ServiceDefinition, so the copies will have to be created as Content type.

  <ItemGroup>
    <ServiceDefinition Include="ServiceDefinition.csdef" />
    <Content Include="ServiceDefinition.T.csdef" />
    <Content Include="ServiceDefinition.A.csdef" />
    <Content Include="ServiceDefinition.P.csdef" />
    <ServiceConfiguration Include="ServiceConfiguration.T.cscfg" />
    <ServiceConfiguration Include="ServiceConfiguration.A.cscfg" />
    <ServiceConfiguration Include="ServiceConfiguration.P.cscfg" />
    <ServiceConfiguration Include="ServiceConfiguration.Local.cscfg" />
    <ServiceConfiguration Include="ServiceConfiguration.Cloud.cscfg" />
  </ItemGroup>

Reload the project file. You’ll see the ServiceDefinition files there.

Result after csproj change

Modify csdef files

You can now customize the csdef files for each environment. Take note that:

  • you’ll need to manually keep the environment-specific copies of the .csdef files up to date. Adding new configuration settings won’t automatically update your copies. The same counts for tasks, endpoints, etc.
  • you’ll need to create a package, or publish the project, while the correct Service Configuration is selected.

Package application

Or, from the msbuild command line, you’ll have to add the /p:TargetProfile=T parameter to your publish/package command.

Advertisements