Using startup tasks in a Microsoft Azure role
Microsoft Azure provides a locked-down environment for websites hosted in IIS (web roles) and application services (worker roles). While this hardening significantly eases administration, it also limits the ability to perform certain tasks such as installing software or writing to the registry. Another problem is that any changes to an instance are lost whenever the instance is reimaged or moved to a different server.
The service definition provides the solution to this problem by allowing the creation of startup tasks, which are script files or executable programs that are invoked each time an instance is started. Startup tasks allow a temporary escape from the restrictions of the locked-down web role and worker role while retaining the benefits of these roles.
A startup task must be robust against errors because a failure could cause the instance to recycle. In particular, the effect of a startup task must be idempotent. As a startup task is invoked each time an instance starts, it must not fail when performed repeatedly. For example, when a startup task is used to install software, any subsequent attempt to reinstall the software must be handled gracefully.
Startup tasks are specified with the Startup
element in the ServiceDefinition.csdef
service definition file. This is a child element of the WebRole
or WorkerRole
element. The child elements in the Startup
element comprise a sequence of one or more individual Task
elements, each specifying a single startup task. The following example shows the definition of a single startup task and includes all the attributes for a Task
element:
<Startup> <TaskcommandLine="Startup.cmd"executionContext="elevated"taskType="simple" /> </Startup>
The commandLine
attribute specifies a script or executable and its location relative to the %RoleRoot%\AppRoot\bin
folder for the role. The executionContext
element takes one of two values: limited
, to indicate the startup task runs with the same privileges as the role, or elevated
, to indicate the startup task runs with full administrator privileges. It is the capability provided by elevated startup tasks that gives them their power. There are three types of startup tasks, which are as follows:
- Simple: This indicates that the system cannot invoke additional startup tasks until this one completes.
- Background: This initiates the startup task in the background. This is useful in the case of a long-running task, the delay in which could cause the instance to appear unresponsive.
- Foreground: This resembles a background startup task, except that the instance cannot be recycled until the startup task completes. This can cause problems if something goes wrong with the startup task.
Windows PowerShell 2 is installed on Microsoft Azure roles that run guest OS 2.x or higher. This provides a powerful scripting language that is ideal for scripting startup tasks.
Tip
The guest OS is the nickname for the version set of Windows Server that runs Azure instances. For the time being, we have four guest OS families:
- Guest OS family 4, which is based on Windows Server 2012 R2 and supports the .NET Framework 4.0, 4.5, and 4.5.1.
- Guest OS family 3, which is based on Windows Server 2012 and supports the .NET Framework 4.0 and 4.5.
- Guest OS family 2, which is based on Windows Server 2008 R2 SP1 and supports the .NET Framework 3.5 and 4.0.
- Guest OS family 1 (retired in 2014), which is based on Windows Server 2008 SP2 and supports the .NET Framework 3.5 and 4.0. It does not support Version 4.5 or later.
A PowerShell script named StartupTask.ps1
is invoked from the startup task command file as follows:
C:\Users\Administrator>PowerShell -ExecutionPolicy Unrestricted .\StartupTask.ps1
The ExecutionPolicy
parameter specifies that StartupTask.ps1
can be invoked even though it is unsigned.
In startup tasks, we can use AppCmd
to manage IIS. We can also use the WebPICmdLine
command-line tool, WebPICmdLine.exe
, to access the functionality of the Microsoft Web Platform Installer. This allows us to install Microsoft Web Platform components, which includes, for example, PHP.
How to do it...
We are going to use a startup task that uses AppCmd
to modify the default idle timeout for IIS application pools. We will do this using the following steps:
- Use Visual Studio to create an empty cloud project.
- Add a web role to the project (accept the default name of
WebRole1
). - Add the
StartupTask.cmd
text file name to the root directory of the web role project. - Set its Copy To Output Directory property to Copy always.
- Insert the following text in the ASCII-encoded file:
%SystemRoot%\system32\inetsrv\appcmd set config -section:applicationPools -applicationPoolDefaults.processModel.idleTimeout:0.01:00:00 exit /b 0
Tip
If the "exit /b 0" clause is not provided, the role will enter a continuous recycle state.
- Add the following, as a child of the
WebRole
element, toServiceDefinition.csdef
:<Startup> <Task commandLine="StartupTask.cmd"executionContext="elevated" taskType="simple"/> </Startup>
- Build and deploy the application into the Cloud Service.
- Open IIS Manager, select Application Pools, right-click on any application pool, and select Advanced Settings…. Verify that the Idle Timeout (minutes) setting is
60
minutes for the application pool.
How it works...
In steps 1 and 2, we created a cloud project with a web role. In steps 3 and 4, we added the command file for the startup task to the project and ensured that the build copied the file to the appropriate location in the Microsoft Azure package. In step 5, we added a command to the file that set the idle timeout to 1
hour for IIS application pools. The exit
command ended the batch file with a return code of 0
.
In step 6, we added the startup task to the service definition file. We set the execution context of the startup task to elevated
so that it had the privilege required to modify IIS settings.
In step 7, we built and deployed the application into a Cloud Service. We verified that the startup task worked in step 8.
There's more...
Note that the web or worker role itself can run with elevated privileges. In a web role, full IIS runs its own process that continues to have limited privileges; only the role-entry code (in WebRole.cs
) runs with elevated privileges. This privilege elevation is achieved by adding the following as a child element of the WebRole
or WorkerRole
element in the ServiceDefinition.csdef
service definition file:
<Runtime executionContext="elevated"/>
The default value for executionContext
is limited
.
Having done this, we can set the application pool idle timeout in code by invoking the following from the OnStart()
method for the web role:
private void SetIdleTimeout(TimeSpan timeout) { using (ServerManager serverManager = new ServerManager()) { serverManager.ApplicationPoolDefaults.ProcessModel.IdleTimeout = timeout; serverManager.CommitChanges(); } }
The ServerManager
class is in the Microsoft.Web.Administrator
namespace, which is contained in the following assembly: %SystemRoot%\System32\inetsrv\Microsoft.Web.Administration.dll
.
Developing startup tasks
When developing startup tasks, it can be useful to log the output of commands to a known location for further analysis. When using the development environment, another trick is to set the startup task script to the following code:
start /wait cmd
This produces a command window in which we can invoke the desired startup command and see any errors or log them with the DOS redirect (">>
"). The /wait
switch blocks the caller until the prompt completes.
See also
Have a look at the following MSDN link to get additional information:
- Best practices for startup tasks (http://msdn.microsoft.com/en-us/library/jj129545.aspx)