LSUClient.psm1
#Requires -Version 5.0 Set-StrictMode -Version 1.0 enum Severity { Critical = 1 Recommended = 2 Optional = 3 } # Source for this information is: https://download.lenovo.com/cdrt/docs/DG-SystemUpdateSuite.pdf page 57 enum PackageType { Reserved = 0 Application = 1 Driver = 2 BIOS = 3 Firmware = 4 } enum DependencyParserState { DO_HAVE = 0 DO_NOT_HAVE = 1 } # Check for old Windows versions in a manner that is compatible with PowerShell 2.0 all the way up to 7.1 $WindowsVersion = (New-Object -TypeName 'System.Management.ManagementObjectSearcher' -ArgumentList "SELECT Version FROM Win32_OperatingSystem").Get() | Select-Object -ExpandProperty Version if ($WindowsVersion -notlike "10.*") { throw "This module requires Windows 10." } $script:CachedHardwareTable = @{} [int]$script:XMLTreeDepth = 0 # Internal class PackageFilePointer { [ValidateNotNullOrEmpty()] [string] $Name [ValidateNotNullOrEmpty()] [string] $Container [ValidateNotNullOrEmpty()] [string] $AbsoluteLocation [ValidateNotNullOrEmpty()] [string] $LocationType [ValidateNotNullOrEmpty()] [string] $Kind [string] $Checksum [Int64] $Size # Constructor with file name PackageFilePointer ( [string] $Name, [string] $AbsoluteLocation, [string] $LocationType, [string] $Kind, [string] $Checksum, [Int64] $Size ) { $this.AbsoluteLocation = $AbsoluteLocation $this.Name = $Name -replace '^.*[\\/]' $this.Container = $AbsoluteLocation -replace '[^\\/]*$' $this.LocationType = $LocationType $this.Kind = $Kind $this.Checksum = $Checksum $this.Size = $Size } # Constructor without explicit file name PackageFilePointer ( [string] $AbsoluteLocation, [string] $LocationType, [string] $Kind, [string] $Checksum, [Int64] $Size ) { $this.AbsoluteLocation = $AbsoluteLocation $this.Name = $AbsoluteLocation -replace '^.*[\\/]' $this.Container = $AbsoluteLocation -replace '[^\\/]*$' $this.LocationType = $LocationType $this.Kind = $Kind $this.Checksum = $Checksum $this.Size = $Size } } # Internal class PackageXmlPointer : PackageFilePointer { [string] $Category # Constructor with file name PackageXmlPointer ( [string] $Name, [string] $AbsoluteLocation, [string] $LocationType, [string] $Kind, [string] $Checksum, [Int64] $Size, [string] $Category ) : base ( $Name, $AbsoluteLocation, $LocationType, $Kind, $Checksum, $Size ) { $this.Category = $Category } # Constructor without explicit file name PackageXmlPointer ( [string] $AbsoluteLocation, [string] $LocationType, [string] $Kind, [string] $Checksum, [Int64] $Size, [string] $Category ) : base ( $AbsoluteLocation, $LocationType, $Kind, $Checksum, $Size ) { $this.Category = $Category } } # Public class LenovoPackage { [string] $ID hidden [string] $Name [string] $Title [Nullable[PackageType]] $Type [string] $Category [version] $Version [Severity] $Severity [DateTime] $ReleaseDate [int] $RebootType [string] $Vendor [Int64] $Size [string] $URL hidden [System.Collections.Generic.List[PackageFilePointer]] $Files [PackageExtractInfo] $Extracter [PackageInstallInfo] $Installer [Nullable[bool]] $IsApplicable [Nullable[bool]] $IsInstalled } # Public class PackageExtractInfo { [string] $Command [string] $FileName [int64] $FileSize [string] $FileSHA PackageExtractInfo ([System.Xml.XmlElement]$PackageXML) { $this.Command = $PackageXML.ExtractCommand $this.FileName = $PackageXML.Files.Installer.File.Name # Unused, kept for backwards compatibility $this.FileSize = $PackageXML.Files.Installer.File.Size # Unused, kept for backwards compatibility $this.FileSHA = $PackageXML.Files.Installer.File.CRC # Unused, kept for backwards compatibility } } # Public class PackageInstallInfo { [bool] $Unattended [ValidateNotNullOrEmpty()] [string] $InstallType [int64[]] $SuccessCodes [string] $InfFile [string] $Command PackageInstallInfo ([System.Xml.XmlElement]$PackageXML) { $this.InstallType = $PackageXML.Install.type $this.SuccessCodes = $PackageXML.Install.rc -split ',' $this.InfFile = $PackageXML.Install.INFCmd.INFfile $this.Command = $PackageXML.Install.Cmdline.'#text' if (($PackageXML.Reboot.type -in 0, 3) -or ($PackageXML.Install.Cmdline.'#text' -match 'winuptp\.exe|Flash\.cmd') -or ($PackageXML.Install.type -eq 'INF')) { $this.Unattended = $true } else { $this.Unattended = $false } } } # Internal class BiosUpdateInfo { [ValidateNotNullOrEmpty()] [bool] $WasRun [int64] $Timestamp [ValidateNotNullOrEmpty()] [int64] $ExitCode [string] $LogMessage [ValidateNotNullOrEmpty()] [string] $ActionNeeded } # Internal class ProcessReturnInformation { [ValidateNotNullOrEmpty()] [string] $FilePath [string] $Arguments [string] $WorkingDirectory [string[]] $StandardOutput [string[]] $StandardError [Int64] $ExitCode [TimeSpan] $Runtime } # Import all private functions foreach ($function in (Get-ChildItem "$PSScriptRoot\private" -File -ErrorAction Ignore)) { . $function.FullName } # Import all public functions foreach ($function in (Get-ChildItem "$PSScriptRoot\public" -File -ErrorAction Ignore)) { . $function.FullName } |