Why is installing windows updates remotely in Windows Azure so hard

Why is installing windows updates remotely in Windows Azure so hard
9/12/2014 9:54:55 PM

I have been working quite a bit lately in PowerShell and Azure.  Specifically I am spinning up large Azure environments via PowerShell, installing a bunch of special sauce, getting the VM’s configured, and then running some automated tests on a very complex “wide area file system” product that we are developing.  The tests are ran via NUnit.  They essentially spin up huge folder structures with massive files scattered all over the place.  It then simulates all sorts of load in the environment and starts to move big files about.  All of this has worked great so far.  But when it was time to take it one more step to automate the entire test suite we need a bit more than I wanted to tinker with in PowerShell and schedule tasks.  So I quickly created the PowerShell Runner Service which is a TopShelf windows service (can run as a console too) that iterates through a configured list of scripts and runs them.  It can currently support running scripts for ever, or N number of times.  Each script is configurable.  And the tool took advantage of Windows Management Framework 4 so that I could interact with powershell easily via C#.  And then the last half of my day melted away…

image

Today was exciting to start wrapping this utility up.  Running the tests for ever and getting metrics as they run without someone manually managing the process is a huge win for the customer.  Now that this utility was working great locally (works on my box Winking smile) it was time to get it into the automation so that I could call it done in our next stand up. 

I usually start this process by installing it on the remote machine manually.  That let’s me know what steps will be needed and if there are any missing tools that will need to be installed in the automation.  To do this I logged into the VM in Azure and I copied over the tiny program and its dependencies.  And as the utility is written with TopShelf it can be quickly ran via the command line for rapid testing purposes.  So I happily edited the configuration file to point the local powershell scripts.  And ran the application.

BOOM!  Somewhat expected boom.  But boom just the same.

image

The next step was obvious.  I was missing the dependency I took on the latest version of powershell.  No worries.  So I downloaded the package (at the link above) and installed it.

image

Once that was completed I ran my program once again and all was good to go.  The tests started to run as planned.

image

Now that I know what is needed in addition to my little powershell runner service I needed to figure out how to get it into the scripts for building the environment.  As I was already copying files and starting processes – moving the service bits over, installing it, and running it is pretty easy.  But installing the windows updates for powershell was a new thing to be figured out.

I started by invoking a command on WUSA (windows update stand alone installer) with all the appropriate information.  Noting that this path is the local path on the remote machine.

Invoke-Command -Session $session {
    $prog = "wusa"
    $arg = "c:\tools\WindowsManagementFramework4.msu /quiet"
    invoke-command {param($p,$a)& $p $a} -ArgumentList $prog,$arg
}

This ran with no output…and no errors.  Hate it when that happens because it usually means something didn’t work.  Where to begin to figure out what needs to change.  Did it install? 

To start figuring out what went wrong/right I logged into the remote machine and went into the installed programs to determine what updates were installed.  I was hoping to see the new update at the top of the list.  Nope.

image

Time to look at the event log.  I found a curious error which didn’t make a bunch of sense to me.

image

And that I have still yet to entirely figure out.  There were all sorts of links about this particular error message.  I looked through them but quickly tried another option.

This article will jump you off to a bunch of other resources on the topic:

http://answers.microsoft.com/en-us/windows/forum/windows_other-windows_update/windows-update-could-not-be-installed-because-of/0aba9600-5b99-467d-97b6-0a3e1c6e66ff

Next I looked into starting a process inside an invoke command instead of invoking a command inside an invoke command.

Invoke-Command -Session $session {
    $SB1={
        Start-Process -FilePath 'wusa' -ArgumentList "c:\tools\WindowsManagementFramework4.msu /quiet" -wait -PassThru
    }
   
    Invoke-Command -ScriptBlock $SB1
    Start-Sleep -Seconds 3
}

image

This started the appropriate process.  Output was given!  A move in the right direction?

image

I looked to see if the update was installed.  Nope.  Back to the error log.

Argh!  A different error.  Windows update  could not be installed because of error 2147942405 "Access is denied." (Command line: ""C:\Windows\system32\wusa.exe" c:\tools\WindowsManagementFramework4.msu /quiet ")

image

A quick search turned up this article which had yet another approach to this known issue.  Apparently you are not allowed to install windows updates remotely.  Who knew?  And…why not?  On to this next approach.

In the article it suggests unpacking the windows update to a folder on the remote machine.  Then using DISM to install the update.  Let’s give that a go.

I started by determining how to do the unpack.  I used WUSA to do the unpack.  You can apparently also use DISM for that task.  Then tried to use DISM to install a specific cab file in the unpack location.  That didn’t work.  So I shifted to having DISM install the update that was in the unpack folder.  It was able to figure out how to do that.

Invoke-Command -Session $session {
    $SB1={
        Start-Process -FilePath 'wusa' -ArgumentList "c:\tools\WindowsManagementFramework4.msu /extract:C:\updates" -wait -PassThru
    }

    $SB2={
        Start-Process -FilePath 'dism' -ArgumentList "/online /add-package /PackagePath:c:\updates /IgnoreCheck" -PassThru
    }
   
    Invoke-Command -ScriptBlock $SB1
    Start-Sleep -Seconds 3

    Invoke-Command -ScriptBlock $SB2
    Start-Sleep -Seconds 3
}

 

image

Hey hey.  This looked like it worked.  Did it?  I checked to see if the update was installed.  NOPE!?  Crap.  So back to the event viewer.  Where to my surprise was an exciting message. 

A reboot is necessary before package KB2809215 can be changed to the Installed state.

image

After a quick restart I finally had my updates installed!  WOO HOO!  I did have to bump the seconds up from 3 to 60.

Next on the list of //TODO is to look into BoxStarter.  They have solved this issue with some super special awesome sauce.

comments powered by Disqus