Using XPath to set environment variables in ServiceDefinition.csdef

In my last post on zipping IIS log files in Windows Azure, my ServiceDefinition.csdef file included the following code to set an environment variable to the value of a local resource path:

 <Task commandLine="Startup\ScheduleLogFileZipAndDeleteTask.cmd" executionContext="elevated" taskType="simple">
  <Environment>
    <Variable name="ZippedLogFilesPath">
      <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='ZippedLogFiles']/@path" />
    </Variable>
  </Environment>
</Task>

As you can see, an XPath query is used to retrieve the resource path. Someone asked me what this XPath query refers to. I didn’t know, so I looked into it.

There is a page in the Windows Azure documentation which talks about this feature, and gives a whole bunch of examples of values you can pull out using different XPath queries. You can use this to set environment variables either for a specific startup task (specifying it under the <Startup> element), or for the role entry point code (specifying it under the <Runtime> element). However the documentation still doesn’t explain what XML document is being queried, so you can’t tell what other information may be available to you.

After a bit of poking around, I found the mysterious document. It only seems to be created if you include an xpath element somewhere in your ServiceDefinition.csdef file, so you can add a dummy environment variable like this to ensure the file is created:

 <Runtime>
  <Environment>
    <Variable name="TestIsEmulated">
      <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated"/>
    </Variable>
  </Environment>
</Runtime>

Now when you deploy the solution (either to Windows Azure or to the local compute emulator), you’ll get a file with a filename similar to DeploymentId.DeploymentId.RoleInstanceName.RoleEnvironment.1.xml. On a real Windows Azure VM, this file is in C:\Config; in the local compute emulator it is more hidden, living at C:\Users\username\AppData\Local\dftmp\Config. (Don’t confuse this file with another one in the same folder that doesn’t end with RoleEnvironment.1, as this file has a different schema and data, and as far as I know can’t be queried in the same way). In both Windows Azure and the emulator, the role environment XML file should have the same format – here’s an example from my IIS log zipping app:

 <?xml version="1.0" encoding="utf-8"?>
<RoleEnvironment xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <Deployment id="046cfa59b8894be3b0ca5f0c6aaeb237" emulated="false" />
  <CurrentInstance id="WebRole1_IN_0" roleName="WebRole1" faultDomain="0" updateDomain="0">
    <ConfigurationSettings>
      <ConfigurationSetting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=xxxx;AccountKey=xxxxx" />
      <ConfigurationSetting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="xxxx" />
      <ConfigurationSetting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="2014-05-08T23:59:59.0000000+10:00" />
      <ConfigurationSetting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="remoteuser" />
      <ConfigurationSetting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="true" />
      <ConfigurationSetting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" />
    </ConfigurationSettings>
    <LocalResources>
      <LocalResource name="DiagnosticStore" path="C:\Resources\directory\046cfa59b8894be3b0ca5f0c6aaeb237.WebRole1.DiagnosticStore\" sizeInMB="4096" />
      <LocalResource name="ZippedLogFiles" path="C:\Resources\directory\046cfa59b8894be3b0ca5f0c6aaeb237.WebRole1.ZippedLogFiles\" sizeInMB="1000" />
    </LocalResources>
    <Endpoints>
      <Endpoint name="Endpoint1" address="10.78.196.62" port="80" publicPort="80" protocol="http" />
      <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" address="10.78.196.62" port="3389" publicPort="0" protocol="tcp" />
      <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput" address="10.78.196.62" port="20000" publicPort="3389" protocol="tcp" />
    </Endpoints>
  </CurrentInstance>
  <Roles />
</RoleEnvironment>

There’s not a whole lot in there that isn’t alluded to in the documentation, but it’s always nice to know exactly what it is that you’re dealing with.