Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make startup tasks idempotent?

I have a number of startup tasks in batch files. In particular I call IIS's appcmd.exe to configure IIS. Startup tasks in Azure are supposed to idempotent (ie, able to be run repeatedly with the same results), in case the role is restarted for some reason. Unfortunately many of my IIS configuration commands will fail the second time around, eg because they delete a configuration node the first time which is then not present on subsequent runs.

My question is, how do I make these startup tasks idempotent? Is there a way to make appcmd.exe not throw errors? Is there a way to make the shell catch the errors? Is there a way to make the Azure framework ignore the errors?

Here's an example of my startup tasks. This is all contained in a command file, configiis.cmd.

@REM Enable IIS compression for application/json MIME type
%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json',enabled='True']" /commit:apphost
%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost

@REM Set IIS to automatically start AppPools
%windir%\system32\inetsrv\appcmd.exe set config -section:applicationPools -applicationPoolDefaults.startMode:AlwaysRunning /commit:apphost

@REM Set IIS to not shut down idle AppPools
%windir%\system32\inetsrv\appcmd set config -section:applicationPools -applicationPoolDefaults.processModel.idleTimeout:00:00:00 /commit:apphost

@REM But don't automatically start the AppPools that we don't use, and do shut them down when idle
%windir%\system32\inetsrv\appcmd.exe set config  -section:system.applicationHost/applicationPools "/[name='Classic .NET AppPool'].startMode:OnDemand" "/[name='Classic .NET AppPool'].autoStart:False" "/[name='Classic .NET AppPool'].processModel.idleTimeout:00:01:00" /commit:apphost
%windir%\system32\inetsrv\appcmd.exe set config  -section:system.applicationHost/applicationPools "/[name='ASP.NET v4.0'].startMode:OnDemand" "/[name='ASP.NET v4.0'].autoStart:False" "/[name='ASP.NET v4.0'].processModel.idleTimeout:00:01:00" /commit:apphost
%windir%\system32\inetsrv\appcmd.exe set config  -section:system.applicationHost/applicationPools "/[name='ASP.NET v4.0 Classic'].startMode:OnDemand" "/[name='ASP.NET v4.0 Classic'].autoStart:False" "/[name='ASP.NET v4.0 Classic'].processModel.idleTimeout:00:01:00" /commit:apphost


@REM remove IIS response headers
%windir%\system32\inetsrv\appcmd.exe set config /section:httpProtocol /-customHeaders.[name='X-Powered-By']
like image 659
Brian Reischl Avatar asked Aug 04 '12 17:08

Brian Reischl


4 Answers

Aside from @Syntaxc4's answer: Consider the use of a breadcrumb (file) locally. In your script, check for existence of a known file (that you create). If it doesn't exist, go through your startup script, also creating a breadcrumb file. Next time the vm starts up, it would again check for existence of the breadcrumb file and, if it exists, exit the cmd file. If the breadcrumb file disappears, this typically means your vm has been reconstituted somewhere else (either a new instance or a respawned instance maybe on different hardware) and IIS configuration would be needed.

like image 73
David Makogon Avatar answered Nov 18 '22 13:11

David Makogon


You would have to check to see if the config setting is present before attempting to delete it (add conditional logic). This could be achieved by:

'appcmd.exe list config -details'

Capturing a return value would give you something to compare against, be it length of output or an actual value.

like image 26
cory-fowler Avatar answered Nov 18 '22 13:11

cory-fowler


MSDN now contains an excellent guide for doing this by handling error codes from APPCMD.

http://msdn.microsoft.com/en-us/library/windowsazure/hh974418.aspx

Basically after any appcmd operation, you can do the following:

IF %ERRORLEVEL% EQU 183 DO VERIFY > NUL

and ignore any acceptable error code.

like image 22
Paul Hiles Avatar answered Nov 18 '22 14:11

Paul Hiles


Based on David Makogon's suggestion, I added the following to the top of each of my .cmd files. This seems to do the trick. It will create a flag file (what David called a breadcrumb file) in the same directory as the executing script, then check for it on subsequent runs.

@REM A file to flag that this script has already run
@REM because if we run it twice, it errors out and prevents the Azure role from starting properly
@REM %~n0 expands to the name of the currently executing file, without the extension
SET FLAGFILE=c:\%~n0-flag.txt

IF EXIST "%FLAGFILE%" (
  ECHO %FLAGFILE% exists, exiting startup script
  exit /B
) ELSE (
  date /t > %FLAGFILE%
)
like image 2
Brian Reischl Avatar answered Nov 18 '22 14:11

Brian Reischl