Strategies for Writing Startup Tasks that can run Multiple Times
Updated: November 13, 2013
In Windows Azure, startup tasks are executed when you first start your role, when your role reboots, and when your role recycles. Because startup tasks execute on role recycles (which do not include a reboot), startup tasks must be written so that they can run more than once between reboots.
Roles Can Recycle Without Rebooting
Roles can recycle for a few reasons. Roles can recycle if WaWebHost.exe or WaWorkerHost.exe crash while hosting your application. Also, if you stop and then start your deployment, then the role will recycle.
Because of these reasons, startup tasks must work even if they are run more than once between reboots. This is important because if a simple task ends with a non-zero errorlevel (or exit code), subsequent simple roles and the role itself will not start. A foreground task can also be problematic because the role will run while the foreground task runs, so if the foreground task hangs the role will not recycle.
There are a few strategies for writing a startup task that will work, even when the tasks would generate errors if run more than once.
Run through the startup task
If the errors can be safely ignored when running a startup task multiple times, or if the startup task does not generate errors, there is no need to do anything special with the startup task. Of course, you should always write a log of the startup activities for debugging later.
Recognize when a task was already performed
If a task only needs to be performed once, or if a task can or should only be performed once, the task can create a small file in the %RoleRoot% directory to indicate success. The startup batch file can then check for the existence of this file and skip that step or exit the batch file entirely.
The following example creates the file App1Install_Success.txt when the application installs without error. If the App1Install_Success.txt file already exists, then the batch file skips the install process:
REM This startup batch file installs the fictitious program Application 1 REM If App1Install_Success.txt exists, then Application 1 is already installed. IF EXIST "%RoleRoot%\App1Install_Success.txt" ( ECHO Application 1 is already installed. Exiting. >> "%TEMP%\StartupLog.txt" 2>&1 GOTO AfterApp1Install ) REM Install Application 1. ECHO Installing Application 1 >> "%TEMP%\StartupLog.txt" 2>&1 "%PathToApp1Install%\setup.exe" >> "%TEMP%\StartupLog.txt" 2>&1 IF %ERRORLEVEL% EQU 0 ( REM The application installed without error. Create a file to indicate that the application REM does not need to be installed again. ECHO This line will create a file to indicate that Application 1 installed correctly. > "%RoleRoot%\App1Install_Success.txt" ) ELSE ( REM An error occurred. Log the error and exit with the error code. DATE /T >> "%TEMP%\StartupLog.txt" 2>&1 TIME /T >> "%TEMP%\StartupLog.txt" 2>&1 ECHO An error occurred installing Application 1. Errorlevel = %ERRORLEVEL%. >> "%TEMP%\StartupLog.txt" 2>&1 EXIT %ERRORLEVEL% ) :AfterApp1Install REM Exit normally. EXIT /B 0
Perform error checking and error handling
Another strategy is to check the errorlevel after each step in the startup task. Expected errors are handled, and the batch file continues. Unexpected errors are logged and the batch file exits.
The following example adds a section to the IIS Web.config file using AppCmd.exe. If the section already exists, then AppCmd.exe displays an error and exits with an errorlevel of 183. This is expected when the batch file is run more than once, so the errorlevel is set back to zero using the verify.exe command (using verify.exe with no parameters will display a simple message, then set the errorlevel to zero). If the errorlevel is any other non-zero value (such as 5, for access denied), then the batch file logs the error and exits.
REM *** Add a compression section to the Web.config file. *** %windir%\system32\inetsrv\appcmd set config /section:urlCompression /doDynamicCompression:True /commit:apphost >> "%TEMP%\StartupLog.txt" 2>&1 REM ERRORLEVEL 183 occurs when trying to add a section that already exists. This error is expected if this REM batch file were executed twice. This can occur and must be accounted for in a Windows Azure startup REM task. To handle this situation, set the ERRORLEVEL to zero by using the Verify command. The Verify REM command will safely set the ERRORLEVEL to zero. IF %ERRORLEVEL% EQU 183 DO VERIFY > NUL REM If the ERRORLEVEL is not zero at this point, some other error occurred. IF %ERRORLEVEL% NEQ 0 ( ECHO Error adding a compression section to the Web.config file. >> "%TEMP%\StartupLog.txt" 2>&1 GOTO ErrorExit ) REM *** Add compression for json. *** %windir%\system32\inetsrv\appcmd set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost >> "%TEMP%\StartupLog.txt" 2>&1 IF %ERRORLEVEL% EQU 183 VERIFY > NUL IF %ERRORLEVEL% NEQ 0 ( ECHO Error adding the JSON compression type to the Web.config file. >> "%TEMP%\StartupLog.txt" 2>&1 GOTO ErrorExit ) REM *** Exit batch file. *** EXIT /b 0 REM *** Log error and exit *** :ErrorExit REM Report the date, time, and ERRORLEVEL of the error. DATE /T >> "%TEMP%\StartupLog.txt" 2>&1 TIME /T >> "%TEMP%\StartupLog.txt" 2>&1 ECHO An error occurred during startup. ERRORLEVEL = %ERRORLEVEL% >> "%TEMP%\StartupLog.txt" 2>&1 EXIT %ERRORLEVEL%
ConceptsRun Startup Tasks in Windows Azure