win-updates.ps1 8.87 KB
Newer Older
1
param($global:RestartRequired=0,
2
        $global:MoreUpdates=0,
3
        $global:MaxCycles=5,
4
5
        $MaxUpdatesPerCycle=500,
        $BeginWithRestart=0)
6

7
8
9
10
11
12
$Logfile = "C:\Windows\Temp\win-updates.log"

function LogWrite {
   Param ([string]$logstring)
   $now = Get-Date -format s
   Add-Content $Logfile -value "$now $logstring"
aaronk1's avatar
aaronk1 committed
13
   Write-Output $logstring
14
15
}

16
function Check-ContinueRestartOrEnd() {
17
18
19
    $RegistryKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
    $RegistryEntry = "InstallWindowsUpdates"
    switch ($global:RestartRequired) {
Stefan Scherer's avatar
Stefan Scherer committed
20
        0 {
21
22
            $prop = (Get-ItemProperty $RegistryKey).$RegistryEntry
            if ($prop) {
23
                LogWrite "Restart Registry Entry Exists - Removing It"
24
25
                Remove-ItemProperty -Path $RegistryKey -Name $RegistryEntry -ErrorAction SilentlyContinue
            }
Stefan Scherer's avatar
Stefan Scherer committed
26

27
            LogWrite "No Restart Required"
28
            Check-WindowsUpdates
29

30
31
32
            if (($global:MoreUpdates -eq 1) -and ($script:Cycles -le $global:MaxCycles)) {
                Install-WindowsUpdates
            } elseif ($script:Cycles -gt $global:MaxCycles) {
33
                LogWrite "Exceeded Cycle Count - Stopping"
aaronk1's avatar
aaronk1 committed
34
                & "a:\enable-winrm.ps1"
Stefan Scherer's avatar
Stefan Scherer committed
35
            } else {
36
                LogWrite "Done Installing Windows Updates"
aaronk1's avatar
aaronk1 committed
37
                & "a:\enable-winrm.ps1"
38
39
40
41
42
            }
        }
        1 {
            $prop = (Get-ItemProperty $RegistryKey).$RegistryEntry
            if (-not $prop) {
43
                LogWrite "Restart Registry Entry Does Not Exist - Creating It"
44
                Set-ItemProperty -Path $RegistryKey -Name $RegistryEntry -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File $($script:ScriptPath) -MaxUpdatesPerCycle $($MaxUpdatesPerCycle)"
45
            } else {
46
                LogWrite "Restart Registry Entry Exists Already"
47
            }
Stefan Scherer's avatar
Stefan Scherer committed
48

49
            LogWrite "Restart Required - Restarting..."
50
51
            Restart-Computer
        }
52
        default {
53
            LogWrite "Unsure If A Restart Is Required"
54
55
            break
        }
56
57
58
59
    }
}

function Install-WindowsUpdates() {
60
    $script:Cycles++
61
    LogWrite "Evaluating Available Updates with limit of $($MaxUpdatesPerCycle):"
62
    $UpdatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'
63
    $script:i = 0;
64
    $CurrentUpdates = $SearchResult.Updates
65
    while($script:i -lt $CurrentUpdates.Count -and $script:CycleUpdateCount -lt $MaxUpdatesPerCycle) {
66
        $Update = $CurrentUpdates.Item($script:i)
aaronk1's avatar
aaronk1 committed
67
        if (($null -ne $Update) -and (!$Update.IsDownloaded)) {
68
69
            [bool]$addThisUpdate = $false
            if ($Update.InstallationBehavior.CanRequestUserInput) {
70
                LogWrite "> Skipping: $($Update.Title) because it requires user input"
71
72
            } else {
                if (!($Update.EulaAccepted)) {
Stefan Scherer's avatar
Stefan Scherer committed
73
74
75
76
                    LogWrite "> Note: $($Update.Title) has a license agreement that must be accepted. Accepting the license."
                    $Update.AcceptEula()
                    [bool]$addThisUpdate = $true
                    $script:CycleUpdateCount++
77
                } else {
Stefan Scherer's avatar
Stefan Scherer committed
78
79
                    [bool]$addThisUpdate = $true
                    $script:CycleUpdateCount++
80
81
                }
            }
Stefan Scherer's avatar
Stefan Scherer committed
82

83
            if ([bool]$addThisUpdate) {
84
                LogWrite "Adding: $($Update.Title)"
85
86
                $UpdatesToDownload.Add($Update) |Out-Null
            }
Stefan Scherer's avatar
Stefan Scherer committed
87
        }
88
        $script:i++
89
    }
90

91
    if ($UpdatesToDownload.Count -eq 0) {
92
        LogWrite "No Updates To Download..."
93
    } else {
94
        LogWrite 'Downloading Updates...'
95
96
97
98
99
100
101
102
        $ok = 0;
        while (! $ok) {
            try {
                $Downloader = $UpdateSession.CreateUpdateDownloader()
                $Downloader.Updates = $UpdatesToDownload
                $Downloader.Download()
                $ok = 1;
            } catch {
103
104
                LogWrite $_.Exception | Format-List -force
                LogWrite "Error downloading updates. Retrying in 30s."
105
106
107
108
                $script:attempts = $script:attempts + 1
                Start-Sleep -s 30
            }
        }
109
    }
Stefan Scherer's avatar
Stefan Scherer committed
110

111
112
    $UpdatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl'
    [bool]$rebootMayBeRequired = $false
113
    LogWrite 'The following updates are downloaded and ready to be installed:'
114
115
    foreach ($Update in $SearchResult.Updates) {
        if (($Update.IsDownloaded)) {
116
            LogWrite "> $($Update.Title)"
117
            $UpdatesToInstall.Add($Update) |Out-Null
118

119
120
121
122
123
            if ($Update.InstallationBehavior.RebootBehavior -gt 0){
                [bool]$rebootMayBeRequired = $true
            }
        }
    }
Stefan Scherer's avatar
Stefan Scherer committed
124

125
    if ($UpdatesToInstall.Count -eq 0) {
126
        LogWrite 'No updates available to install...'
127
128
        $global:MoreUpdates=0
        $global:RestartRequired=0
aaronk1's avatar
aaronk1 committed
129
        & "a:\enable-winrm.ps1"
130
        break
131
132
    }

133
    if ($rebootMayBeRequired) {
134
        LogWrite 'These updates may require a reboot'
135
136
        $global:RestartRequired=1
    }
Stefan Scherer's avatar
Stefan Scherer committed
137

138
    LogWrite 'Installing updates...'
139

140
141
142
    $Installer = $script:UpdateSession.CreateUpdateInstaller()
    $Installer.Updates = $UpdatesToInstall
    $InstallationResult = $Installer.Install()
Stefan Scherer's avatar
Stefan Scherer committed
143

144
145
146
    LogWrite "Installation Result: $($InstallationResult.ResultCode)"
    LogWrite "Reboot Required: $($InstallationResult.RebootRequired)"
    LogWrite 'Listing of updates installed and individual installation results:'
Stefan Scherer's avatar
Stefan Scherer committed
147
    if ($InstallationResult.RebootRequired) {
148
149
150
151
        $global:RestartRequired=1
    } else {
        $global:RestartRequired=0
    }
Stefan Scherer's avatar
Stefan Scherer committed
152

153
154
155
156
    for($i=0; $i -lt $UpdatesToInstall.Count; $i++) {
        New-Object -TypeName PSObject -Property @{
            Title = $UpdatesToInstall.Item($i).Title
            Result = $InstallationResult.GetUpdateResult($i).ResultCode
157
        }
158
159
        LogWrite "Item: $($UpdatesToInstall.Item($i).Title)"
        LogWrite "Result: $($InstallationResult.GetUpdateResult($i).ResultCode)"
160
    }
Stefan Scherer's avatar
Stefan Scherer committed
161

162
    Check-ContinueRestartOrEnd
163
}
164

165
function Check-WindowsUpdates() {
166
    LogWrite "Checking For Windows Updates"
167
    $Username = $env:USERDOMAIN + "\" + $env:USERNAME
Stefan Scherer's avatar
Stefan Scherer committed
168

169
    New-EventLog -Source $ScriptName -LogName 'Windows Powershell' -ErrorAction SilentlyContinue
Stefan Scherer's avatar
Stefan Scherer committed
170

171
    $Message = "Script: " + $ScriptPath + "`nScript User: " + $Username + "`nStarted: " + (Get-Date).toString()
172

173
    Write-EventLog -LogName 'Windows Powershell' -Source $ScriptName -EventID "104" -EntryType "Information" -Message $Message
174
    LogWrite $Message
175

176
    $script:UpdateSearcher = $script:UpdateSession.CreateUpdateSearcher()
177
178
179
180
181
182
183
184
    $script:successful = $FALSE
    $script:attempts = 0
    $script:maxAttempts = 12
    while(-not $script:successful -and $script:attempts -lt $script:maxAttempts) {
        try {
            $script:SearchResult = $script:UpdateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
            $script:successful = $TRUE
        } catch {
185
186
            LogWrite $_.Exception | Format-List -force
            LogWrite "Search call to UpdateSearcher was unsuccessful. Retrying in 10s."
187
188
189
190
191
            $script:attempts = $script:attempts + 1
            Start-Sleep -s 10
        }
    }

192
    if ($SearchResult.Updates.Count -ne 0) {
193
        $Message = "There are " + $SearchResult.Updates.Count + " more updates."
194
        LogWrite $Message
195
        try {
bigwave's avatar
bigwave committed
196
197
198
199
200
201
            for($i=0; $i -lt $script:SearchResult.Updates.Count; $i++) {
              LogWrite $script:SearchResult.Updates.Item($i).Title
              LogWrite $script:SearchResult.Updates.Item($i).Description
              LogWrite $script:SearchResult.Updates.Item($i).RebootRequired
              LogWrite $script:SearchResult.Updates.Item($i).EulaAccepted
          }
202
203
            $global:MoreUpdates=1
        } catch {
204
205
            LogWrite $_.Exception | Format-List -force
            LogWrite "Showing SearchResult was unsuccessful. Rebooting."
206
207
208
            $global:RestartRequired=1
            $global:MoreUpdates=0
            Check-ContinueRestartOrEnd
209
            LogWrite "Show never happen to see this text!"
210
211
            Restart-Computer
        }
212
    } else {
213
        LogWrite 'There are no applicable updates'
214
215
216
        $global:RestartRequired=0
        $global:MoreUpdates=0
    }
217
218
219
220
221
222
223
224
225
}

$script:ScriptName = $MyInvocation.MyCommand.ToString()
$script:ScriptPath = $MyInvocation.MyCommand.Path
$script:UpdateSession = New-Object -ComObject 'Microsoft.Update.Session'
$script:UpdateSession.ClientApplicationID = 'Packer Windows Update Installer'
$script:UpdateSearcher = $script:UpdateSession.CreateUpdateSearcher()
$script:SearchResult = New-Object -ComObject 'Microsoft.Update.UpdateColl'
$script:Cycles = 0
226
$script:CycleUpdateCount = 0
227

228
229
230
231
232
if ($BeginWithRestart) {
  $global:RestartRequired = 1
  Check-ContinueRestartOrEnd
}

233
234
Check-WindowsUpdates
if ($global:MoreUpdates -eq 1) {
235
    Install-WindowsUpdates
236
} else {
237
    Check-ContinueRestartOrEnd
Stefan Scherer's avatar
Stefan Scherer committed
238
}