vagrant-windows-update 0.0.5 → 0.0.6
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.
- checksums.yaml +4 -4
- data/lib/vagrant-windows-update.rb +49 -8
- data/lib/vagrant-windows-update/version.rb +1 -1
- data/lib/vagrant-windows-update/windows-update.ps1 +67 -19
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f351ccfce6734c91491ac81b4048ab62396aefa
|
|
4
|
+
data.tar.gz: 7a1789990f433bf461e670d4083d5507369276da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0125aade3e921cb9815c92ebf7b5742ce4c3345c79e51ce4f3ee370954b61499a13e0a47a7845ad9ed7c56be02d95cbee8a545b40ac3378579b7a2be0fdde44b
|
|
7
|
+
data.tar.gz: f6795a0861c8083c45593fe1cd582e967d9478eb1751af2e3bbf5e791afc012fb3cfbded9f5dec73f7d536e3466aa1f38ca3d5664dff58efda93a2e3b4a9766b
|
|
@@ -4,25 +4,50 @@ rescue LoadError
|
|
|
4
4
|
raise "The Vagrant Windows Update plugin must be run within Vagrant."
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
if Vagrant::VERSION < "
|
|
8
|
-
raise "The Vagrant Windows Update plugin is only compatible with Vagrant
|
|
7
|
+
if Vagrant::VERSION < "2.0.3"
|
|
8
|
+
raise "The Vagrant Windows Update plugin is only compatible with Vagrant 2.0.3+"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
require "base64"
|
|
12
|
+
|
|
11
13
|
module VagrantPlugins
|
|
12
14
|
module WindowsUpdate
|
|
13
15
|
class Plugin < Vagrant.plugin("2")
|
|
14
16
|
name "Windows Update"
|
|
15
17
|
description "Vagrant plugin to update a Windows VM as a provisioning step."
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
class
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
config("windows-update", :provisioner) do
|
|
20
|
+
class Config < Vagrant.plugin("2", :config)
|
|
21
|
+
attr_accessor :filters
|
|
22
|
+
|
|
23
|
+
def initialize
|
|
24
|
+
@filters = UNSET_VALUE
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def finalize!
|
|
28
|
+
@filters = ['include:$_.AutoSelectOnWebSites'] if @filters == UNSET_VALUE
|
|
21
29
|
end
|
|
22
30
|
|
|
23
|
-
def
|
|
31
|
+
def validate(machine)
|
|
32
|
+
errors = _detected_errors
|
|
33
|
+
errors << 'filters must be a string array' unless filters_valid?
|
|
34
|
+
return { "windows-update provisioner" => errors }
|
|
24
35
|
end
|
|
25
36
|
|
|
37
|
+
def filters_valid?
|
|
38
|
+
return false if !filters.is_a?(Array)
|
|
39
|
+
filters.each do |a|
|
|
40
|
+
return false if !a.kind_of?(String)
|
|
41
|
+
end
|
|
42
|
+
return true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
provisioner("windows-update") do
|
|
50
|
+
class Provisioner < Vagrant.plugin("2", :provisioner)
|
|
26
51
|
# see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/ui.rb
|
|
27
52
|
# see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/plugin/v2/provisioner.rb
|
|
28
53
|
# see https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/plugin/v2/communicator.rb
|
|
@@ -32,7 +57,7 @@ module VagrantPlugins
|
|
|
32
57
|
@machine.communicate.upload(
|
|
33
58
|
File.join(File.dirname(__FILE__), "vagrant-windows-update", "windows-update.ps1"),
|
|
34
59
|
remote_path)
|
|
35
|
-
command = "PowerShell -ExecutionPolicy Bypass -OutputFormat Text -
|
|
60
|
+
command = "PowerShell -ExecutionPolicy Bypass -OutputFormat Text -EncodedCommand #{windows_update_encoded_command(remote_path, config.filters)}"
|
|
36
61
|
loop do
|
|
37
62
|
begin
|
|
38
63
|
until @machine.communicate.ready?
|
|
@@ -85,6 +110,22 @@ module VagrantPlugins
|
|
|
85
110
|
@machine.ui.info(data.chomp, options)
|
|
86
111
|
end
|
|
87
112
|
end
|
|
113
|
+
|
|
114
|
+
def windows_update_encoded_command(remote_path, filters)
|
|
115
|
+
# NB you can get the string back with:
|
|
116
|
+
# Base64.decode64(encoded).force_encoding("utf-16le")
|
|
117
|
+
return Base64.strict_encode64("#{remote_path}#{windows_update_filters_argument(filters)}".encode("utf-16le"))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def windows_update_filters_argument(filters)
|
|
121
|
+
return "" if !filters
|
|
122
|
+
arg = " -Filters "
|
|
123
|
+
filters.each_with_index do |filter, i|
|
|
124
|
+
arg += "," if i > 0
|
|
125
|
+
arg += "'#{filter.gsub("'", "''")}'" # escape single quotes with another single quote.
|
|
126
|
+
end
|
|
127
|
+
return arg
|
|
128
|
+
end
|
|
88
129
|
end
|
|
89
130
|
|
|
90
131
|
Provisioner
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
# see IUpdate interface
|
|
14
14
|
# at https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx
|
|
15
15
|
|
|
16
|
+
param(
|
|
17
|
+
[string[]]$Filters = @('include:$_.AutoSelectOnWebSites'),
|
|
18
|
+
[int]$UpdateLimit = 100
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
$mock = $false
|
|
22
|
+
|
|
23
|
+
function ExitWithCode($exitCode) {
|
|
24
|
+
$host.SetShouldExit($exitCode)
|
|
25
|
+
Exit
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
Set-StrictMode -Version Latest
|
|
17
29
|
|
|
18
30
|
$ErrorActionPreference = 'Stop'
|
|
@@ -21,7 +33,23 @@ trap {
|
|
|
21
33
|
Write-Output "ERROR: $_"
|
|
22
34
|
Write-Output (($_.ScriptStackTrace -split '\r?\n') -replace '^(.*)$','ERROR: $1')
|
|
23
35
|
Write-Output (($_.Exception.ToString() -split '\r?\n') -replace '^(.*)$','ERROR EXCEPTION: $1')
|
|
24
|
-
|
|
36
|
+
ExitWithCode 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ($mock) {
|
|
40
|
+
$mockWindowsUpdatePath = 'C:\Windows\Temp\windows-update-count-mock.txt'
|
|
41
|
+
if (!(Test-Path $mockWindowsUpdatePath)) {
|
|
42
|
+
Set-Content $mockWindowsUpdatePath 10
|
|
43
|
+
}
|
|
44
|
+
$count = [int]::Parse((Get-Content $mockWindowsUpdatePath).Trim())
|
|
45
|
+
if ($count) {
|
|
46
|
+
Write-Output "Synthetic reboot countdown counter is at $count"
|
|
47
|
+
Set-Content $mockWindowsUpdatePath (--$count)
|
|
48
|
+
Write-Output 'Rebooting...'
|
|
49
|
+
ExitWithCode 101
|
|
50
|
+
}
|
|
51
|
+
Write-Output 'No Windows updates found'
|
|
52
|
+
ExitWithCode 0
|
|
25
53
|
}
|
|
26
54
|
|
|
27
55
|
Add-Type @'
|
|
@@ -62,34 +90,47 @@ function Wait-Condition {
|
|
|
62
90
|
}
|
|
63
91
|
}
|
|
64
92
|
|
|
65
|
-
function ExitWhenRebootRequired(
|
|
66
|
-
$rebootRequired = $forceReboot
|
|
67
|
-
|
|
93
|
+
function ExitWhenRebootRequired($rebootRequired = $false) {
|
|
68
94
|
# check for pending Windows Updates.
|
|
69
95
|
if (!$rebootRequired) {
|
|
70
96
|
$systemInformation = New-Object -ComObject 'Microsoft.Update.SystemInfo'
|
|
71
|
-
$rebootRequired = $
|
|
97
|
+
$rebootRequired = $systemInformation.RebootRequired
|
|
72
98
|
}
|
|
73
99
|
|
|
74
100
|
# check for pending Windows Features.
|
|
75
101
|
if (!$rebootRequired) {
|
|
76
102
|
$pendingPackagesKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending'
|
|
77
103
|
$pendingPackagesCount = (Get-ChildItem -ErrorAction SilentlyContinue $pendingPackagesKey | Measure-Object).Count
|
|
78
|
-
$rebootRequired = $
|
|
104
|
+
$rebootRequired = $pendingPackagesCount -gt 0
|
|
79
105
|
}
|
|
80
106
|
|
|
81
107
|
if ($rebootRequired) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Wait-Condition {(Get-Process -ErrorAction SilentlyContinue TiWorker,TrustedInstaller | Measure-Object).Count -eq 0}
|
|
85
|
-
}
|
|
108
|
+
Write-Output 'Pending Reboot detected. Waiting for the Windows Modules Installer to exit...'
|
|
109
|
+
Wait-Condition {(Get-Process -ErrorAction SilentlyContinue TiWorker | Measure-Object).Count -eq 0}
|
|
86
110
|
Write-Output 'Rebooting...'
|
|
87
|
-
|
|
111
|
+
ExitWithCode 101
|
|
88
112
|
}
|
|
89
113
|
}
|
|
90
114
|
|
|
91
115
|
ExitWhenRebootRequired
|
|
92
116
|
|
|
117
|
+
$updateFilters = $Filters | ForEach-Object {
|
|
118
|
+
$action, $expression = $_ -split ':',2
|
|
119
|
+
New-Object PSObject -Property @{
|
|
120
|
+
Action = $action
|
|
121
|
+
Expression = [ScriptBlock]::Create($expression)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function Test-IncludeUpdate($filters, $update) {
|
|
126
|
+
foreach ($filter in $filters) {
|
|
127
|
+
if (Where-Object -InputObject $update $filter.Expression) {
|
|
128
|
+
return $filter.Action -eq 'include'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return $false
|
|
132
|
+
}
|
|
133
|
+
|
|
93
134
|
$updateSession = New-Object -ComObject 'Microsoft.Update.Session'
|
|
94
135
|
$updateSession.ClientApplicationID = 'vagrant-windows-update'
|
|
95
136
|
|
|
@@ -98,20 +139,24 @@ $updatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'
|
|
|
98
139
|
$updatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl'
|
|
99
140
|
$updateSearcher = $updateSession.CreateUpdateSearcher()
|
|
100
141
|
$searchResult = $updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
|
|
142
|
+
$rebootRequired = $false
|
|
101
143
|
for ($i = 0; $i -lt $searchResult.Updates.Count; ++$i) {
|
|
102
144
|
$update = $searchResult.Updates.Item($i)
|
|
145
|
+
$updateDate = $update.LastDeploymentChangeTime.ToString('yyyy-MM-dd')
|
|
146
|
+
$updateSize = ($update.MaxDownloadSize/1024/1024).ToString('0.##')
|
|
147
|
+
$updateSummary = "Windows update ($updateDate; $updateSize MB): $($update.Title)"
|
|
103
148
|
|
|
104
149
|
if ($update.InstallationBehavior.CanRequestUserInput) {
|
|
150
|
+
Write-Output "Skipped (CanRequestUserInput) $updateSummary"
|
|
105
151
|
continue
|
|
106
152
|
}
|
|
107
153
|
|
|
108
|
-
if (
|
|
154
|
+
if (!(Test-IncludeUpdate $updateFilters $update)) {
|
|
155
|
+
Write-Output "Skipped (filter) $updateSummary"
|
|
109
156
|
continue
|
|
110
157
|
}
|
|
111
158
|
|
|
112
|
-
|
|
113
|
-
$updateSize = ($update.MaxDownloadSize/1024/1024).ToString('0.##')
|
|
114
|
-
Write-Output "Found Windows update ($updateDate; $updateSize MB): $($update.Title)"
|
|
159
|
+
Write-Output "Found $updateSummary"
|
|
115
160
|
|
|
116
161
|
$update.AcceptEula() | Out-Null
|
|
117
162
|
|
|
@@ -120,6 +165,10 @@ for ($i = 0; $i -lt $searchResult.Updates.Count; ++$i) {
|
|
|
120
165
|
}
|
|
121
166
|
|
|
122
167
|
$updatesToInstall.Add($update) | Out-Null
|
|
168
|
+
if ($updatesToInstall.Count -ge $UpdateLimit) {
|
|
169
|
+
$rebootRequired = $true
|
|
170
|
+
break
|
|
171
|
+
}
|
|
123
172
|
}
|
|
124
173
|
|
|
125
174
|
if ($updatesToDownload.Count) {
|
|
@@ -134,8 +183,7 @@ if ($updatesToInstall.Count) {
|
|
|
134
183
|
$updateInstaller = $updateSession.CreateUpdateInstaller()
|
|
135
184
|
$updateInstaller.Updates = $updatesToInstall
|
|
136
185
|
$installResult = $updateInstaller.Install()
|
|
137
|
-
ExitWhenRebootRequired $
|
|
138
|
-
|
|
186
|
+
ExitWhenRebootRequired ($installResult.RebootRequired -or $rebootRequired)
|
|
187
|
+
} else {
|
|
188
|
+
Write-Output 'No Windows updates found'
|
|
139
189
|
}
|
|
140
|
-
|
|
141
|
-
Write-Output 'No Windows updates found'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vagrant-windows-update
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rui Lopes
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-04-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -53,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
53
53
|
version: '0'
|
|
54
54
|
requirements: []
|
|
55
55
|
rubyforge_project:
|
|
56
|
-
rubygems_version: 2.5.2
|
|
56
|
+
rubygems_version: 2.5.2.1
|
|
57
57
|
signing_key:
|
|
58
58
|
specification_version: 4
|
|
59
59
|
summary: Vagrant plugin for installing Windows updates.
|