Private/Status/Get-StatusPendingUpdatesInternal.ps1
|
#Requires -Version 5.1 function Get-StatusPendingUpdatesInternal { [CmdletBinding()] param( [Parameter(Mandatory)] [psobject]$OemTooling, [Parameter()] [ValidateRange(1, 2000)] [int]$MaxPerProvider = 200 ) $pending = New-Object System.Collections.Generic.List[object] $errors = New-Object System.Collections.Generic.List[object] $providers = @{} function Add-ProviderStateInternal { param( [string]$Name, [bool]$Required, [bool]$Available, [bool]$Attempted, [bool]$Success, [string]$SkippedReason, [string]$Error, [int]$PendingCount ) $providers[$Name] = [pscustomobject]@{ Name = $Name Required = $Required Available = $Available Attempted = $Attempted Success = $Success SkippedReason = $SkippedReason Error = $Error PendingCount = $PendingCount } } function Add-ScanErrorInternal { param( [string]$Provider, [string]$Stage, [string]$Message, [hashtable]$Details ) $errors.Add([pscustomobject]@{ Provider = $Provider Stage = $Stage Message = $Message Details = $Details }) | Out-Null } $detected = $null try { $detected = [string]$OemTooling.Detected } catch { $detected = 'Other' } $oemRequiredProvider = $null if ($detected -eq 'Dell') { $oemRequiredProvider = 'Dell' } elseif ($detected -eq 'Lenovo') { $oemRequiredProvider = 'Lenovo' } #region Dell (scan-only, requires existing DCU) $dellRequired = ($oemRequiredProvider -eq 'Dell') $dcuPath = $null try { $dcuPath = $OemTooling.Dell.DCU.CliPath } catch { $dcuPath = $null } if (-not $dcuPath) { try { $dcuPath = Get-DellCommandUpdatePath } catch { $dcuPath = $null } } if (-not $dcuPath -or -not (Test-Path $dcuPath)) { Add-ProviderStateInternal -Name 'Dell' -Required:$dellRequired -Available:$false -Attempted:$false -Success:$false -SkippedReason 'ToolMissing' -Error $null -PendingCount 0 if ($dellRequired) { Add-ScanErrorInternal -Provider 'Dell' -Stage 'Prereq' -Message 'Dell Command Update CLI not found; status mode will not auto-install tooling.' -Details @{ CliPath = $dcuPath } } } else { $reportPath = Join-Path $env:ProgramData 'Dell\UpdateScan' try { if (-not (Test-Path $reportPath)) { New-Item -Path $reportPath -ItemType Directory -Force | Out-Null } } catch { } $applicableXml = Join-Path $reportPath 'DCUApplicableUpdates.xml' try { $scanArgs = @( '/scan' '-silent' "-report=$reportPath" "-updateType=driver,bios,firmware,application" "-updateSeverity=security,recommended,optional" ) $null = & $dcuPath @scanArgs 2>&1 $updates = @() if (Test-Path $applicableXml) { try { [xml]$updatesXml = Get-Content -Path $applicableXml -ErrorAction Stop $nodes = @($updatesXml.updates.update) foreach ($u in ($nodes | Select-Object -First $MaxPerProvider)) { $updates += [pscustomobject]@{ Provider = 'Dell' UpdateType = $u.type Title = $u.name Identifier = $u.releaseid InstalledVersion = $null AvailableVersion = $u.version Severity = $u.urgency RebootRequired = $null Source = 'DCUApplicableUpdates.xml' } } } catch { Add-ScanErrorInternal -Provider 'Dell' -Stage 'Parse' -Message $_.Exception.Message -Details @{ Report = $applicableXml } } } foreach ($x in $updates) { $pending.Add($x) | Out-Null } Add-ProviderStateInternal -Name 'Dell' -Required:$dellRequired -Available:$true -Attempted:$true -Success:$true -SkippedReason $null -Error $null -PendingCount @($updates).Count } catch { Add-ProviderStateInternal -Name 'Dell' -Required:$dellRequired -Available:$true -Attempted:$true -Success:$false -SkippedReason $null -Error $_.Exception.Message -PendingCount 0 Add-ScanErrorInternal -Provider 'Dell' -Stage 'Scan' -Message $_.Exception.Message -Details @{ CliPath = $dcuPath } } } #endregion #region Lenovo (scan-only, requires LSUClient already installed) $lenovoRequired = ($oemRequiredProvider -eq 'Lenovo') $lsuPresent = $false try { $lsuPresent = [bool]$OemTooling.Lenovo.LSUClientModulePresent } catch { $lsuPresent = $false } if (-not $lsuPresent) { Add-ProviderStateInternal -Name 'Lenovo' -Required:$lenovoRequired -Available:$false -Attempted:$false -Success:$false -SkippedReason 'ToolMissing' -Error $null -PendingCount 0 if ($lenovoRequired) { Add-ScanErrorInternal -Provider 'Lenovo' -Stage 'Prereq' -Message 'LSUClient module not installed; status mode will not auto-install tooling.' -Details @{} } } else { try { Import-Module LSUClient -Force -ErrorAction Stop $allUpdates = @() try { $allUpdates = @(Get-LSUpdate) } catch { $allUpdates = @() } $updates = @() foreach ($u in ($allUpdates | Where-Object { $_.Installer.Unattended } | Select-Object -First $MaxPerProvider)) { $updates += [pscustomobject]@{ Provider = 'Lenovo' UpdateType = $u.Type Title = $u.Title Identifier = $u.ID InstalledVersion = $null AvailableVersion = $u.Version Severity = $u.Severity RebootRequired = (try { [bool]$u.Reboot.Required } catch { $null }) Source = 'LSUClient' } } foreach ($x in $updates) { $pending.Add($x) | Out-Null } Add-ProviderStateInternal -Name 'Lenovo' -Required:$lenovoRequired -Available:$true -Attempted:$true -Success:$true -SkippedReason $null -Error $null -PendingCount @($updates).Count } catch { Add-ProviderStateInternal -Name 'Lenovo' -Required:$lenovoRequired -Available:$true -Attempted:$true -Success:$false -SkippedReason $null -Error $_.Exception.Message -PendingCount 0 Add-ScanErrorInternal -Provider 'Lenovo' -Stage 'Scan' -Message $_.Exception.Message -Details @{} } } #endregion #region Intel (scan-only, uses existing Get-IntelDriverUpdates) $intelDevices = @() try { $intelDevices = @(Get-IntelDevices) } catch { $intelDevices = @() } if (-not $intelDevices -or $intelDevices.Count -eq 0) { Add-ProviderStateInternal -Name 'Intel' -Required:$false -Available:$false -Attempted:$false -Success:$true -SkippedReason 'NoHardware' -Error $null -PendingCount 0 } else { try { $intelUpdates = @() try { $intelUpdates = @(Get-IntelDriverUpdates) } catch { $intelUpdates = @() } $updates = @() foreach ($u in ($intelUpdates | Select-Object -First $MaxPerProvider)) { $updates += [pscustomobject]@{ Provider = 'Intel' UpdateType = 'Driver' Title = $u.PackageName Identifier = $u.PackageId InstalledVersion = $u.InstalledVersion AvailableVersion = $u.AvailableVersion Severity = $null RebootRequired = $null DeviceName = $u.DeviceName Category = $u.Category Source = 'IntelDSAFeed' } } foreach ($x in $updates) { $pending.Add($x) | Out-Null } Add-ProviderStateInternal -Name 'Intel' -Required:$false -Available:$true -Attempted:$true -Success:$true -SkippedReason $null -Error $null -PendingCount @($updates).Count } catch { Add-ProviderStateInternal -Name 'Intel' -Required:$false -Available:$true -Attempted:$true -Success:$false -SkippedReason $null -Error $_.Exception.Message -PendingCount 0 Add-ScanErrorInternal -Provider 'Intel' -Stage 'Scan' -Message $_.Exception.Message -Details @{} } } #endregion #region Windows Update (driver updates via COM API; no PSWindowsUpdate install) try { $wu = Get-WindowsUpdatePendingInternal -DriversOnly -MaxUpdates $MaxPerProvider $updates = @() if ($wu.Success -and $wu.Updates) { foreach ($u in @($wu.Updates)) { $updates += [pscustomobject]@{ Provider = 'WindowsUpdate' UpdateType = 'Driver' Title = $u.Title Identifier = $u.Identifier InstalledVersion = $null AvailableVersion = $u.AvailableVersion Severity = $null RebootRequired = $u.RebootRequired KBArticleIDs = $u.KBArticleIDs DriverManufacturer = $u.DriverManufacturer DriverModel = $u.DriverModel DriverClass = $u.DriverClass Source = 'WUA' } } } foreach ($x in ($updates | Select-Object -First $MaxPerProvider)) { $pending.Add($x) | Out-Null } Add-ProviderStateInternal -Name 'WindowsUpdate' -Required:$true -Available:$true -Attempted:$true -Success:$wu.Success -SkippedReason $null -Error $wu.Error -PendingCount @($updates).Count if (-not $wu.Success) { Add-ScanErrorInternal -Provider 'WindowsUpdate' -Stage 'Scan' -Message $wu.Error -Details @{} } } catch { Add-ProviderStateInternal -Name 'WindowsUpdate' -Required:$true -Available:$true -Attempted:$true -Success:$false -SkippedReason $null -Error $_.Exception.Message -PendingCount 0 Add-ScanErrorInternal -Provider 'WindowsUpdate' -Stage 'Scan' -Message $_.Exception.Message -Details @{} } #endregion # NOTE: Avoid `@($pending)` on generic lists; it can trip dynamic binder edge cases in PS 5.1. return [pscustomobject]@{ PendingUpdates = $pending.ToArray() Errors = $errors.ToArray() Providers = [pscustomobject]$providers } } |