Microsoft Azure Development Cookbook Second Edition
上QQ阅读APP看书,第一时间看更新

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:

  1. Use Visual Studio to create an empty cloud project.
  2. Add a web role to the project (accept the default name of WebRole1).
  3. Add the StartupTask.cmd text file name to the root directory of the web role project.
  4. Set its Copy To Output Directory property to Copy always.
  5. 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.

  6. Add the following, as a child of the WebRole element, to ServiceDefinition.csdef:
    <Startup>
      <Task commandLine="StartupTask.cmd"executionContext="elevated" taskType="simple"/>
    </Startup>
  7. Build and deploy the application into the Cloud Service.
  8. 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: