Skip to content

Instantly share code, notes, and snippets.

@MyITGuy
Last active January 22, 2023 12:23
Show Gist options
  • Save MyITGuy/d4f6dbd1b4bcbdba7de97505ae64cbd2 to your computer and use it in GitHub Desktop.
Save MyITGuy/d4f6dbd1b4bcbdba7de97505ae64cbd2 to your computer and use it in GitHub Desktop.
PowerShell: Get-MicrosoftUpdates
$MicrosoftUpdates = Get-MicrosoftUpdates -IncludeDISM
$GetHotfixOnlyUpdates = $MicrosoftUpdates | ? {$_.Source.Count -eq 1 -and $_.Source -contains 'Get-Hotfix'}
$MicrosoftUpdateOnlyUpdates = $MicrosoftUpdates | ? {$_.Source.Count -eq 1 -and $_.Source -contains 'MicrosoftUpdate'}
$DismOnlyUpdates = $MicrosoftUpdates | ? {$_.Source.Count -eq 1 -and $_.Source -contains 'DISM'}
$GetHotfixUpdates = $MicrosoftUpdates | ? {$_.Source -contains 'Get-Hotfix'}
$MicrosoftUpdateUpdates = $MicrosoftUpdates | ? {$_.Source -contains 'MicrosoftUpdate'}
$DismUpdates = $MicrosoftUpdates | ? {$_.Source -contains 'DISM'}
$MissingFromGetHotfix = $MicrosoftUpdates | ? {$_.Source -notcontains 'Get-Hotfix'}
$MissingFromMicrosoftUpdate = $MicrosoftUpdates | ? {$_.Source -notcontains 'MicrosoftUpdate'}
$MissingFromDism = $MicrosoftUpdates | ? {$_.Source -notcontains 'DISM'}
#GetHotfixOnlyUpdates do show in Add/Remove Programs
#MicrosoftUpdateOnlyUpdates do not show in Add/Remove Programs
#DismOnlyUpdates do not show in Add/Remove Programs
#region Get-MicrosoftUpdates
function Get-MicrosoftUpdates {
[CmdletBinding(SupportsShouldProcess=$True,DefaultParameterSetName="None")]
PARAM(
[switch]$IncludeDISM
)
try {
#region Windows Installer Patches
$Installer = New-Object -ComObject WindowsInstaller.Installer
$szProductCode = [String]::Empty
$szUserSid = [String]::Empty
$dwContext = 4
$dwFilter = 15
# PatchesEx implements MsiEnumPatchesEx, https://msdn.microsoft.com/en-us/library/aa370100(v=vs.85).aspx
$Patches = $Installer.PatchesEx($szProductCode, $szUserSid, $dwContext, $dwFilter)
$UseableWindowsInstallerPatchDetails = foreach ($Patch In $Patches) {
# Returns a Patch object
# Patch object: https://msdn.microsoft.com/en-us/library/windows/desktop/aa370594(v=vs.85).aspx
$PatchProperties = [ordered]@{}
# PatchProperty, https://msdn.microsoft.com/en-us/library/aa370598(v=vs.85).aspx
$PatchProperties.Title = $Patch.PatchProperty('DisplayName')
$PatchProperties.HotfixID = $null
try {
$MatchFound = $PatchProperties.Title -match 'KB\d+(?!^\d)'
if ($MatchFound) {$PatchProperties.HotfixID = $Matches[0]}
} catch {
}
try {
$PatchProperties.Caption = $Patch.PatchProperty('MoreInfoURL')
} catch {
$PatchProperties.Caption = $null
}
$PatchProperties.InstallDate = ([datetime]::ParseExact($Patch.PatchProperty('InstallDate'), "yyyyMMdd", [System.Globalization.CultureInfo]::CurrentCulture))
$PatchProperties.Source = "WindowsInstallerPatch"
New-Object -TypeName PSObject -Property $PatchProperties | ? {$_.HotfixID}
}
#endregion Windows Installer Patches
#region Windows Installer Products
$Installer = New-Object -ComObject WindowsInstaller.Installer
$szProductCode = [String]::Empty
$szUserSid = [String]::Empty
$dwContext = 4
$dwIndex = 0
# ProductsEx implements MsiEnumProductsEx, https://msdn.microsoft.com/en-us/library/aa370102(v=vs.85).aspx
$Products = $Installer.ProductsEx($szProductCode, $szUserSid, $dwContext, $dwIndex)
$UseableWindowsInstallerProductDetails = foreach ($Product In $Products) {
# Returns a Product object
# Product object: https://msdn.microsoft.com/en-us/library/windows/desktop/aa370867(v=vs.85).aspx
$ProductProperties = [ordered]@{}
# ProductProperty, https://msdn.microsoft.com/en-us/library/aa370598(v=vs.85).aspx
$ProductProperties.Title = $Product.InstallProperty('ProductName')
$ProductProperties.HotfixID = $null
try {
$MatchFound = $ProductProperties.Title -match 'KB\d+(?!^\d)'
if ($MatchFound) {$ProductProperties.HotfixID = $Matches[0]}
} catch {
}
try {
$ProductProperties.InstallDate = ([datetime]::ParseExact($Product.InstallProperty('InstallDate'), "yyyyMMdd", [System.Globalization.CultureInfo]::CurrentCulture))
} catch {
$ProductProperties.InstallDate = $null
}
$ProductProperties.Source = "WindowsInstallerProduct"
New-Object -TypeName PSObject -Property $ProductProperties | ? {$_.HotfixID}
}
#endregion Windows Installer Products
#region Microsoft Updates
$IUpdateSession = New-Object -ComObject Microsoft.Update.Session
$IUpdateSearcher = $IUpdateSession.CreateUpdateSearcher()
$HistoryCount = $IUpdateSearcher.GetTotalHistoryCount()
$IUpdateHistoryEntryCollection = $IUpdateSearcher.QueryHistory(0, $HistoryCount)
# Extract useable data
$UseableMicrosoftUpdateDetails = $IUpdateHistoryEntryCollection | % {
try {
$MatchFound = $_.Title -match 'KB\d+(?!^\d)'
if ($MatchFound) {$tmpHotfixId = $Matches[0]}
} catch {
$tmpHotfixId = [string]::Empty
}
New-Object -TypeName PSObject -Property @{
InstallDate = ([datetime]$_.Date).ToLocalTime()
HotfixID = $tmpHotfixId
Title = $_.Title
Source = "MicrosoftUpdate"
}
}
#endregion Microsoft Updates
#region DISM
if ($IncludeDISM) {
if ((New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
$DISMDetails = dism.exe /Online /Get-Packages /Format:Table | ? {$_.Contains("|") -and -not $_.StartsWith("-")} | % {($_.Split('|').Trim() -join ',')} | ConvertFrom-Csv
$UseableDISMDetails = $DISMDetails | ? {@('Update','Security Update') -contains $_."Release Type" -and $_."Package Identity".StartsWith('Package_for_') -eq $true -and $_."Package Identity".StartsWith('Package_for_RollupFix') -eq $false} | foreach {
New-Object -TypeName PSObject -Property @{
InstallDate = ([datetime]$_."Install Time").ToLocalTime()
HotfixID = (($_."Package Identity".TrimStart('Package_for_')).split('~'))[0]
Source = "DISM"
}
}
} else {
Write-Error "IncludeDISM parameter can only be used with accounts holding the Adminstrator role."
}
}
#endregion DISM
#region Get-Hotfix
# Get installed hotfixes, extract useable data
$InstalledHotfixes = Get-HotFix | Select Caption, Description, HotfixID, InstalledBy, @{Name='InstallDate';Expression={$_.InstalledOn}}, @{Name='Source';Expression={'Get-Hotfix'}}, @{Name='Title';Expression={$null}}
#endregion Get-Hotfix
# Get only hotfix IDs
$AllHotfixIDs = @()
$AllHotfixIDs += $UseableWindowsInstallerProductDetails | Select -ExpandProperty HotfixID
$AllHotfixIDs += $UseableWindowsInstallerPatchDetails | Select -ExpandProperty HotfixID
$AllHotfixIDs += $UseableMicrosoftUpdateDetails | Select -ExpandProperty HotfixID
$AllHotfixIDs += $UseableDISMDetails | Select -ExpandProperty HotfixID
$AllHotfixIDs += $InstalledHotfixes | Select -ExpandProperty HotfixID
$AllHotfixIDs = $AllHotfixIDs | Sort -Unique
foreach ($SingleHotfixID IN $AllHotfixIDs) {
$hash = @{}
$hash.ComputerName = $env:COMPUTERNAME
$hash.HotfixID = $SingleHotfixID
$WindowsInstallerProductDetail = $UseableWindowsInstallerProductDetails | ? {$_.HotfixID -eq $hash.HotfixID}
$WindowsInstallerPatchDetail = $UseableWindowsInstallerPatchDetails | ? {$_.HotfixID -eq $hash.HotfixID}
$MicrosoftUpdateDetail = $UseableMicrosoftUpdateDetails | ? {$_.HotfixID -eq $hash.HotfixID}
$UseableDISMDetail = $UseableDISMDetails | ? {$_.HotfixID -eq $hash.HotfixID}
$GetHotfixDetail = $InstalledHotfixes | ? {$_.HotfixID -eq $hash.HotfixID}
$hash.Source = @()
$hash.InstallDate = $null
$hash.Title = $null
$hash.Caption = $null
$hash.Description = $null
$hash.InstalledBy = $null
$PrioritizedDetails = @($GetHotfixDetail, $UseableDISMDetail, $MicrosoftUpdateDetail, $WindowsInstallerPatchDetail, $WindowsInstallerProductDetail)
foreach ($PrioritizedDetail In $PrioritizedDetails) {
if ($PrioritizedDetail.Source) {$hash.Source += $PrioritizedDetail.Source | Sort | Select -Last 1}
if (!$hash.InstallDate -and $PrioritizedDetail.InstallDate) {$hash.InstallDate = $PrioritizedDetail.InstallDate | Sort | Select -Last 1}
if (!$hash.Title -and $PrioritizedDetail.Title) {$hash.Title = $PrioritizedDetail.Title | Sort | Select -Last 1}
if (!$hash.Caption -and $PrioritizedDetail.Caption) {$hash.Caption = $PrioritizedDetail.Caption | Sort | Select -Last 1}
if (!$hash.Description -and $PrioritizedDetail.Description) {$hash.Description = $PrioritizedDetail.Description | Sort | Select -Last 1}
if (!$hash.InstalledBy -and $PrioritizedDetail.InstalledBy) {$hash.InstalledBy = $PrioritizedDetail.InstalledBy | Sort | Select -Last 1}
}
New-Object -TypeName PSObject -Property $hash
}
} catch {
throw $_
} finally {
}
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment