Windows Azure Start-up Tasks Part 2

In the previous post “Windows Azure Startup Tasks Part 1” we learned about the basics of start-up tasks and how they can enable you to realize solutions that may otherwise have been outside of the scope of PaaS.  This powerful feature offers some additional functionality that we will cover in this post.

Towards the end of this post we will discuss some of the lessons I have learned when working with Start-up tasks easing your adoption of them.

Environment Variables
While the start-up tasks are executing they have access to environment variables that define various characteristics of the instance.  The predefined variables are:

  • @AccountUsername: the remote desktop account user name
  • @AccountEncryptedPassword: the remote desktop account’s encrypted password
  • @AccountExpiration: the remote desktop account’s expiration date
  • @ConnectionString: the default diagnostics connection string
  • DiagnosticStore: the local directory where diagnostic information is stored
  • RdRoleId: the internal identifier azure uses to represent the current role
  • RdRoleRoot: the root path of the drive that contains the application code
  • RoleDeploymentId: the identifier that represents the current deployment
  • RoleInstanceId: the identifier that represents the current instance
  • RoleName: the name of the role that the currently executing instance is based on
  • RoleRoot: the volume identifier for the drive that contains the application code
  • WaRoleType:  a string that indicates the type of role the currently executing instance is based on (ex. Web Role)

In some cases you may have a need to set an environment variable to be used in your start-up tasks.   This can be done by adding a bit more XML.  To add a new environment variable for the startup.cmd script to use named TaskName I simply add it to the service definition file as you see below.

 

In order to get access to more specific instance and environment details you can leverage XPATH queries against Worker Role Schema (https://msdn.microsoft.com/en-us/library/windowsazure/gg557552.aspx) or the Web Role Schema (https://msdn.microsoft.com/en-us/library/windowsazure/gg557553.aspx).  In the following example an XPATH is used to determine if the task is running in the emulator.

 
XPATH based environment variables that are typically of interest include

  • /RoleEnvironment/Deployment/@emulated 
    • Indicates whether the script is running in an emulated environment
  • /RoleEnvironment/Deployment/@id
    • The deployment id of the current instance
  • /RoleEnvironment/CurrentInstance/@id
    • The id of the current instance
  • /RoleEnvironment/CurrentInstance/@updateDomain
    • The number of the current instances update domain
  • /RoleEnvironment/CurrentInstance/@faultDomain
    • The fault domain the current instance is part of
  • /RoleEnvironment/CurrentInstance/@roleName
    • The name of the current role
  • /RoleEnvironment/CurrentInstance/ConfigurationSettings/ConfigurationSetting[@name='Setting1']/@value 
    • Gets the value of a RoleEnvironment setting
  • /RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='LocalStore1']/@path
    • Gets the root directory of the specified local storage entry
  • /RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='LocalStore1']/@sizeInMB
    • The size in MB of the local storage entry
  • /RoleEnvironment/CurrentInstance/Endpoints/Endpoint[@name='Endpoint1']/@protocol
    • Gets the protocol of the specified endpoint
  • /RoleEnvironment/CurrentInstance/Endpoints/Endpoint[@name='Endpoint1']/@port
    • Gets the port of the specified endpoint
  • /RoleEnvironment/CurrentInstance/Endpoints/Endpoint[@name='Endpoint1']/@address
    • Gets the IP Address of the specified endpoint

This was not a complete list of environment variables.  To find out more you can review the links above where you will see other exposed data such as the Virtual Machine size or web site names.

Lessons Learned
The following few points summarize lessons I have learned over time.

  1. If you create your cmd, batch or ps1 files in Visual Studio be sure to use the advanced save options to select “Save with Encoding”.  You can access this using the “Save” button’s drop down menu.  The encoding type you want to select is “Unicode (UTF-8 without signature) – Codepage 65001” or it may not execute properly as a start-up task
  2. When running the web platform installer command line tool it executes some packages using a 32 bit profile which will require changes to the executing user’s appdata directory as seen in a later post
  3. Be sure that your install never presents a dialog requiring user interaction, when unexpected this can cause your instance to fail to start
  4. Building your scripts on a virtual machine running Windows 2008 or 2008 R2 enterprise (depending on the matching Operating System Family) is very helpful.  Start the clean instance and launch cmd.exe.  If you can run your entire script without any user interaction you should be in good shape.
  5. If you have a start-up task that is failing and you cannot figure out why it is helpful to make it a background task, giving you the option of using remote desktop to see what is happening on the box.  
  6. Log everything you can.  In batch files you can leverage output redirection such as “dir >> log.txt 2 >> error.txt” or in PowerShell you can leverage the Add-Content cmdlet such as ‘Add-Content "$debugFileName" "`nHere is my logged text"’
  7. Only elevate the tasks you need to.  Executing processes that require elevation as a start-up task allows you to continue running your core codebase as limited.  Do not overlook the opportunity to move things to start-up for this reason.

Helpful Things to Know
So that pretty much covers the basics.  I am hoping everyone is comfortable with the information we have covered to this point because we are not going to dive into some more detailed scenarios and samples.

For this next section I will point out some things you can do in batch and PowerShell scripts that you may find useful.

Batch Files

  1. You can use “%~dp0” to represent the directory the script is running in.  It is helpful sometimes to change directory to the folder immediately ensuring you know where you are located for building relative paths.  Placing the “cd %~dp0” command at the top of you batch file takes care of this.  
  2. When debugging install scripts you can use “start /w cmd.exe” to open a command window in the context of the executing user.  When using this in conjunction with a background task you may be able to get additional details as to why it is failing.  Please be sure not to use this in the final production release, this is for script debugging only.
  3. There are often points in a script that you wish you had a sleep command but you don’t.  Leveraging the choice command you can get the same behavior “choice /d y /t 5 > nul” says to display a list of choices for 5 seconds and at the end of 5 seconds default to choice “y”.
  4. When running PowerShell scripts from the batch file remember that by default the execution policies will require you to have a signed script.  Most people do not sign their start-up scripts so in PowerShell v2 you can use If you need to run an unsigned PowerShell V2 script even though they are restricted you can execute “powershell -ExecutionPolicy Unrestricted .\Startup.ps1” to launch it. 
    1. If you want to change this setting on the machine for all PowerShell scripts in PowerShell v2 you can execute
      “powershell Set-ExecutionPolicy Unrestricted”
      In PowerShell v1 you have to edit the registry
      “reg add HKLM\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell /v ExecutionPolicy /d Unrestricted /f”  
    2. Please remember that any time you do this you need to weigh out the security considerations of your decision. 
  5. Remember that you can simulate a function call in a batch file using code blocks wrapped in
    1. :myFunctionBlockName
      my commands to execute using %~1 for the first parameter
      goto:eof
    2. To call the above command you would use
      call: myFunctionBlockName parameter1
    3. Before your code blocks place  
      goto:eof
      to ensure it does not start executing the blocks without being called

PowerShell

  1. You can load .NET assemblies which allows you to script the download of blobs etc. without building a custom exe
     ‘add-type -Path "$roleRoot\base\Microsoft.WindowsAzure.ServiceRuntime.dll" | Out-Null’
  2. You can check if the last command failed to executed using “if(!$?)”.  This can be really handy to know for taking corrective actions in your script.  An example of where you could use this is checking whether a library loaded and if not loading it from another location.

Hopefully the lessons learned above will help guide you through the successful creation of start-up tasks.  Watch for the next post “Widnows Azure Startup-up Tasks Part3” where I will show some common tasks that I perform in my scripts that may help you in the future.