-
Notifications
You must be signed in to change notification settings - Fork 416
/
Updates.ps1
216 lines (168 loc) · 9.93 KB
/
Updates.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
function Get-HotFixList {
<#
.SYNOPSIS
Helper - Gets a list of installed updates and hotfixes.
.DESCRIPTION
This check reads the registry in order to enumerate all the installed KB hotfixes. The output is sorted by date so that most recent patches appear first in the list. The output is similar to the output of the built-in 'Get-HotFix' powershell command. There is a major difference between this script and the 'Get-HotFix' command though. The latter relies on WMI to delegate the "enumeration" whereas this script directly parses the registry. The other benefit of this method is that it allows one to extract more information related to the KBs (although it's not in the output of this script). If the current user can't read the registry, the script falls back to the built-in 'Get-HotFix' cmdlet.
.EXAMPLE
PS C:\> Get-HotFixList
HotFixID Description InstalledBy InstalledOn
-------- ----------- ----------- -----------
KB4557968 Security Update 2020-05-11 07:37:09
KB4560366 Security Update DESKTOP-7A0AKQI\admin 2020-06-22 12:40:39
KB4566785 Security Update NT AUTHORITY\SYSTEM 2020-07-16 13:08:14
KB4570334 Security Update NT AUTHORITY\SYSTEM 2020-08-13 17:45:34
KB4577266 Security Update NT AUTHORITY\SYSTEM 2020-09-11 13:37:59
KB4537759 Security Update 2020-05-11 07:44:14
KB4561600 Security Update NT AUTHORITY\SYSTEM 2020-06-22 13:00:50
KB4578968 Update NT AUTHORITY\SYSTEM 2020-10-14 18:06:18
KB4580325 Security Update NT AUTHORITY\SYSTEM 2020-10-14 13:09:37
#>
[CmdletBinding()] Param()
function Get-PackageInfo {
Param(
[String]$Path
)
$Info = New-Object -TypeName PSObject
[xml] $PackageContentXml = Get-Content -Path $Path -ErrorAction SilentlyContinue -ErrorVariable GetContentError
if (-not $GetContentError) {
$PackageContentXml.GetElementsByTagName("assembly") | ForEach-Object {
$Info | Add-Member -MemberType "NoteProperty" -Name "DisplayName" -Value "$($_.displayName)"
$Info | Add-Member -MemberType "NoteProperty" -Name "SupportInformation" -Value "$($_.supportInformation)"
}
$PackageContentXml.GetElementsByTagName("package") | Where-Object { $null -ne $_.identifier } | ForEach-Object {
$Info | Add-Member -MemberType "NoteProperty" -Name "Identifier" -Value "$($_.identifier)"
$Info | Add-Member -MemberType "NoteProperty" -Name "ReleaseType" -Value "$($_.releaseType)"
}
$Info
}
}
if ($CachedHotFixList.Count -eq 0) {
# In the registry, one KB may have multiple entries because it can be split up into multiple
# packages. This array will help keep track of KBs that have already been checked by the
# script.
$InstalledKBs = New-Object -TypeName System.Collections.ArrayList
$AllPackages = Get-ChildItem -Path "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\Packages" -ErrorAction SilentlyContinue -ErrorVariable ErrorGetChildItem
if (-not $ErrorGetChildItem) {
$AllPackages | ForEach-Object {
# Filter only KB-related packages
if (($_.Name | Split-Path -Leaf) -Like "Package_*for_KB*") {
$PackageProperties = $_ | Get-ItemProperty
# Get the KB id, e.g.: KBXXXXXXX
$PackageName = $PackageProperties.InstallName.Split('~')[0].Split('_') | Where-Object { $_ -Like "KB*" }
if ($PackageName) {
# Check whether this KB has already been handled
if (-not ($InstalledKBs -contains $PackageName)) {
# Add the KB id to the list so we don't check it multiple times
[void]$InstalledKBs.Add($PackageName)
# Who installed this update?
$InstalledBy = Convert-SidToName -Sid $PackageProperties.InstallUser
# Get the install date. It's stored in the registry just like a FILETIME structure. So, we have to
# combine the low part and the high part and convert the result to a DateTime object.
$DateHigh = $PackageProperties.InstallTimeHigh
$DateLow = $PackageProperties.InstallTimeLow
$FileTime = $DateHigh * [Math]::Pow(2, 32) + $DateLow
$InstallDate = [DateTime]::FromFileTime($FileTime)
# Parse the package metadata file and extract some useful information...
$ServicingPackagesPath = Join-Path -Path $env:windir -ChildPath "servicing\Packages"
$PackagePath = Join-Path -Path $ServicingPackagesPath -ChildPath $PackageProperties.InstallName
$PackageInfo = Get-PackageInfo -Path $PackagePath
$Result = New-Object -TypeName PSObject
$Result | Add-Member -MemberType "NoteProperty" -Name "HotFixID" -Value "$PackageName"
$Result | Add-Member -MemberType "NoteProperty" -Name "Description" -Value "$($PackageInfo.ReleaseType)"
$Result | Add-Member -MemberType "NoteProperty" -Name "InstalledBy" -Value "$InstalledBy"
$Result | Add-Member -MemberType "NoteProperty" -Name "InstalledOnDate" -Value $InstallDate
$Result | Add-Member -MemberType "NoteProperty" -Name "InstalledOn" -Value (Convert-DateToString -Date $InstallDate)
[void]$CachedHotFixList.Add($Result)
}
}
}
}
}
else {
# If we can't read the registry, fall back to the built-in 'Get-HotFix' cmdlet
Get-HotFix | Select-Object HotFixID,Description,InstalledBy,InstalledOn | ForEach-Object {
$_ | Add-Member -MemberType "NoteProperty" -Name "InstalledOnDate" -Value $_.InstalledOn
$_.InstalledOn = Convert-DateToString -Date $_.InstalledOn
[void]$CachedHotFixList.Add($_)
}
}
}
$CachedHotFixList | ForEach-Object {
$_
}
}
function Invoke-WindowsUpdateCheck {
<#
.SYNOPSIS
Gets the last update time of the machine.
Author: @itm4n
License: BSD 3-Clause
.DESCRIPTION
The Windows Update status can be queried thanks to the Microsoft.Update.AutoUpdate COM object. It gives the last successful search time and the last successfull update installation time.
.EXAMPLE
PS C:\> Invoke-WindowsUpdateCheck
Time
----
2020-01-12 - 09:17:37
#>
[CmdletBinding()] Param()
try {
$WindowsUpdate = (New-Object -ComObject "Microsoft.Update.AutoUpdate").Results
if ($WindowsUpdate.LastInstallationSuccessDate) {
$WindowsUpdateResult = New-Object -TypeName PSObject
$WindowsUpdateResult | Add-Member -MemberType "NoteProperty" -Name "Time" -Value $(Convert-DateToString -Date $WindowsUpdate.LastInstallationSuccessDate)
$WindowsUpdateResult | Add-Member -MemberType "NoteProperty" -Name "TimeRaw" -Value $WindowsUpdate.LastInstallationSuccessDate
$WindowsUpdateResult
}
}
catch {
# We might get an access denied when querying this COM object
Write-Verbose "Error while requesting COM object Microsoft.Update.AutoUpdate."
}
}
function Invoke-HotFixCheck {
<#
.SYNOPSIS
If a patch was not installed in the last 31 days, return the latest patch that was installed, otherwise return nothing.
.DESCRIPTION
This check simply invokes the helper function 'Get-HotFixList' and sorts the results from the newest to the oldest.
.PARAMETER Info
Use this flag to get the list of all installed patches.
.EXAMPLE
PS C:\> Invoke-HotFixCheck -Info
HotFixID Description InstalledBy InstalledOn
-------- ----------- ----------- -----------
KB4578968 Update NT AUTHORITY\SYSTEM 2020-10-14 18:06:18
KB4580325 Security Update NT AUTHORITY\SYSTEM 2020-10-14 13:09:37
KB4577266 Security Update NT AUTHORITY\SYSTEM 2020-09-11 13:37:59
KB4570334 Security Update NT AUTHORITY\SYSTEM 2020-08-13 17:45:34
KB4566785 Security Update NT AUTHORITY\SYSTEM 2020-07-16 13:08:14
KB4561600 Security Update NT AUTHORITY\SYSTEM 2020-06-22 13:00:50
KB4560366 Security Update DESKTOP-7A0AKQI\admin 2020-06-22 12:40:39
KB4537759 Security Update 2020-05-11 07:44:14
KB4557968 Security Update 2020-05-11 07:37:09
#>
[CmdletBinding()] Param(
[switch] $Info,
[UInt32] $BaseSeverity
)
# Get the list of installed patches
$HotFixList = Get-HotFixList | Sort-Object -Property "InstalledOnDate" -Descending
# If Info, return the list directly
if ($Info) { $HotFixList | Select-Object HotFixID,Description,InstalledBy,InstalledOn; return }
# To get the latest patch, we can simple get the first item in the list because it is sorted in
# descending order.
$LatestHotfix = $HotFixList | Select-Object -First 1
$TimeSpan = New-TimeSpan -Start $LatestHotfix.InstalledOnDate -End $(Get-Date)
if ($TimeSpan.TotalDays -gt 31) {
$Results = $LatestHotfix | Select-Object HotFixID,Description,InstalledBy,InstalledOn
}
else {
Write-Verbose "At least one hotfix was installed in the last 31 days."
}
$Result = New-Object -TypeName PSObject
$Result | Add-Member -MemberType "NoteProperty" -Name "Result" -Value $Results
$Result | Add-Member -MemberType "NoteProperty" -Name "Severity" -Value $(if ($Results) { $BaseSeverity } else { $SeverityLevelEnum::None })
$Result
}