1
votes

I have a web/worker roles which have custom startup script to execute on role start to ensure required version of .NET is installed.

ServiceDefinition.csdef file of cloud service:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="MyServices" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2015-04.2.6">
  <WebRole name="WebApiRole" vmsize="Standard_D1">
    <Sites>
      <Site name="Web" physicalDirectory="..\..\..\WebApiRole\">
        <Bindings>
          <Binding name="HttpIn" endpointName="HttpIn" />
          <Binding name="Endpoint1" endpointName="HttpsIn" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
      <InputEndpoint name="HttpsIn" protocol="https" port="443" certificate="myservices.com" />
    </Endpoints>
    <Certificates>
      <Certificate name="myservices.com" storeLocation="LocalMachine" storeName="CA" />
    </Certificates>
    <LocalResources>
      <LocalStorage name="InstallLogs" sizeInMB="5" cleanOnRoleRecycle="false" />
    </LocalResources>
    <Startup>
      <Task commandLine="install.cmd" executionContext="elevated" taskType="simple">
        <Environment>
          <Variable name="PathToInstallLogs">
            <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='InstallLogs']/@path" />
          </Variable>
        </Environment>
      </Task>
    </Startup>
    <Imports>
      <Import moduleName="RemoteAccess" />
      <Import moduleName="RemoteForwarder" />
    </Imports>
  </WebRole>
  <WorkerRole name="WorkerRole" vmsize="Standard_D1">
    <LocalResources>
      <LocalStorage name="CustomTempLocalStore" sizeInMB="2048" cleanOnRoleRecycle="true" />
      <LocalStorage name="InstallLogs" sizeInMB="5" cleanOnRoleRecycle="false" />
    </LocalResources>
    <Startup>
      <Task commandLine="install.cmd" executionContext="elevated" taskType="simple">
        <Environment>
          <Variable name="PathToInstallLogs">
            <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='InstallLogs']/@path" />
          </Variable>
        </Environment>
      </Task>
    </Startup>
    <Imports>
      <Import moduleName="RemoteAccess" />
    </Imports>
  </WorkerRole>
</ServiceDefinition>

From what I can see, startup task to run install.cmd is according to microsoft docs.

I can confirm that upon deployment install.cmd is deployed at root folder - AppRoot\Bin.

If I execute install.cmd manually on cloud service VM it runs as expected. However, it is not running upon deployment and as a result web/worker roles keep recycling (due to missing .NET version which code depends on).

For reference this is the contents of install.cmd

REM Set the value of netfx to install appropriate .NET Framework. 
REM ***** To install .NET 4.5.2 set the variable netfx to "NDP452" *****
REM ***** To install .NET 4.6 set the variable netfx to "NDP46" *****
set netfx="NDP48"

REM ***** Setup .NET filenames and registry keys *****
if %netfx%=="NDP48" goto NDP48
if %netfx%=="NDP46" goto NDP46
    set netfxinstallfile="NDP452-KB2901954-Web.exe"
    set netfxregkey="0x5cbf5"
    goto logtimestamp

:NDP46
set netfxinstallfile="NDP46-KB3045560-Web.exe"
set netfxregkey="0x60051"
goto logtimestamp

:NDP48
set netfxinstallfile="ndp48-web.exe"
set netfxregkey="0x80eb1"
goto logtimestamp

:logtimestamp
REM ***** Setup LogFile with timestamp *****
set timehour=%time:~0,2%
set timestamp=%date:~-4,4%%date:~-10,2%%date:~-7,2%-%timehour: =0%%time:~3,2%
set startuptasklog=%PathToInstallLogs%startuptasklog-%timestamp%.txt
set netfxinstallerlog=%PathToInstallLogs%NetFXInstallerLog-%timestamp%
echo Logfile generated at: %startuptasklog% >> %startuptasklog%

REM ***** Check if .NET is installed *****
echo Checking if .NET (%netfx%) is installed >> %startuptasklog%
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release | Find %netfxregkey%
if %ERRORLEVEL%== 0 goto end

REM ***** Installing .NET *****
echo Installing .NET. Logfile: %netfxinstallerlog% >> %startuptasklog%
start /wait %~dp0%netfxinstallfile% /q /serialdownload /log %netfxinstallerlog% >> %startuptasklog% 2>>&1
if %ERRORLEVEL%== 0 goto installed
    echo .NET installer exited with code %ERRORLEVEL% >> %startuptasklog%    
    if %ERRORLEVEL%== 3010 goto restart
    if %ERRORLEVEL%== 1641 goto restart
    echo .NET (%netfx%) install failed with Error Code %ERRORLEVEL%. Further logs can be found in %netfxinstallerlog% >> %startuptasklog%
    goto exit

:restart
echo Restarting to complete .NET (%netfx%) installation >> %startuptasklog%
shutdown.exe /r /t 5 /c "Installed .NET framework" /f /d p:2:4

:end
echo install.cmd completed: %date:~-4,4%%date:~-10,2%%date:~-7,2%-%timehour: =0%%time:~3,2% >> %startuptasklog%

:exit
EXIT /B 0

I can confirm that ndp48-web.exe executable can be found under root folder alongside with install.cmd. As I said, script is not executed on startup (I can't find any logs, nor any entries in Event Viewer).

Any clue what's wrong with this ?

1

1 Answers

2
votes

The error is in the install.cmd script.

I had the same problem using the script from documentation, here, which is similar with yours.

If you look closely the name of the log file has a the %timestamp% at the end. This makes the filename to have a wrong format. It has /. So, the first time the script tries to write to the log file, it gets the error: The system cannot find the path specified.

This successful ends the script, but without doing anything. It doesn't create any logs, it doesn't install the .NET Framework.

My solution was to change the timestamp format, by replacing the %timestamp% variable with the %dtStamp% that I compute with below script:

@ECHO OFF
SET HOUR=%time:~0,2%
SET dtStamp9=%date:~-4%%date:~4,2%%date:~7,2%_0%time:~1,1%%time:~3,2%%time:~6,2% 
SET dtStamp24=%date:~-4%%date:~4,2%%date:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
if "%HOUR:~0,1%" == " " (SET dtStamp=%dtStamp9%) else (SET dtStamp=%dtStamp24%)
@ECHO ON

echo timestamp: %dtStamp%