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.
- checksums.yaml +7 -0
- data/lib/vagrant-windows-update.rb +94 -0
- data/lib/vagrant-windows-update/version.rb +5 -0
- data/lib/vagrant-windows-update/windows-update.ps1 +140 -0
- metadata +60 -0
checksums.yaml
ADDED
|
@@ -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,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: []
|