Summary
Using Chef (or any other remote management tool for that matter – like Windows Remote Management or PowerShell Remoting) to apply Windows updates to a remote system is difficult because some of the Windows Update methods will not work when executed from a remote connection, even if you’re using Administrator level credentials (this is apparently a feature, not a bug). To get around this, the Chef recipe must launch the update commands via a task in Task Scheduler. This can be done by configuring the Task Scheduler task to call the Chef recipe via the local ‘chef-client’ utility.
In this example I’m creating a task to ‘run once’, but since it’s in the past, this task will never get executed on its own. Then I’m manually launching the newly created task, which just calls the Chef Client to run my InstallWindowsUpdates cookbook (recipe: default.rb).
Create/Execute Task via PowerShell Remoting Example:
Invoke-Command -ComputerName <server name> -Credential <admin credentials> -ScriptBlock { cmd /c "schtasks /Create /RU System /RL HIGHEST /F /TR ""c:\opscode\chef\bin\chef-client.bat -o InstallWindowsUpdates"" /TN ChefInstallUpdates /SC Once /ST 00:00 2>&1" } Invoke-Command -ComputerName <server name> -Credential <admin credentials> -ScriptBlock { cmd /c "schtasks /run /tn ChefInstallUpdates 2>&1" }
Windows Update Recipe Example (default.rb):
# # Cookbook Name:: InstallWindowsUpdates # Recipe:: default # Author(s):: Otto Helweg # # Configures Windows Update automatic updates powershell_script "install-windows-updates" do guard_interpreter :powershell_script # Set a 2 hour timeout timeout 7200 code <<-EOH Write-Host -ForegroundColor Green "Searching for updates (this may take up to 30 minutes or more)..." $updateSession = New-Object -com Microsoft.Update.Session $updateSearcher = $updateSession.CreateupdateSearcher() try { $searchResult = $updateSearcher.Search("Type='Software' and IsHidden=0 and IsInstalled=0").Updates } catch { eventcreate /t ERROR /ID 1 /L APPLICATION /SO "Chef-Cookbook" /D "InstallWindowsUpdates: Update attempt failed." $updateFailed = $true } if(!($updateFailed)) { foreach ($updateItem in $searchResult) { $UpdatesToDownload = New-Object -com Microsoft.Update.UpdateColl if (!($updateItem.EulaAccepted)) { $updateItem.AcceptEula() } $UpdatesToDownload.Add($updateItem) $Downloader = $UpdateSession.CreateUpdateDownloader() $Downloader.Updates = $UpdatesToDownload $Downloader.Download() $UpdatesToInstall = New-Object -com Microsoft.Update.UpdateColl $UpdatesToInstall.Add($updateItem) $Title = $updateItem.Title Write-host -ForegroundColor Green " Installing Update: $Title" $Installer = $UpdateSession.CreateUpdateInstaller() $Installer.Updates = $UpdatesToInstall $InstallationResult = $Installer.Install() eventcreate /t INFORMATION /ID 1 /L APPLICATION /SO "Chef-Cookbook" /D "InstallWindowsUpdates: Installed update $Title." } if (!($searchResult.Count)) { eventcreate /t INFORMATION /ID 999 /L APPLICATION /SO "Chef-Cookbook" /D "InstallWindowsUpdates: No updates available." } eventcreate /t INFORMATION /ID 1 /L APPLICATION /SO "Chef-Cookbook" /D "InstallWindowsUpdates: Done Installing Updates." } EOH action :run end
Enjoy!