Compute Node - Install .NET 4.6.1

Category: azure batch


Jon Maxwell on Wed, 18 May 2016 17:33:21

I am setting up a pool with an application package that requires that .NET 4.6.1 is installed. As of the current compute node VM specifications, they only have .NET 4.5 installed. 

I have it set up that .NET 4.6.1 installer is included in the application package, and then the setup is run via the start task of the compute node, but the setup of .NET requires a restart of the node. If you accept the restart, then the compute node becomes "Unusable" because the start task technically failed because of the restart. 

Is there a way to install .NET 4.6.1 on a compute node without needing a restart?

Can .NET 4.6.1 be pre-deployed on a compute node image?

What is a safe way to restart a compute node during a start task so that it doesn't become unusable or have the status "Start task failed"?


Matthew Christopher on Thu, 19 May 2016 00:31:10

Hi jomax7296,

I am not an expert on .NET installation, but according to here there is a norestart option.

It looks like you probably want: passive and norestart at least.

One thing to consider is that after a reboot, the start task will run again (it runs any time the VM restarts -- either because of reimage or a plain reboot), so you start task could be failing the second time because .NET 4.6 is already installed -- look for that too (i.e. your trouble may be due to this, not due to the start task exiting prematurely the first time).

Also, as an aside, once Server 2016 support is in Azure, you could use that OS to get .NET 4.6 support right out of the box (no install needed).  See here.

Hope that helps - let us know if it doesn't.

Matthew Christopher on Mon, 23 May 2016 16:37:34

See this article for more information, specifically take a look at their suggested "install.cmd"

Note that the "startup task" they are referring to in that article is an Azure Cloud Services startup task, not an Azure Batch startup task, but the install.cmd script and general principle should work in Azure Batch as well.

Jonathan.Maxwell on Mon, 23 May 2016 17:16:44

Note that I am the same person who originally posted the question.

After many tries we are still not able to install .NET 4.6.1 on a compute node via the StartTask. The /passive or /quiet install is failing with exit code 112, which is “disk space full”. However, if I log into the compute node VM and install .NET 4.6.1 manually I don’t have the problem. I assume that the account that the batch node is using for the StartTask is not an admin.

Is there a way to elevate the permissions of a StartTask node account?

Matthew Christopher on Mon, 23 May 2016 19:52:32

Yes, the StartTask has a RunElevated option.

Jonathan.Maxwell on Mon, 23 May 2016 20:57:50

Thanks for the help, Matthew.

That worked fine, and now .NET can be installed via a StartTask as elevated. The only issue is that the StartTask is finishing before the actual restart happens, causing the node to be assigned tasks.

For reference, I have an a C# executable that is called as the "CommandLine" parameter of the start task, and returns "nReturnCode".

Here's the code:

Pool Setup:

        pool.StartTask = new StartTask
          // Specify a command line for the StartTask that copies the task application files to the
          // node's shared directory. Every compute node in a Batch pool is configured with a number
          // of pre-defined environment variables that can be referenced by commands or applications
          // run by tasks.

          // Since a successful execution of robocopy can return a non-zero exit code (e.g. 1 when one or
          // more files were successfully copied) we need to manually exit with a 0 for Batch to recognize
          // StartTask execution success.
          RunElevated = true,
          CommandLine = "cmd /c %AZ_BATCH_APP_PACKAGE_INSTALLERAPP#1%\\ComputeNode.exe",
          ResourceFiles = resourceFiles,
          WaitForSuccess = true

ComputeNode.cs (code is a little messy)

static int Main(string[] args) { int nExitCode = 0; bool installNET461 = true; string environmentVariableNET461 = "AZ_BATCH_APP_PACKAGE_DOTNET#4.6.1"; string installFolderNET = Environment.GetEnvironmentVariable(environmentVariableNET461); // Check current installation of .NET try { using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full")) { if (key != null) { string versionStr = key.GetValue("Release").ToString(); int currentVersion = Int32.Parse(versionStr); if (currentVersion < 394254) { installNET461 = true; Console.Out.WriteLine(".NET 461 Needs Installed"); } else { installNET461 = false; Console.Out.WriteLine(".NET 461 Does not need installed"); } } } } catch (Exception ex) //just for's always best to handle specific exceptions { //react appropriately } if (installNET461) // Don't install if we have determined that it was already installed. { Console.Out.WriteLine("Preparing to install .NET 4.6.1."); ProcessStartInfo startInstaller = new ProcessStartInfo(); startInstaller.Arguments = "/q /log " + installFolderNET + "installLog.txt"; startInstaller.FileName = installFolderNET + "\\NDP461-KB3102436-x86-x64-AllOS-ENU.exe"; var process = Process.Start(startInstaller); process.WaitForExit(); Console.Out.WriteLine("Exit Code: " + process.ExitCode); nExitCode = process.ExitCode; // If the exit code is 3010, then that means that the system requires // a reboot. if(nExitCode == 3010) { //nExitCode = 0; <- Matthew, if I don't set this to 0, then the node becomes "unusable".

<- However, once it is 0, then the node begins to be assigned tasks before it restarts. Console.Out.WriteLine("Restarting the machine..."); var restart = Process.Start("shutdown", "/r /t 0"); restart.WaitForExit(); } } return nExitCode; }

I commented out a line that manually sets the nExitCode to 0, because when I do the node becomes available for tasking. The installer for .NET will give me a return code of 3010 when running in silent mode, which means that the system needs rebooted for changes to take effect. If I return 3010 to the StartTask, then the node will have the status of "StartTaskFailed" and then becomes unusable (I assume).

Am I going about this wrong, or do you have any suggestions for me to try?

Matthew Christopher on Tue, 24 May 2016 18:02:03

Hi Jon,

No, you're not going about this wrong.

You should be able to return exit code 3010 there, because even if your start task fails, after the reboot it will run again, and the re-run start task will exit with code 0 presumably because the if(installNET461) will be false and the if condition won't execute.  The only weird thing about doing it this way is that you'll have an intermediate situation where your start task is reported as "failed" when in reality it is doing exactly what you expect -- but once the VM reboots it should come back up and re-run your start task which should succeed.

Let me know if that's not the behavior you're observing.


Jon Maxwell on Wed, 25 May 2016 01:12:21


The behavior I'm observing is interesting. When I call the reboot on the node, "shutdown -r -t 0", the node restarts and becomes "unusable". I also notice that the StartTask does not run at all after the restart. I have a custom log file that i create that writes a line immediately after the static int main(string[] args) { line and the log is never written to again after the restart. 

Does a restart called by shutdown -r (in it's own VM) make it lose the environment variables for the StartTask? I'm thinking that rebooting the node without doing it from the pool operations leads it to lose connection to the actual pool.

- Jon

Jon Maxwell on Wed, 25 May 2016 13:27:54



I found out how to use the virtualMachineConfiguration for the pool and was able to create a 2016 preview node instead of the 2012 OS, which would remove the need for the restart alltogether since .NET 461 is already included.

However, I get a message saying that ApplicationPackageReferences are not supported when using this virtualMachineConfiguration. Are there any alternatives to using ApplicationPackageReferences? I need to get an installer MSI on the node VM in order to install our software using for the analysis.

- Jon

Matthew Christopher on Wed, 25 May 2016 17:03:36

You can use a StartTask's resource files to pull data down onto a VirtualMachineConfiguration based pools nodes.

I will follow up about seeing the StartTask not run twice.  It should run after a reboot (unless shutdown -r didn't actually restart the node, which would be checkable based on the Nodes LastBootTime property.)  If you are still investigating the StartTask based solution to installing .NET461 I would look at the LastBootTime of the VMs and see if it changes between when the VM first comes up and when your StartTask runs.

Jon Maxwell on Thu, 02 Jun 2016 13:11:47

Any news on your findings?

Matthew Christopher on Mon, 06 Jun 2016 20:23:22

Hi Jon,

I was able to get the restart pattern I mentioned above to work -- but one thing I did confirm in my testing is that if the task does not have "RunElevated" specified, it doesn't correctly reboot after "shutdown /r /t 0" -- from your code snippet it did seem like you were using that flag, but I just wanted to confirm that.

One thing you can do to check is get the standard out file for the start task (stdout.txt) and the standard error file (stderr.txt) -- I saw that when I didn't have RunElevated specified, stderr.txt said "Access Denied" or something along those lines.