vagrant-windows-update 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54ea485d5e4758a19ea3a8504daf7f1e5ff17d02
4
+ data.tar.gz: e17fa78130d2eee869788409b26217595e9692f4
5
+ SHA512:
6
+ metadata.gz: e90107938bd2ad31cdee9e9bcf85f48af5e352e6d246221ca822168654bf775bcdffb256966b3347fac7d4bfe21071bdb80e66287da7bcf73d2b6834244b8e22
7
+ data.tar.gz: 6cb0b04df5ad00fe868c3871e7e90db1e686fe3ff5cc22bb8491bed49b308d6631d7eb95eb6b4b3f70c4a8be211d8cd1b2323d100f73164256a27bd04d6aba57
@@ -0,0 +1,94 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The Vagrant Windows Update plugin must be run within Vagrant."
5
+ end
6
+
7
+ if Vagrant::VERSION < "1.9.0"
8
+ raise "The Vagrant Windows Update plugin is only compatible with Vagrant 1.9+"
9
+ end
10
+
11
+ module VagrantPlugins
12
+ module WindowsUpdate
13
+ class Plugin < Vagrant.plugin("2")
14
+ name "Windows Update"
15
+ description "Vagrant plugin to update a Windows VM as a provisioning step."
16
+
17
+ provisioner "windows-update" do
18
+ class Provisioner < Vagrant.plugin("2", :provisioner)
19
+ def initialize(machine, config)
20
+ super
21
+ end
22
+
23
+ def configure(root_config)
24
+ end
25
+
26
+ # see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/ui.rb
27
+ # see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/plugin/v2/provisioner.rb
28
+ # see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/plugin/v2/communicator.rb
29
+ # see https://github.com/mitchellh/vagrant/blob/master/plugins/provisioners/shell/provisioner.rb
30
+ def provision
31
+ remote_path = "C:/Windows/Temp/vagrant-windows-update.ps1"
32
+ @machine.communicate.upload(
33
+ File.join(File.dirname(__FILE__), "vagrant-windows-update", "windows-update.ps1"),
34
+ remote_path)
35
+ command = "PowerShell -ExecutionPolicy Bypass -OutputFormat Text -File #{remote_path}"
36
+ loop do
37
+ begin
38
+ until @machine.communicate.ready?
39
+ sleep 10
40
+ end
41
+
42
+ reboot_required = false
43
+ @machine.communicate.sudo(command, {elevated: true, interactive: false}) do |type, data|
44
+ reboot_required = true if type == :stdout && data.start_with?("Rebooting...")
45
+ handle_comm(type, data)
46
+ end
47
+ break unless reboot_required
48
+
49
+ # NB we have to set an absurd high halt timeout to make sure the machine is
50
+ # not turned off before the Windows updates are installed.
51
+ original_graceful_halt_timeout = @machine.config.vm.graceful_halt_timeout
52
+ @machine.config.vm.graceful_halt_timeout = 4*60*60 # 4h.
53
+ begin
54
+ options = {}
55
+ options[:provision_ignore_sentinel] = false
56
+ @machine.action(:reload, options)
57
+ ensure
58
+ @machine.config.vm.graceful_halt_timeout = original_graceful_halt_timeout
59
+ end
60
+ rescue => e
61
+ @machine.ui.warn("Ignoring error, hoping it is transient: #{e.class} #{e} at:\n\t#{e.backtrace.join("\n\t")}")
62
+ next
63
+ end
64
+ end
65
+ end
66
+
67
+ def cleanup
68
+ end
69
+
70
+ protected
71
+
72
+ # This handles outputting the communication data back to the UI
73
+ def handle_comm(type, data)
74
+ if [:stderr, :stdout].include?(type)
75
+ # Output the data with the proper color based on the stream.
76
+ color = type == :stdout ? :green : :red
77
+
78
+ # Clear out the newline since we add one
79
+ data = data.chomp
80
+ return if data.empty?
81
+
82
+ options = {}
83
+ options[:color] = color if !config.keep_color
84
+
85
+ @machine.ui.info(data.chomp, options)
86
+ end
87
+ end
88
+ end
89
+
90
+ Provisioner
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module WindowsUpdate
3
+ VERSION = "0.0.3"
4
+ end
5
+ end
@@ -0,0 +1,140 @@
1
+ # see Using the Windows Update Agent API | Searching, Downloading, and Installing Updates
2
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa387102(v=vs.85).aspx
3
+ # see ISystemInformation interface
4
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386095(v=vs.85).aspx
5
+ # see IUpdateSession interface
6
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386854(v=vs.85).aspx
7
+ # see IUpdateSearcher interface
8
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386515(v=vs.85).aspx
9
+ # see IUpdateDownloader interface
10
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386131(v=vs.85).aspx
11
+ # see IUpdateCollection interface
12
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386107(v=vs.85).aspx
13
+ # see IUpdate interface
14
+ # at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx
15
+
16
+ Set-StrictMode -Version Latest
17
+
18
+ $ErrorActionPreference = 'Stop'
19
+
20
+ trap {
21
+ Write-Output "ERROR: $_"
22
+ Write-Output (($_.ScriptStackTrace -split '\r?\n') -replace '^(.*)$','ERROR: $1')
23
+ Exit 1
24
+ }
25
+
26
+ Add-Type @'
27
+ using System;
28
+ using System.Runtime.InteropServices;
29
+
30
+ public static class Windows
31
+ {
32
+ [DllImport("kernel32", SetLastError=true)]
33
+ public static extern UInt64 GetTickCount64();
34
+
35
+ public static TimeSpan GetUptime()
36
+ {
37
+ return TimeSpan.FromMilliseconds(GetTickCount64());
38
+ }
39
+ }
40
+ '@
41
+
42
+ function Wait-Condition {
43
+ param(
44
+ [scriptblock]$Condition,
45
+ [int]$DebounceSeconds=15
46
+ )
47
+ process {
48
+ $begin = [Windows]::GetUptime()
49
+ do {
50
+ Start-Sleep -Seconds 1
51
+ try {
52
+ $result = &$Condition
53
+ } catch {
54
+ $result = $false
55
+ }
56
+ if (-not $result) {
57
+ $begin = [Windows]::GetUptime()
58
+ continue
59
+ }
60
+ } while ((([Windows]::GetUptime()) - $begin).TotalSeconds -lt $DebounceSeconds)
61
+ }
62
+ }
63
+
64
+ function ExitWhenRebootRequired {
65
+ # check for pending Windows Updates.
66
+ $systemInformation = New-Object -ComObject 'Microsoft.Update.SystemInfo'
67
+ $rebootRequired = $systemInformation.RebootRequired
68
+
69
+ # check for pending Windows Features.
70
+ if (!$rebootRequired) {
71
+ $pendingPackagesKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending'
72
+ $pendingPackagesCount = (Get-ChildItem -ErrorAction SilentlyContinue $pendingPackagesKey | Measure-Object).Count
73
+ $rebootRequired = $rebootRequired -or $pendingPackagesCount -gt 0
74
+ }
75
+
76
+ if ($rebootRequired) {
77
+ Write-Output 'Pending Reboot detected. Waiting for the Windows Modules Installer to exit...'
78
+ Wait-Condition {(Get-Process -ErrorAction SilentlyContinue TiWorker,TrustedInstaller | Measure-Object).Count -eq 0}
79
+ Write-Output 'Rebooting...'
80
+ Exit 0
81
+ }
82
+ }
83
+
84
+ ExitWhenRebootRequired
85
+
86
+ $updateSession = New-Object -ComObject 'Microsoft.Update.Session'
87
+ $updateSession.ClientApplicationID = 'vagrant-windows-update'
88
+
89
+ Write-Output 'Searching for Windows updates...'
90
+ $updateSearcher = $updateSession.CreateUpdateSearcher()
91
+ $updatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'
92
+ $searchResult = $updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
93
+ if ($searchResult.Updates.Count) {
94
+ for ($i = 0; $i -lt $searchResult.Updates.Count; ++$i) {
95
+ $update = $searchResult.Updates.Item($i)
96
+
97
+ if ($update.InstallationBehavior.CanRequestUserInput) {
98
+ continue
99
+ }
100
+
101
+ $updateDate = $update.LastDeploymentChangeTime.ToString('yyyy-MM-dd')
102
+ $updateSize = ($update.MaxDownloadSize/1024/1024).ToString('0.##')
103
+ Write-Output "Found Windows update ($updateDate; $updateSize MB): $($update.Title)"
104
+
105
+ $update.AcceptEula() | Out-Null
106
+
107
+ if ($update.IsDownloaded) {
108
+ continue
109
+ }
110
+
111
+ $updatesToDownload.Add($update) | Out-Null
112
+ }
113
+
114
+ if ($updatesToDownload.Count) {
115
+ Write-Output 'Downloading Windows updates...'
116
+ $updateDownloader = $updateSession.CreateUpdateDownloader()
117
+ $updateDownloader.Updates = $updatesToDownload
118
+ $updateDownloader.Download() | Out-Null
119
+ }
120
+
121
+ $updatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl'
122
+ for ($i = 0; $i -lt $searchResult.Updates.Count; ++$i) {
123
+ $update = $searchResult.Updates.Item($i)
124
+ if ($update.IsDownloaded) {
125
+ $updatesToInstall.Add($update) | Out-Null
126
+ }
127
+ }
128
+
129
+ if ($updatesToInstall.Count) {
130
+ Write-Output 'Installing Windows updates...'
131
+ $updateInstaller = $updateSession.CreateUpdateInstaller()
132
+ $updateInstaller.Updates = $updatesToInstall
133
+ $installResult = $updateInstaller.Install()
134
+ if ($installResult.RebootRequired) {
135
+ ExitWhenRebootRequired
136
+ }
137
+ }
138
+ } else {
139
+ Write-Output 'No Windows updates found'
140
+ }
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vagrant-windows-update
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Rui Lopes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Vagrant plugin for installing Windows updates.
28
+ email: rgl@ruilopes.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/vagrant-windows-update.rb
34
+ - lib/vagrant-windows-update/version.rb
35
+ - lib/vagrant-windows-update/windows-update.ps1
36
+ homepage: https://github.com/rgl/vagrant-windows-update
37
+ licenses:
38
+ - LGPLv3
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.5.2
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Vagrant plugin for installing Windows updates.
60
+ test_files: []