QCT-Update-Management.psm1
<#
.SYNOPSIS <Overview of script> .DESCRIPTION <Brief description of script> .PARAMETER <Parameter_Name> <Brief description of parameter input required. Repeat this attribute if required> .INPUTS <Inputs if any, otherwise state None> .OUTPUTS <Outputs if any, otherwise state None - example: Log file stored in C:\Windows\Temp\<name>.log> .NOTES Version: 1.5.18 Author: Jim.Lin Creation Date: 2023.11.20 Purpose/Change: Bugfix. .EXAMPLE <Example goes here. Repeat this attribute for more than one example> #> #---------------------------------------------------------[Initialisations]-------------------------------------------------------- #Set Error Action to Silently Continue/Stop $ErrorActionPreference = 'Stop' #----------------------------------------------------------[Declarations]---------------------------------------------------------- #-----------------------------------------------------------[Functions]------------------------------------------------------------ function callProcessStdin($ProcessPath, $CmdArgs, $StdinArray) { $ErrorActionPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = $ProcessPath $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardInput = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $CmdArgs $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit(3 * 1000) | Out-Null foreach ($command in $StdinArray) { $p.StandardInput.WriteLine($command) $p.WaitForExit(1 * 1000) | Out-Null if ($command.IndexOf('dl -f') -ge 0) { $p.WaitForExit(60 * 1000) | Out-Null } } $stdout = $p.StandardOutput.ReadToEnd() return $stdout } function callProcess($ProcessPath, $CmdArgs) { $ErrorActionPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ASLocalWDACPolicyMode = 1 if ($(Get-Command Get-ASLocalWDACPolicyMode -ErrorAction SilentlyContinue) -ne $null) { # to redirect the message from the verbose stream (stream #4) to the $null $ASLocalWDACPolicyMode = Get-ASLocalWDACPolicyMode 4> $null } if ($ASLocalWDACPolicyMode -eq 2) { $currentPath = Get-Location Set-Location $PSScriptRoot $fileName = Split-Path $ProcessPath -Leaf $fileName = ".\$filename" $result = & $fileName $CmdArgs.Split(' ') Set-Location $currentPath return $result } else { $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = $ProcessPath $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $CmdArgs $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit(3 * 1000) | Out-Null $stdout = $p.StandardOutput.ReadToEnd() return $stdout } } function callProcessWorkingDirectory($ProcessPath, $CmdArgs) { $ErrorActionPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ASLocalWDACPolicyMode = 1 if ($(Get-Command Get-ASLocalWDACPolicyMode -ErrorAction SilentlyContinue) -ne $null) { $ASLocalWDACPolicyMode = Get-ASLocalWDACPolicyMode 4> $null } if ($ASLocalWDACPolicyMode -eq 2) { $currentPath = Get-Location Set-Location $PSScriptRoot $fileName = Split-Path $ProcessPath -Leaf $fileName = ".\$filename" $result = & $fileName $CmdArgs.Split(' ') Set-Location $currentPath return $result } else { $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = $ProcessPath $pinfo.WorkingDirectory = Split-Path $ProcessPath $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $CmdArgs $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit(3 * 1000) | Out-Null $stdout = $p.StandardOutput.ReadToEnd() return $stdout } } function Get-Driver_FW() { [CmdletBinding()] Param ( [Parameter(Mandatory = $false, Position = 0)] [ValidateSet('BIOS', 'BMC', 'Disk', 'Expander', 'HBA', 'intelChipset', 'AMDChipset', 'NIC')] [string]$Device = '', [Parameter(Mandatory = $false, Position = 1)] [ValidateSet($true, $false, 1, 0)] $SKipCheck = $false, [Parameter(Mandatory = $false, Position = 2)] [switch]$SKipCheck2 ) $SKipCheck = [System.Convert]::ToBoolean($SKipCheck) if ((-not $SKipCheck) -or (-not $SKipCheck2)) { CheckVersion } $script:table2 = @() function addRow2($OEMDevice, $OEMName, $DriverFW, $OEMVersion, $OEMPS, $OEMDriverGUID) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'Device' -MemberType Noteproperty -Value $OEMDevice $row2 | Add-Member -Name 'Name' -MemberType Noteproperty -Value $OEMName $row2 | Add-Member -Name 'DriverFW' -MemberType Noteproperty -Value $DriverFW $row2 | Add-Member -Name 'Version' -MemberType Noteproperty -Value $OEMVersion $row2 | Add-Member -Name 'PS' -MemberType Noteproperty -Value $OEMPS $row2 | Add-Member -Name 'Location' -MemberType Noteproperty -Value $hostname #FQDN $row2 | Add-Member -Name 'Location2' -MemberType Noteproperty -Value $hostname2 $row2 | Add-Member -Name 'ServerModel' -MemberType Noteproperty -Value $ServerModel $row2 | Add-Member -Name 'DriverGUID' -MemberType Noteproperty -Value $OEMDriverGUID $script:table2 += $row2 } $Win32_Computersystem = Get-CimInstance Win32_Computersystem $ServerModel = ($Win32_Computersystem | Select-Object Model).Model $SysGUID = '4D36E97D-E325-11CE-BFC1-08002BE10318' $NICGUID = '4D36E972-E325-11CE-BFC1-08002BE10318' $sasGUID = '4D36E97B-E325-11CE-BFC1-08002BE10318' $cpu = Get-CimInstance Win32_Processor | Select-Object Name -First 1 $Win32_PnPEntity = Get-CimInstance -Class Win32_PnPEntity if ($Win32_Computersystem.PartofDomain) { $hostname = $Win32_Computersystem.DNSHostName + '.' + $Win32_Computersystem.Domain } else { $hostname = hostname } $hostname2 = hostname #BIOS if (($Device -eq 'BIOS') -or ($Device -eq '')) { $BIOSVersion = (Get-CimInstance win32_bios).SMBIOSBIOSVersion addRow2 'BIOS' 'BIOS' 'FW' $BIOSVersion '' '' } #BMC if (($Device -eq 'BMC') -or ($Device -eq '')) { $PcsvDevice = Get-PcsvDevice -ErrorAction SilentlyContinue | Select-Object CurrentManagementFirmwareMajorVersion, CurrentManagementFirmwareMinorVersion if ($PcsvDevice) { $BMCVersion = $PcsvDevice.CurrentManagementFirmwareMajorVersion.ToString() + '.' + $PcsvDevice.CurrentManagementFirmwareMinorVersion.ToString() addRow2 'BMC' 'BMC' 'FW' $BMCVersion '' '' } } #Intel Chipset if (($Device -eq 'intelChipset') -or ($Device -eq '')) { $intelChipset = Get-ItemProperty -Path 'HKLM:\Software\Intel\INFInst' -Name 'Version' -ErrorAction SilentlyContinue if ($null -eq $intelChipset) { $intelChipset = ($Win32_PnPEntity | Where-Object Caption -like '*Intel*SMB*' | Get-PnpDeviceProperty | Where-Object KeyName -eq 'DEVPKEY_Device_DriverVersion' | Select-Object Data -First 1).data } else { $intelChipset = $intelChipset.Version } if ($intelChipset) { addRow2 'intelChipset' 'intelChipset' 'Driver' $intelChipset '' $SysGUID } else { if ($cpu.Name.IndexOf('Intel') -ge 0 -and $ServerModel -ne 'Virtual Machine') { addRow2 'intelChipset' 'intelChipset' 'Driver' '' '' $SysGUID } } } #AMD Chipset if (($Device -eq 'AMDChipset') -or ($Device -eq '')) { $AMDChipset = $Win32_PnPEntity | Where-Object Caption -like '*AMD GPIO*' | Get-PnpDeviceProperty | Where-Object KeyName -eq 'DEVPKEY_Device_DriverVersion' | Select-Object Data -First 1 if ($AMDChipset) { addRow2 'AMDChipset' 'AMD GPIO' 'Driver' $AMDChipset.Data '' $SysGUID } else { if ($cpu.Name.IndexOf('AMD') -ge 0 -and $ServerModel -ne 'Virtual Machine') { addRow2 'AMDChipset' 'AMD GPIO' 'Driver' '' '' $SysGUID } } $AMDChipset = $Win32_PnPEntity | Where-Object Caption -like '*AMD PSP*' | Get-PnpDeviceProperty | Where-Object KeyName -eq 'DEVPKEY_Device_DriverVersion' | Select-Object Data -First 1 if ($AMDChipset) { addRow2 'AMDChipset' 'AMD PSP' 'Driver' $AMDChipset.Data '' $SysGUID } else { if ($cpu.Name.IndexOf('AMD') -ge 0) { addRow2 'AMDChipset' 'AMD PSP' 'Driver' '' '' $SysGUID } } $AMDChipset = $Win32_PnPEntity | Where-Object Caption -like '*AMD CCP*' | Get-PnpDeviceProperty | Where-Object KeyName -eq 'DEVPKEY_Device_DriverVersion' | Select-Object Data -First 1 if ($AMDChipset) { addRow2 'AMDChipset' 'AMD CCP' 'Driver' $AMDChipset.Data '' $SysGUID } else { if ($cpu.Name.IndexOf('AMD') -ge 0) { addRow2 'AMDChipset' 'AMD CCP' 'Driver' '' '' $SysGUID } } } if (($Device -eq 'NIC') -or ($Device -eq '')) { $NetAdapters = Get-NetAdapter if ($null -ne $($NetAdapters | Where-Object InterfaceDescription -like '*Mellanox*')) { $MellanoxFW = callProcess "$PSScriptRoot\mlxup.exe" '--query' if ($null -ne $MellanoxFW -and $MellanoxFW -ne '') { $MellanoxFWName = $($MellanoxFW | findstr /I /c:'Device Type').Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2] $MellanoxFWVersion = $($MellanoxFW | findstr /I FW).Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] if ($null -ne $MellanoxFWName) { addRow2 'NIC' $MellanoxFWName 'FW' $MellanoxFWVersion '' $NICGUID } else { addRow2 'NIC' $MellanoxFWName 'FW' '' '' $NICGUID } } $Mellanoxs = $NetAdapters | Where-Object InterfaceDescription -Like *Mellanox* | Sort-Object InterfaceDescription | Select-Object Name, InterfaceDescription, DriverVersionString foreach ($Mellanox in $Mellanoxs) { addRow2 'NIC' $Mellanox.InterfaceDescription 'Driver' $Mellanox.DriverVersionString '' $NICGUID break; } } #Qlogic if ($null -ne $($NetAdapters | Where-Object InterfaceDescription -like '*Qlogic*')) { $QlogicFW = callProcessStdin "$PSScriptRoot\winfwnx2.exe" '' @('q') $QlogicFW = $QlogicFW -split "`r`n" for ($i = 0; $i -lt $QlogicFW.Length; $i++) { if ($QlogicFW[$i].ToUpper().IndexOf('QLOGIC') -ge 0) { if ($QlogicFW[$i].IndexOf(']') -ge 0) { $QlogicFWName = $($QlogicFW[$i].Split(']')[1].trim() -replace '\s+ ', '_').tostring().split('_')[0] $QlogicFWVersion = $($QlogicFW[$i].Split(']')[1].trim() -replace '\s+ ', '_').tostring().split('_')[1] addRow2 'NIC' $QlogicFWName 'FW' $QlogicFWVersion '' $NICGUID } } } $Qlogics = $NetAdapters | Where-Object InterfaceDescription -Like *QLogic* | Sort-Object InterfaceDescription | Select-Object Name, InterfaceDescription, DriverVersionString foreach ($Qlogic in $Qlogics) { addRow2 'NIC' $Qlogic.InterfaceDescription 'Driver' $Qlogic.DriverVersionString '' $NICGUID break; } } #Intel if ($null -ne $($NetAdapters | Where-Object InterfaceDescription -like '*Intel*')) { $IntelNics = $NetAdapters | Where-Object InterfaceDescription -Like *Intel* | Sort-Object InterfaceDescription | Select-Object Name, InterfaceDescription, DriverVersionString foreach ($IntelNic in $IntelNics) { addRow2 'NIC' $IntelNic.InterfaceDescription 'Driver' $IntelNic.DriverVersionString '' $NICGUID break; } } } #Storage Controller if (($Device -eq 'HBA') -or ($Device -eq '')) { $sasinfo1 = $Win32_PnPEntity | Where-Object Manufacturer -like '*AVAGO*' | Where-Object ClassGuid -like "*$sasGUID*" | Get-PnpDeviceProperty | Where-Object KeyName -eq 'DEVPKEY_Device_DriverVersion' | Select-Object Data if ($sasinfo1.count) { for ($i = 0; $i -lt $sasinfo1.count; $i++) { $sasController = '' $sasfwversion1 = '' $sasfwversion2 = '' $sasinfo = callProcess "$PSScriptRoot\sas3flash.exe" " -c $i -list" if (($null -ne $sasinfo) -and (-not ($sasinfo | findstr /I /c:'ERROR'))) { $sasController = $($sasinfo | findstr /I /c:'Controller ').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $sasfwversion1 = $($sasinfo | findstr /I /c:'NVDATA Version (Default)').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $sasfwversion2 = $($sasinfo | findstr /I /c:'Firmware Version').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() addRow2 'Storage Controller' $sasController 'FW' $($sasfwversion1 + '_' + $sasfwversion2) '' $sasGUID addRow2 'Storage Controller' $sasController 'Driver' $($sasinfo1[$i].Data) '' $sasGUID } else { $sasinfo = callProcess "$PSScriptRoot\storcli.exe" " /c$i show" if ($null -ne $sasinfo) { $sasController = $($($sasinfo -split "`r`n" | Select-String 'Product Name').ToString().Trim() -split '=')[1].Trim() $sasfwversion1 = $($($sasinfo -split "`r`n" | Select-String 'FW Version').ToString().Trim() -split '=')[1].Trim() addRow2 'Storage Controller' $sasController 'FW' $sasfwversion1 '' $sasGUID addRow2 'Storage Controller' $sasController 'Driver' $sasinfo1.Data '' $sasGUID } } } } else { if ($sasinfo1) { $sasController = '' $sasfwversion1 = '' $sasfwversion2 = '' $sasinfo = callProcess "$PSScriptRoot\sas3flash.exe" ' -list' if (($null -ne $sasinfo) -and (-not ($sasinfo | findstr /I /c:'ERROR'))) { $sasController = $($sasinfo | findstr /I /c:'Controller ').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $sasfwversion1 = $($sasinfo | findstr /I /c:'NVDATA Version (Default)').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $sasfwversion2 = $($sasinfo | findstr /I /c:'Firmware Version').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() addRow2 'Storage Controller' $sasController 'FW' $($sasfwversion1 + '_' + $sasfwversion2) '' $sasGUID addRow2 'Storage Controller' $sasController 'Driver' $sasinfo1.Data '' $sasGUID } else { $sasinfo = callProcess "$PSScriptRoot\storcli.exe" ' /c0 show' if ($null -ne $sasinfo) { $sasController = $($($sasinfo -split "`r`n" | Select-String 'Product Name').ToString().Trim() -split '=')[1].Trim() $sasfwversion1 = $($($sasinfo -split "`r`n" | Select-String 'FW Version').ToString().Trim() -split '=')[1].Trim() addRow2 'Storage Controller' $sasController 'FW' $sasfwversion1 '' $sasGUID addRow2 'Storage Controller' $sasController 'Driver' $sasinfo1.Data '' $sasGUID } } } } } #Expander if (($Device -eq 'Expander') -or ($Device -eq '')) { $commandlist = @" 1 show quit "@ $currentPath = Get-Location Set-Location $PSScriptRoot try { $output = $commandlist | .\g4Xflash_x64.exe } catch { } if ($output | findstr /I /c:'Firmware Version') { $expanderinfo = ($output | findstr /I /c:'Firmware Version').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } $expander1Name = 'Expander1' if ($output | findstr /I /c:'Product Id') { $expander1Name = $($($output -split "`r`n" | Select-String 'Product Id').ToString().Trim() -split ':')[1].Trim() } if ($output | findstr /I /C:'2)') { $commandlist = @" 2 show quit "@ $output2 = $commandlist | .\g4Xflash_x64.exe if ($output2 | findstr /I /c:'Firmware Version') { $expanderinfo2 = ($output2 | findstr /I /c:'Firmware Version').Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } $expanderName2 = 'Expander2' if ($output2 | findstr /I /c:'Product Id') { $expander2Name = $($($output2 -split "`r`n" | Select-String 'Product Id').ToString().Trim() -split ':')[1].Trim() } } Set-Location $currentPath if ($expanderinfo) { if ($expanderinfo2) { addRow2 'Expander' $expander1Name 'FW' $expanderinfo '' '' addRow2 'Expander' $expander2Name 'FW' $expanderinfo2 '' '' } else { addRow2 'Expander' $expander1Name 'FW' $expanderinfo '' '' } } } #Disk if (($Device -eq 'Disk') -or ($Device -eq '')) { $phds = $null $StorageNodes = Get-StorageNode | Where-Object Name -like "$(hostname)*" $disks = Get-Disk | Where-Object { ($_.BootFromDisk -eq $true) -or ($_.IsBoot -eq $true) } | Select-Object UniqueId foreach ($StorageNode in $StorageNodes) { $phds += Get-PhysicalDisk -StorageNode $StorageNode -PhysicallyConnected | Where-Object { $_.UniqueId -notin $disks.UniqueId } | Where-Object FriendlyName -NotLike '*Virtual*' } $phds = $phds | Where-Object { $_.UniqueId -notin $disks.UniqueId } | Select-Object * $SSDs = $phds | Where-Object { $_.Mediatype -eq 'SSD' } foreach ($SSD in $SSDs) { addRow2 'Disk' $SSD.FriendlyName 'FW' $SSD.FirmwareVersion $SSD.SerialNumber '' } $HDDs = $phds | Where-Object { $_.Mediatype -eq 'HDD' } foreach ($HDD in $HDDs) { addRow2 'Disk' $HDD.FriendlyName 'FW' $HDD.FirmwareVersion $HDD.SerialNumber '' } } $script:table2 } function Get-ClusterDriver_FW() { [CmdletBinding()] [OutputType([Bool])] Param ( [Parameter(Mandatory = $false, Position = 0)] [ValidateSet($true, $false, 1, 0)] $SkipInstallPackage = $false, [Parameter(Mandatory = $true, Position = 1)] [string]$ClusterName = '', [Parameter(Mandatory = $false, Position = 2)] [ValidateSet($true, $false, 1, 0)] $SKipCheck = $false, [Parameter(Mandatory = $false, Position = 2)] [switch]$SKipCheck2 ) $SKipCheck = [System.Convert]::ToBoolean($SKipCheck) if ((-not $SKipCheck) -or (-not $SKipCheck2)) { CheckVersion } $SkipInstallPackage = [System.Convert]::ToBoolean($SkipInstallPackage) $ClusterNodes = Get-ClusterNode -Cluster $ClusterName if (-not $SkipInstallPackage) { foreach ($ClusterNode in $ClusterNodes) { Invoke-Command -ComputerName $ClusterNode -ScriptBlock { Install-PackageProvider Nuget -Force -MinimumVersion 2.8.5.201 > $null Install-Module -Name PowershellGet -SkipPublisherCheck -AllowClobber -Force -Confirm:$false > $null Install-Module QCT-Update-Management -SkipPublisherCheck -AllowClobber -Force > $null Import-Module QCT-Update-Management -Force > $null } | Out-Null } } <# foreach ($CheckItem in $('BIOS', 'BMC', 'intelChipset', 'AMDChipset', 'NIC', 'HBA', 'Expander', 'Disk')) { Write-Host $CheckItem foreach ($ClusterNode in $ClusterNodes) { $Driver_FWs = Invoke-Command -ComputerName $ClusterNode -ScriptBlock { param($CheckItem) Get-Driver_FW -Device $CheckItem } -ArgumentList $CheckItem foreach ($Driver_FW in $Driver_FWs) { Write-Host "$ClusterNode $CheckItem-$($Driver_FW.Name)_$($Driver_FW.DriverFW):" $Driver_FW.Version } } Write-Host '----------------------------------------------------------------' } #> foreach ($ClusterNode in $ClusterNodes) { New-Variable -Name "clusternode_$($ClusterNode.Name)" -Value $(Invoke-Command -ComputerName $ClusterNode -ScriptBlock { Get-Driver_FW }) } foreach ($CheckItem in $('BIOS', 'BMC', 'intelChipset', 'AMDChipset', 'NIC', 'HBA', 'Expander', 'Disk')) { foreach ($ClusterNode in $ClusterNodes) { $(Get-Variable -Name "clusternode_$($ClusterNode.Name)" -ValueOnly) | where Device -eq $CheckItem | ft } } } function Enable-DiskLocate() { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$SerialNumber = '' ) # CheckVersion $allController = callProcess "$PSScriptRoot\storcli.exe" 'show ctrlcount' $ctrlCount = $($($allController -split "`r`n" | Select-String -Pattern 'Controller') -split '=')[1].ToString().Trim() for ($i = 0; $i -lt $ctrlCount; $i++) { $alldiskStr = callProcess "$PSScriptRoot\storcli.exe" "/c$i show all" $diskStr = $alldiskStr -split "`r`n" | Select-String -Pattern $SerialNumber -Context 6 if ($diskStr -eq $null) { $alldiskStr = callProcess "$PSScriptRoot\storcli.exe" "/c$i/eall/sall show all" $diskStr = $alldiskStr -split "`r`n" | Select-String -Pattern $SerialNumber -Context 6 } if ($diskStr) { $disklocation = $($($diskStr.ToString() -split "`r`n" | Select-String 'Drive /').ToString().Trim() -split ' ')[1] if ($disklocation) { return callProcess "$PSScriptRoot\storcli.exe" "$disklocation start locate" } } } return 'Failed.' } function Disable-DiskLocate() { [CmdletBinding()] Param ( [Parameter(Mandatory = $false, Position = 0)] [string]$SerialNumber = '' ) # CheckVersion $allController = callProcess "$PSScriptRoot\storcli.exe" 'show ctrlcount' $ctrlCount = $($($allController -split "`r`n" | Select-String -Pattern 'Controller') -split '=')[1].ToString().Trim() for ($i = 0; $i -lt $ctrlCount; $i++) { $alldiskStr = callProcess "$PSScriptRoot\storcli.exe" "/c$i show all" $diskStr = $alldiskStr -split "`r`n" | Select-String -Pattern $SerialNumber -Context 6 if ($diskStr -eq $null) { $alldiskStr = callProcess "$PSScriptRoot\storcli.exe" "/c$i/eall/sall show all" $diskStr = $alldiskStr -split "`r`n" | Select-String -Pattern $SerialNumber -Context 6 } if ($diskStr) { $disklocation = $($($diskStr.ToString() -split "`r`n" | Select-String 'Drive /').ToString().Trim() -split ' ')[1] if ($disklocation) { return callProcess "$PSScriptRoot\storcli.exe" "$disklocation stop locate" } } } return 'Failed.' } function checkModel($Model) { $Brand = 'unknow' $Series = 'unknow' if ($Model -like '*INTEL*') { $Brand = 'INTEL' } elseif ($Model -like '*SAMSUNG*') { $Brand = 'SAMSUNG' } elseif ($Model -like '*TOSHIBA*') { $Brand = 'TOSHIBA' } else { $Brand = 'unknow' } if ($Brand -eq 'INTEL') { if ($Model -like '*SSDSC2BA*4*') { $Series = 'INTEL_S3710' } elseif ($Model -like '*SSDSC2BB*6*') { $Series = 'INTEL_S3510' } elseif (($Model -like '*SSDSC2BB*7*') -or ($Model -like '*SSDSCKJB*7*')) { $Series = 'INTEL_S3520' } elseif ($Model -like '*SSDSC2KB*7*') { $Series = 'INTEL_S4500' } elseif ($Model -like '*SSDSC2KB*8*') { $Series = 'INTEL_D3-S4510' } elseif ($Model -like '*SSDSC2KG*7*') { $Series = 'INTEL_S4600' } elseif ($Model -like '*SSDSC2KG*8*') { $Series = 'INTEL_D3-S4610' } elseif ($Model -like '*SSDPEKKA512G7*') { $Series = 'INTEL_P3100' } elseif (($Model -like '*SSDPE2MD*4*') -or ($Model -like '*SSDPEDMD*4*')) { $Series = 'INTEL_P3700' } elseif (($Model -like '*SSDPEDKX*7*') -or ($Model -like '*SSDPE2KX*7*')) { $Series = 'INTEL_P4500' } elseif ($Model -like '*SSDPE7KX*7*') { $Series = 'INTEL_P4501' } elseif ($Model -like '*SSDPE2KX*8*') { $Series = 'INTEL_P4510' } elseif ($Model -like '*SSDPELKX*8*') { $Series = 'INTEL_P4511' } elseif (($Model -like '*SSDPE2KE*7*') -or ($Model -like '*SSDPEDKE*7*')) { $Series = 'INTEL_P4600' } elseif ($Model -like '*SSDPE7KE*7*') { $Series = 'INTEL_P4601' } elseif ($Model -like '*SSDPE2KE*8*') { $Series = 'INTEL_P4610' } elseif (($Model -like '*SSDPED1K*A*') -or ($Model -like '*SSDPE21K*A*')) { $Series = 'INTEL_P4800X' } else { $Series = 'unknow' } } elseif ($Brand -eq 'SAMSUNG') { if ($Model -like '*MZ7KM*HA*') { $Series = 'SAMSUNG_SM863' } elseif ($Model -like '*MZ7KM*HM*') { $Series = 'SAMSUNG_SM863a' } elseif ($Model -like '*MZ7KH*HA*') { $Series = 'SAMSUNG_SM883' } elseif ($Model -like '*MZQLB*H*') { $Series = 'SAMSUNG_PM983' } elseif ($Model -like '*MZQLW*H*') { $Series = 'SAMSUNG_PM963' } elseif ($Model -like '*MZ7LH*H*') { $Series = 'SAMSUNG_PM883' } elseif ($Model -like '*MZ7LM*H*') { $Series = 'SAMSUNG_PM863a' } elseif (($Model -like '*MZPLL*HMLA*') -or ($Model -like '*MZPLL*HAJQ*') -or ($Model -like '*MZWLL*HMLA*') -or ($Model -like '*MZWLL*HAJQ*')) { $Series = 'SAMSUNG_PM1725b' } elseif (($Model -like '*MZPLL*HMLS*') -or ($Model -like '*MZPLL*HEHP*') -or ($Model -like '*MZWLL*HEHP*') -or ($Model -like '*MZWLL*HMJP*') -or ($Model -like '*MZWLL*HMLS*')) { $Series = 'SAMSUNG_PM1725a' } elseif ($Model -like '*MZILT*H*') { $Series = 'SAMSUNG_PM1643' } elseif ($Model -like '*MZILS*H*') { $Series = 'SAMSUNG_PM1633a' } else { $Series = 'unknow' } } elseif ($Brand -eq 'TOSHIBA') { if ($Model -like '*THNSNJ800PCSZ*') { $Series = 'Toshiba_HK3E2' } else { $Series = 'unknow' } } else { if ($Model -like '*ST8000*') { $Series = 'SEAGATE_Archive_HDD_v2' } else { $Series = 'unknow' } } return $Series } function checkLifetime($smartInfo) { $Wear_Leveling_Count = 0 $Wear_Leveling_Count_Threshold = 0 if ($null -ne $($smartInfo -split "`r`n" | Select-String 'Wear_Leveling_Count')) { # Samsung SSD 99 to 0 $Wear_Leveling_Count = [int] $($smartInfo -split "`r`n" | Select-String 'Wear_Leveling_Count').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[3].Trim() $Wear_Leveling_Count_Threshold = [int] $($smartInfo -split "`r`n" | Select-String 'Wear_Leveling_Count').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[5].Trim() } elseif ($null -ne $($smartInfo -split "`r`n" | Select-String 'Media_Wearout_Indicator')) { # INTEL SSD 100 to 1 $Wear_Leveling_Count = $($smartInfo -split "`r`n" | Select-String 'Media_Wearout_Indicator').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[3].Trim() $Wear_Leveling_Count_Threshold = $($smartInfo -split "`r`n" | Select-String 'Media_Wearout_Indicator').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[5].Trim() } elseif ($null -ne $($smartInfo -split "`r`n" | Select-String 'Available Spare:')) { #INTEL NVME $Wear_Leveling_Count = $($smartInfo -split "`r`n" | Select-String 'Available Spare:').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace('%', '') $Wear_Leveling_Count_Threshold = $($smartInfo -split "`r`n" | Select-String 'Available Spare Threshold:').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace('%', '') } else { $Wear_Leveling_Count = 'none' $Wear_Leveling_Count_Threshold = 'none' } $Wear_Leveling_Count $Wear_Leveling_Count_Threshold } function checkPoweronHour($smartInfo) { if ($smartInfo -split "`r`n" | Select-String 'Power_On_Hours') { $Power_On_Hours = $($smartInfo -split "`r`n" | Select-String 'Power_On_Hours').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Trim() } elseif ($smartInfo -split "`r`n" | Select-String 'Power On Hours:') { $Power_On_Hours = $($smartInfo -split "`r`n" | Select-String 'Power On Hours:').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } else { $Power_On_Hours = 'none' } if ($smartInfo -split "`r`n" | Select-String 'Power_Cycle_Count') { $Power_Cycle_Count = $($smartInfo -split "`r`n" | Select-String 'Power_Cycle_Count').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Trim() } elseif ($smartInfo -split "`r`n" | Select-String 'Power Cycles:') { $Power_Cycle_Count = $($smartInfo -split "`r`n" | Select-String 'Power Cycles:').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } else { $Power_Cycle_Count = 'none' } $Power_On_Hours $Power_Cycle_Count } function checkUserCapacity($smartInfo) { if ($smartInfo -split "`r`n" | Select-String 'User Capacity') { $userCapacity = $($smartInfo -split "`r`n" | Select-String 'User Capacity').ToString().Split('[', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace(']', '').Replace(' ', '_') } elseif ($smartInfo -split "`r`n" | Select-String 'Namespace 1 Size/Capacity') { $userCapacity = $($smartInfo -split "`r`n" | Select-String 'Namespace 1 Size/Capacity').ToString().Split('[', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace(']', '').Replace(' ', '_') } else { $userCapacity = 'none' } $userCapacity } function writeHostColored() { [CmdletBinding(ConfirmImpact = 'None', SupportsShouldProcess = $false, SupportsTransactions = $false)] param( [Parameter(Position = 0, ValueFromPipeline = $true)] [string[]] $Text , [switch] $NoNewline , [ConsoleColor] $BackgroundColor = $host.UI.RawUI.BackgroundColor , [ConsoleColor] $ForegroundColor = $host.UI.RawUI.ForegroundColor ) begin { if ($null -ne $Text) { $Text = "$Text" } } process { if ($Text) { $curFgColor = $ForegroundColor $curBgColor = $BackgroundColor $tokens = $Text.split('#') $prevWasColorSpec = $false foreach ($token in $tokens) { if (-not $prevWasColorSpec -and $token -match '^([a-z]+)(:([a-z]+))?$') { # a potential color spec. try { $curFgColor = [ConsoleColor] $matches[1] $prevWasColorSpec = $true } catch {} if ($matches[3]) { try { $curBgColor = [ConsoleColor] $matches[3] $prevWasColorSpec = $true } catch {} } if ($prevWasColorSpec) { continue } } $prevWasColorSpec = $false if ($token) { $argsHash = @{} if ([int] $curFgColor -ne -1) { $argsHash += @{ 'ForegroundColor' = $curFgColor } } if ([int] $curBgColor -ne -1) { $argsHash += @{ 'BackgroundColor' = $curBgColor } } Write-Host -NoNewline @argsHash $token } $curFgColor = $ForegroundColor $curBgColor = $BackgroundColor } } if (-not $NoNewLine) { write-host } } } function Get-SmartInfo() { [CmdletBinding()] [OutputType([Bool])] Param ( [Parameter(Mandatory = $false, Position = 0)] [ValidateSet($true, $false, 1, 0)] $Color = $false ) # CheckVersion $Color = [System.Convert]::ToBoolean($Color) $smartPath = "$PSScriptRoot\smartctl.exe" $disks = callProcess $smartPath '--scan' 1 $diskArray = @() foreach ($disk in $($disks -split "`r`n")) { if ($($disk | select-string '/dev/')) { $disk = $($disk -split ' ' )[0] $smartInfo = callProcess $smartPath " -A -i -H $disk -s on" 0.2 if (-not $($smartInfo -split "`r`n" | Select-String 'Open failed')) { if (($smartInfo -split "`r`n" | Select-String 'SMART overall-health self-assessment test result')) { $smartResult = $($smartInfo -split "`r`n" | Select-String 'SMART overall-health self-assessment test result').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } else { if (($smartInfo -split "`r`n" | Select-String 'SMART Health Status')) { $smartResult = $($smartInfo -split "`r`n" | Select-String 'SMART Health Status').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } else { continue } } if ($smartInfo -split "`r`n" | Select-String 'Sector Size') { $sectorSize = [int] $($smartInfo -split "`r`n" | Select-String 'Sector Size').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2] } else { $sectorSize = [int] 512 } $Model = "" if ($($smartInfo -split "`r`n" | Select-String 'Device Model') -eq $null) { if ($($smartInfo -split "`r`n" | Select-String 'Model Number') -ne $null) { $Model = $($smartInfo -split "`r`n" | Select-String 'Model Number').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().ToUpper().Replace(' ', '_') } } else { $Model = $($smartInfo -split "`r`n" | Select-String 'Device Model').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().ToUpper().Replace(' ', '_') } $serialNumber = $($smartInfo -split "`r`n" | Select-String 'Serial Number').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $return = checkPoweronHour $smartInfo $Power_On_Hours = $return[0] $Power_Cycle_Count = $return[1] $userCapacity = checkUserCapacity $smartInfo $Series = checkModel $Model $Reallocated_Sector_Ct = 0 $Current_Pending_Sector = 0 $Offline_Uncorrectable = 0 if ($null -ne $($smartInfo -split "`r`n" | Select-String 'Reallocated_Sector_Ct')) { $Reallocated_Sector_Ct = [int] $($smartInfo -split "`r`n" | Select-String 'Reallocated_Sector_Ct').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[', '') } if ($null -ne $($smartInfo -split "`r`n" | Select-String 'Current_Pending_Sector')) { $Current_Pending_Sector = [int] $($smartInfo -split "`r`n" | Select-String 'Current_Pending_Sector').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[', '') } if ($null -ne $($smartInfo -split "`r`n" | Select-String 'Offline_Uncorrectable')) { $Offline_Uncorrectable = [int] $($smartInfo -split "`r`n" | Select-String 'Offline_Uncorrectable').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[', '') } if ($null -ne $($smartInfo -split "`r`n" | Select-String 'Command_Timeout')) { $Command_Timeout = [long] $($smartInfo -split "`r`n" | Select-String 'Command_Timeout').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[', '') } if ($null -ne $($smartInfo -split "`r`n" | Select-String 'Spin_Retry_Count')) { $Spin_Retry_Count = [int] $($smartInfo -split "`r`n" | Select-String 'Spin_Retry_Count').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[', '') } if ($null -ne $($smartInfo -split "`r`n" | Select-String 'End-to-End_Error')) { $End_to_End_Error = [int] $($smartInfo -split "`r`n" | Select-String 'End-to-End_Error').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[', '') } $smartCaution = '' if ($Reallocated_Sector_Ct + $Current_Pending_Sector + $Offline_Uncorrectable + $Spin_Retry_Count + $End_to_End_Error -gt 0) { $smartResult = 'Caution' if ($Reallocated_Sector_Ct -gt 0) { $smartCaution += 'Reallocated Sector Ct:' + $Reallocated_Sector_Ct + '`n' } if ($Current_Pending_Sector -gt 0) { $smartCaution += 'Current Pending Sector:' + $Current_Pending_Sector + '`n' } if ($Offline_Uncorrectable -gt 0) { $smartCaution += 'Offline Uncorrectable:' + $Offline_Uncorrectable + '`n' } if ($Spin_Retry_Count -gt 0) { $smartCaution += 'Spin Retry Count:' + $Spin_Retry_Count + '`n' } if ($End_to_End_Error -gt 0) { $smartCaution += 'End-to-End error:' + $End_to_End_Error + '`n' } if ($Command_Timeout -gt 0) { $smartCaution += 'Command Timeout:' + $Command_Timeout + '`n' } } if (($smartInfo -split "`r`n") -like '241 *') { $Total_LBAs_Written = [long] $(($smartInfo -split "`r`n") -like '241 *').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[9] } else { $Total_LBAs_Written = '' } $Total_Host_Write = 0 if (($Model.IndexOf('INTEL') -ge 0) -or ($Model.IndexOf('TOSHIBA') -ge 0)) { $Total_Host_Write = '{0:n2}' -f $($Total_LBAs_Written * 32 / 1024) #GB } elseif ($Model.IndexOf('SD134') -ge 0) { $Total_Host_Write = '{0:N2}' -f $($Total_LBAs_Written / 1024) #GB } else { $Total_Host_Write = '{0:N2}' -f $($Total_LBAs_Written * $($sectorSize / 1024) / 1024 / 1024) #GB } if ($Total_Host_Write -eq '0.00') { $Total_Host_Write = 'N/A' } else { $Total_Host_Write = "$Total_Host_Write GB" } $return = checkLifetime $smartInfo $Wear_Leveling_Count = $return[0] $Wear_Leveling_Count_Threshold = $return[1] if ($Color -and ($smartResult -ne 'PASSED')) { writeHostColored "$disk $Series $userCapacity $Model #yellow#$smartResult# $serialNumber $Total_Host_Write $Wear_Leveling_Count $Wear_Leveling_Count_Threshold $Power_On_Hours $Power_Cycle_Count" } $diskObj = New-Object -TypeName PSObject $diskObj | Add-Member -Name 'Name' -MemberType Noteproperty -Value $disk $diskObj | Add-Member -Name 'Series' -MemberType Noteproperty -Value $Series $diskObj | Add-Member -Name 'userCapacity' -MemberType Noteproperty -Value $disk $diskObj | Add-Member -Name 'Model' -MemberType Noteproperty -Value $Model $diskObj | Add-Member -Name 'smartResult' -MemberType Noteproperty -Value $smartResult $diskObj | Add-Member -Name 'serialNumber' -MemberType Noteproperty -Value $serialNumber $diskObj | Add-Member -Name 'Total_Host_Write' -MemberType Noteproperty -Value $Total_Host_Write $diskObj | Add-Member -Name 'Wear_Leveling_Count' -MemberType Noteproperty -Value $Wear_Leveling_Count $diskObj | Add-Member -Name 'Wear_Leveling_Count_Threshold' -MemberType Noteproperty -Value $Wear_Leveling_Count_Threshold $diskObj | Add-Member -Name 'Power_On_Hours' -MemberType Noteproperty -Value $Power_On_Hours $diskObj | Add-Member -Name 'Power_Cycle_Count' -MemberType Noteproperty -Value $Power_Cycle_Count $diskObj | Add-Member -Name 'SmartCaution' -MemberType Noteproperty -Value $smartCaution $diskArray += $diskObj } } } $disks = callProcess $smartPath '-d nvme --scan' 1 foreach ($disk in $($disks -split "`r`n")) { if ($($disk | Select-String '/dev/')) { $disk = $($disk -split ' ' )[0] $smartInfo = callProcess $smartPath " -A -i -H $disk -s on" 0.3 if (($smartInfo -split "`r`n" | Select-String 'SMART overall-health self-assessment test result')) { $smartResult = $($smartInfo -split "`r`n" | Select-String 'SMART overall-health self-assessment test result').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() } else { continue } $sectorSize = [int] 512 # $($smartInfo -split "`r`n" | Select-String 'Sector Size').ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[2] $Model = $($smartInfo -split "`r`n" | Select-String 'Model').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().ToUpper().Replace(' ', '_') $serialNumber = $($smartInfo -split "`r`n" | Select-String 'Serial Number').ToString().Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $return = checkPoweronHour $smartInfo $Power_On_Hours = $return[0] $Power_Cycle_Count = $return[1] $userCapacity = checkUserCapacity $smartInfo $Series = checkModel $Model if ($($smartInfo -split "`r`n" | Select-String 'Data Units Written').ToString().IndexOf('PB') -ge 0) { $Data_Units_Written = '{0:n2}' -f $(1024 * 1024 * [int] $($smartInfo -split "`r`n" | Select-String 'Data Units Written').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[4].Replace('[', '')) } elseif ($($smartInfo -split "`r`n" | Select-String 'Data Units Written').ToString().IndexOf('TB') -ge 0) { $Data_Units_Written = '{0:n2}' -f $(1024 * [int] $($smartInfo -split "`r`n" | Select-String 'Data Units Written').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[4].Replace('[', '')) } elseif ($($smartInfo -split "`r`n" | Select-String 'Data Units Written').ToString().IndexOf('GB') -ge 0) { $Data_Units_Written = '{0:n2}' -f $([int] $($smartInfo -split "`r`n" | Select-String 'Data Units Written').ToString().Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[4].Replace('[', '')) } $smartCaution = '' $return = checkLifetime $smartInfo $Wear_Leveling_Count = $return[0] $Wear_Leveling_Count_Threshold = $return[1] if ($Color -and ($smartResult -ne 'PASSED')) { writeHostColored "$disk $Series $userCapacity $Model #yellow#$smartResult# $serialNumber $Data_Units_Written $Wear_Leveling_Count $Wear_Leveling_Count_Threshold $Power_On_Hours $Power_Cycle_Count" } $diskObj = New-Object -TypeName PSObject $diskObj | Add-Member -Name 'Name' -MemberType Noteproperty -Value $disk $diskObj | Add-Member -Name 'Series' -MemberType Noteproperty -Value $Series $diskObj | Add-Member -Name 'userCapacity' -MemberType Noteproperty -Value $userCapacity $diskObj | Add-Member -Name 'Model' -MemberType Noteproperty -Value $Model $diskObj | Add-Member -Name 'smartResult' -MemberType Noteproperty -Value $smartResult $diskObj | Add-Member -Name 'serialNumber' -MemberType Noteproperty -Value $serialNumber $diskObj | Add-Member -Name 'Total_Host_Write' -MemberType Noteproperty -Value "$Data_Units_Written GB" $diskObj | Add-Member -Name 'Wear_Leveling_Count' -MemberType Noteproperty -Value $Wear_Leveling_Count $diskObj | Add-Member -Name 'Wear_Leveling_Count_Threshold' -MemberType Noteproperty -Value $Wear_Leveling_Count_Threshold $diskObj | Add-Member -Name 'Power_On_Hours' -MemberType Noteproperty -Value $Power_On_Hours $diskObj | Add-Member -Name 'Power_Cycle_Count' -MemberType Noteproperty -Value $Power_Cycle_Count $diskObj | Add-Member -Name 'SmartCaution' -MemberType Noteproperty -Value $smartCaution $diskArray += $diskObj } } $diskArray } function Update-OEMFirmware-LSI { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$FwPath = '', [Parameter(Mandatory = $true, Position = 1)] [string]$Mptsas = '', [Parameter(Mandatory = $true, Position = 2)] [string]$Mptx64 = '' ) # CheckVersion $script:tableFW = @() function addRowFW($FWType, $FWVersion, $FWVersionNew) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'Type' -MemberType Noteproperty -Value $FWType $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty -Value $FWVersion $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty -Value $FWVersionNew $script:tableFW += $row2 } $OEMStorageControllPath = $PSScriptRoot $sasinfo = callProcess "$OEMStorageControllPath\sas3flash.exe" '-list' + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -list" $sasinfo1 = Get-CimInstance Win32_PnPSignedDriver | Select-Object devicename, driverversion | Where-Object { $_.devicename -like '*SAS3*' } $LsiFWVersion = $($sasinfo -split "`r`n" | Select-String 'NVDATA Version' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $LsiBIOSVersion = $($sasinfo -split "`r`n" | Select-String 'BIOS' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $LsiUEFIVersion = $($sasinfo -split "`r`n" | Select-String 'UEFI' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() if ($sasinfo1.count) { for ($i = 0; $i -lt $sasinfo1.count; $i++) { #LSI Firmware $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -c $i -f $FwPath" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -f $FwPath" -Verbose Sleep 5 #LSI Legacy Mode BIOS $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -c $i -b $Mptsas" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -b $Mptsas" -Verbose Sleep 5 #LSI UEFI Mode BIOS $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -c $i -b $Mptx64" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -b $Mptx64" -Verbose } } else { #LSI Firmware $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -f $FwPath" + [System.Environment]::NewLine Sleep 5 #LSI Legacy Mode BIOS $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -b $Mptsas" + [System.Environment]::NewLine Sleep 5 #LSI UEFI Mode BIOS $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -b $Mptx64" + [System.Environment]::NewLine } $sasinfoNew = callProcess "$OEMStorageControllPath\sas3flash.exe" '-list' #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -list" $LsiFWVersionNew = $($sasinfoNew -split "`r`n" | Select-String 'NVDATA Version' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $LsiBIOSVersionNew = $($sasinfoNew -split "`r`n" | Select-String 'BIOS' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() $LsiUEFIVersionNew = $($sasinfoNew -split "`r`n" | Select-String 'UEFI' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() addRowFW 'BIOS' $LsiBIOSVersion $LsiBIOSVersionNew addRowFW 'UEFI' $LsiUEFIVersion $LsiUEFIVersionNew addRowFW 'FW' $LsiFWVersion $LsiFWVersionNew $script:tableFW $script:rebootneed = 1 } function Update-OEMFirmware-Mellanox { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$FwPath = '' ) # CheckVersion $script:tableFW = @() function addRowFW($FWType, $FWVersion, $FWVersionNew) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'Type' -MemberType Noteproperty -Value $FWType $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty -Value $FWVersion $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty -Value $FWVersionNew $script:tableFW += $row2 } #Mellanox Firmware Update $OEMMellanoxPath = $PSScriptRoot $nicinfo = callProcess "$OEMMellanoxPath\mlxup.exe" ' -query --sfx-extract-dir C:\Windows\Temp' # $MellanoxFWName = $($nicinfo -split "`r`n" | Select-String 'Device Type').line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2] $MellanoxFWVersion = $($nicinfo -split "`r`n" | Select-String 'FW' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxCLPVersion = $($nicinfo -split "`r`n" | Select-String 'CLP' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxPXEVersion = $($nicinfo -split "`r`n" | Select-String 'PXE' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxUEFIVersion = $($nicinfo -split "`r`n" | Select-String 'UEFI' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] # $MellanoxFWPSID = $($nicinfo -split "`r`n" | Select-String 'PSID' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] # $MellanoxPDName = $($nicinfo -split "`r`n" | Select-String 'PCI Device Name' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() if ($($nicinfo | Select-String 'ConnectX3Pro').length) { $result = callProcess "$OEMMellanoxPath\mlxup.exe" " -i $FwPath --sfx-extract-dir C:\Windows\Temp -u -f -y" if ($($result | Select-String '100%Done').length) { $resultNew = callProcess "$OEMMellanoxPath\mlxup.exe" $MellanoxFWVersionNew = $($resultNew -split "`r`n" | Select-String 'FW' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxCLPVersionNew = $($resultNew -split "`r`n" | Select-String 'CLP' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxPXEVersionNew = $($resultNew -split "`r`n" | Select-String 'PXE' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxUEFIVersionNew = $($resultNew -split "`r`n" | Select-String 'UEFI' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] addRowFW 'CLP' $MellanoxCLPVersion $MellanoxCLPVersionNew addRowFW 'PXE' $MellanoxPXEVersion $MellanoxPXEVersionNew addRowFW 'UEFI' $MellanoxUEFIVersion $MellanoxUEFIVersionNew addRowFW 'FW' $MellanoxFWVersion $MellanoxFWVersionNew $script:tableFW } $script:rebootneed = 1 } elseif ($($nicinfo | Select-String 'ConnectX4').length) { $result = callProcess "$OEMMellanoxPath\mlxup.exe" " -i $FwPath --sfx-extract-dir C:\Windows\Temp -u -f -y" if ($($result | Select-String '100%Done').length) { Reset-OEMFirmware-Mellanox -Verbose $resultNew = callProcess "$OEMMellanoxPath\mlxup.exe" $MellanoxFWVersionNew = $($resultNew -split "`r`n" | Select-String 'FW' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxCLPVersionNew = $($resultNew -split "`r`n" | Select-String 'CLP' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxPXEVersionNew = $($resultNew -split "`r`n" | Select-String 'PXE' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxUEFIVersionNew = $($resultNew -split "`r`n" | Select-String 'UEFI' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] addRowFW 'CLP' $MellanoxCLPVersion $MellanoxCLPVersionNew addRowFW 'PXE' $MellanoxPXEVersion $MellanoxPXEVersionNew addRowFW 'UEFI' $MellanoxUEFIVersion $MellanoxUEFIVersionNew addRowFW 'FW' $MellanoxFWVersion $MellanoxFWVersionNew $script:tableFW } $script:rebootneed = 1 } } function Reset-OEMFirmware-Mellanox { # CheckVersion function addRowFW($FWType, $FWVersion, $FWVersionNew) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'Type' -MemberType Noteproperty -Value $FWType $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty -Value $FWVersion $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty -Value $FWVersionNew $script:tableFW += $row2 } #Mellanox Firmware Update $OEMMellanoxPath = $PSScriptRoot $nicinfo = callProcess "$OEMMellanoxPath\mlxup.exe" ' -query --sfx-extract-dir C:\Windows\Temp' # $MellanoxFWName = $($nicinfo -split "`r`n" | Select-String 'Device Type').line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[2] $MellanoxFWVersion = $($nicinfo -split "`r`n" | Select-String 'FW' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] # $MellanoxFWPSID = $($nicinfo -split "`r`n" | Select-String 'PSID' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] # $MellanoxPDName = $($nicinfo -split "`r`n" | Select-String 'PCI Device Name' -CaseSensitive).line.Split(':', [System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim() # $nicinfo if ($($nicinfo | Select-String 'ConnectX4').length) { $result = callProcessWorkingDirectory "$OEMMellanoxPath\mlxfwreset.exe" ' -d mt4117_pciconf0 -l 3 reset -y' $resultNew = callProcess "$OEMMellanoxPath\mlxup.exe" $MellanoxFWVersionNew = $($resultNew -split "`r`n" | Select-String 'FW' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxCLPVersionNew = $($resultNew -split "`r`n" | Select-String 'CLP' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxPXEVersionNew = $($resultNew -split "`r`n" | Select-String 'PXE' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] $MellanoxUEFIVersionNew = $($resultNew -split "`r`n" | Select-String 'UEFI' -CaseSensitive).line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[1] addRowFW 'CLP' $MellanoxCLPVersion $MellanoxCLPVersionNew addRowFW 'PXE' $MellanoxPXEVersion $MellanoxPXEVersionNew addRowFW 'UEFI' $MellanoxUEFIVersion $MellanoxUEFIVersionNew addRowFW 'FW' $MellanoxFWVersion $MellanoxFWVersionNew $script:tableFW $script:rebootneed = 1 } } function Query-OEMFirmware-Mellanox { # CheckVersion #Mellanox Firmware Update $OEMMellanoxPath = $PSScriptRoot $nicinfo = callProcess "$OEMMellanoxPath\mlxup.exe" ' -query --sfx-extract-dir C:\Windows\Temp' $nicinfo } function Update-OEMFirmware-QLogic { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$FwPath = '' ) # CheckVersion $script:tableFW = @() function addRowFW($FWType, $FWVersion, $FWVersionNew) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'Type' -MemberType Noteproperty -Value $FWType $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty -Value $FWVersion $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty -Value $FWVersionNew $script:tableFW += $row2 } #QLogic Firmware Update $OEMQLogicPath = $PSScriptRoot $QlogicFW = callProcessStdin "$PSScriptRoot\winfwnx2.exe" '' @('q') $QlogicFW = $QlogicFW -split "`r`n" for ($i = 0; $i -lt $QlogicFW.Length; $i++) { if ($QlogicFW[$i].ToUpper().IndexOf('QLOGIC') -ge 0) { if ($QlogicFW[$i].IndexOf(']') -ge 0) { # $QlogicFWName = $($QlogicFW[$i].Split(']')[1].trim() -replace '\s+ ', '_').tostring().split('_')[0] $QlogicFWVersion = $($QlogicFW[$i].Split(']')[1].trim() -replace '\s+ ', '_').tostring().split('_')[1] # $QlogicFWVersion } } } $nicinfo = callProcessStdin "$OEMQLogicPath\winfwnx2.exe" '' @('q') $FwPath2 = $FwPath.Replace('\', '/') $nicinfo = callProcess "$OEMQLogicPath\winfwnx2.exe" " -all upgrade -f -mbi $FwPath2 qlgcrestore" $QlogicFW = callProcessStdin "$PSScriptRoot\winfwnx2.exe" '' @('q') $QlogicFW = $QlogicFW -split "`r`n" for ($i = 0; $i -lt $QlogicFW.Length; $i++) { if ($QlogicFW[$i].ToUpper().IndexOf('QLOGIC') -ge 0) { if ($QlogicFW[$i].IndexOf(']') -ge 0) { # $QlogicFWNameNew = $($QlogicFW[$i].Split(']')[1].trim() -replace '\s+ ', '_').tostring().split('_')[0] $QlogicFWVersionNew = $($QlogicFW[$i].Split(']')[1].trim() -replace '\s+ ', '_').tostring().split('_')[1] # $QlogicFWVersionNew } } } addRowFW 'FW' $QlogicFWVersion $QlogicFWVersionNew $script:tableFW $script:rebootneed = 1 } function Update-OEMFirmware-Disk { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$FwPath = '', [Parameter(Mandatory = $true, Position = 1)] [string]$SerialNumber = '' ) # CheckVersion $script:tableFW = @() function addRowFW($FWType, $FWVersion, $FWVersionNew) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'Type' -MemberType Noteproperty -Value $FWType $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty -Value $FWVersion $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty -Value $FWVersionNew $script:tableFW += $row2 } $pd = Get-PhysicalDisk | Where-Object SerialNumber -eq $SerialNumber $SupportsUpdate = ($pd | Get-StorageFirmwareInformation).SupportsUpdate if ($SupportsUpdate) { $pdFWVersion = (Get-PhysicalDisk -SerialNumber $SerialNumber | Select-Object FirmwareVersion).FirmwareVersion $pd | Update-StorageFirmware -ImagePath $FwPath -SlotNumber 0 # Get-PhysicalDisk | Where-Object SerialNumber -eq $SerialNumber | Get-StorageFirmwareInformation | Select-Object FirmwareVersionInSlot | out-string $pdFWVersionNew = (Get-PhysicalDisk -SerialNumber $SerialNumber | Select-Object FirmwareVersion).FirmwareVersion addRowFW 'FW' $pdFWVersion $pdFWVersionNew $script:tableFW } else { $SupportsUpdate | Out-String } } function Get-Driver_Information { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$Path = '', [Parameter(Mandatory = $false, Position = 1)] [ValidateNotNullOrEmpty()] [ValidateSet($true, $false, 1, 0)] $Recursive = $false ) # CheckVersion $Recursive = [System.Convert]::ToBoolean($Recursive) $script:DriverList = @() function addRow2($File2, $Path2, $Provider2, $Version2, $DriverVersion2, $DriverDate2, $GUID2, $Class2) { $row2 = New-Object -TypeName PSObject $row2 | Add-Member -Name 'File' -MemberType Noteproperty -Value $File2 $row2 | Add-Member -Name 'Path' -MemberType Noteproperty -Value $Path2 $row2 | Add-Member -Name 'Provider' -MemberType Noteproperty -Value $Provider2 $row2 | Add-Member -Name 'Version' -MemberType Noteproperty -Value $Version2 $row2 | Add-Member -Name 'DriverVersion' -MemberType Noteproperty -Value $DriverVersion2 $row2 | Add-Member -Name 'DriverDate' -MemberType Noteproperty -Value $DriverDate2 $row2 | Add-Member -Name 'GUID' -MemberType Noteproperty -Value $GUID2 $row2 | Add-Member -Name 'Class' -MemberType Noteproperty -Value $Class2 $script:DriverList += $row2 } $driversCollection = (Get-ChildItem -Path "$Path" -Filter '*.inf' -recurse:$Recursive -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName) foreach ($Driver in $driversCollection) { if ((Get-Item $Driver) -is [System.IO.DirectoryInfo]) { continue } $GUID = '' $Version = '' $DriverVersion = '' $DriverDate = '' $Provider = '' $Class = '' $content = Get-Content -Path "$Driver" $line = ($content | Select-String 'ClassGuid') if ($null -ne $line) { $GUID = $line.Line.Split('=')[-1].Split(' ').Split(';') $GUID = ([string]$GUID).trim().Replace('{', '').Replace('}', '') } $line = ($content | Select-String 'DriverVer') if ($null -ne $line) { $Version = $line.Line.Split('=')[-1].Split(' ').Split(';') $Version = ([string]$Version).trim().Replace(' ', '') $DriverVersion = $Version.Split(',')[1].Trim() $DriverDate = $Version.Split(',')[0].Trim() } $line = ($content | Select-String 'Provider') if ($null -ne $line) { $Provider = $line[0].Line.Split('=')[-1].Split(' ').Split(';') $Provider = ([string]$Provider).trim() if ($Provider.IndexOf('%') -ge 0) { $line = ($content | Select-String $Provider)[1] if ($null -ne $line) { $Provider = $line.Line.Split('=')[-1].Split(' ').Split(';') $Provider = ([string]$Provider).trim() } if ($Provider.IndexOf('%') -ge 0) { for ($i = 0; $i -le $content.Length; $i++) { if ($content[$i].IndexOf('[Manufacturer]') -ge 0) { $Provider = $content[$i + 1].Split('=')[-1].Split(' ').Split(';') $Provider = ([string]$Provider).trim() break } } } } } $line = ($content | Select-String 'Class') if ($line.Line -gt 1) { foreach ($l in $Line) { if ($l.Line.Split('=')[0].Trim() -eq 'Class') { $line = $l break } } } if ($null -ne $line) { $Class = $line.Line.Split('=')[-1].Split(' ').Split(';') $Class = ([string]$Class).trim().Replace('{', '').Replace('}', '') } $FileName = Split-Path $Driver -Leaf addRow2 $FileName $Driver $Provider $Version $DriverVersion $DriverDate $GUID $Class } $script:DriverList } function Install-Driver { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$Path = '' ) # CheckVersion $script:updateRecord = @() function addRowUpdateRecord($updateState2, $updateAttempted2, $updateSuccessfully2, $updateResult2) { $rowUpdateRecord = New-Object -TypeName PSObject $rowUpdateRecord | Add-Member -Name 'updateState' -MemberType Noteproperty -Value $updateState2 $rowUpdateRecord | Add-Member -Name 'updateAttempted' -MemberType Noteproperty -Value $updateAttempted2 $rowUpdateRecord | Add-Member -Name 'updateSuccessfully' -MemberType Noteproperty -Value $updateSuccessfully2 $rowUpdateRecord | Add-Member -Name 'updateResult' -MemberType Noteproperty -Value $updateResult2 $script:updateRecord += $rowUpdateRecord } $updateState = '' $updateAttempted = '' $updateSuccessfully = '' $updateResult = '' $updateResult = pnputil.exe -i -a $Path foreach ($line in $updateResult) { if ($line.indexOf('Failed to install the driver') -ge 0) { $updateState = 'failed' } if ($line.indexOf('Driver package added successfully') -ge 0) { $updateState = 'success' } if ($line.indexOf('Total attempted') -ge 0) { $updateAttempted = $line.Split(':')[1].trim() } if ($line.indexOf('Number successfully imported') -ge 0) { $updateSuccessfully = $line.Split(':')[1].trim() } } addRowUpdateRecord $updateState $updateAttempted $updateSuccessfully $updateResult $script:updateRecord } function CheckVersionOnline() { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [string]$filePath = '' ) New-Item -ItemType File -Path $filePath > $null $script:currentVersion = (Get-InstalledModule -Name QCT-Update-Management -ErrorAction SilentlyContinue).version; $script:latestVersion = (Find-Module -Name QCT-Update-Management -ErrorAction SilentlyContinue).version; $script:currentVersion | Out-File $filePath -Append $script:latestVersion | Out-File $filePath -Append } function CheckVersion() { if ($null -ne $script:currentVersion) { $tempPath = [System.IO.Path]::GetTempPath() $name = 'QCT-Update-Management.ver' $filePath = Join-Path $tempPath $name if (Test-Path $filePath) { $lastWritetime = $(Get-Item $filePath).LastWriteTime if ($lastWritetime.Date -ge $(Get-Date).Date) { $content = Get-Content $filePath $script:currentVersion = $content[0] $script:latestVersion = $content[1] } else { Remove-Item $filePath CheckVersionOnline $filePath } } else { CheckVersionOnline $filePath } } if ($script:currentVersion -ne $script:latestVersion) { Write-Warning "Your package version is $script:currentVersion, the latest version is $script:latestVersion.(QCT-Update-Management)" } } $global:SearchResult = $null $global:DownloadUpdate = 0 function Check-WindowsUpdate() { [CmdletBinding()] Param ( [Parameter(Mandatory = $false)] [switch]$OptionalInstallation ) $global:SearchResult = $null if ($OptionalInstallation) { $OptionalInstallationStr = "IsInstalled=0 and DeploymentAction='OptionalInstallation' and AutoSelectOnWebSites=1 or " } else { $OptionalInstallationStr = '' } $Searcher = New-Object -ComObject Microsoft.Update.Searcher # https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdatesearcher-search $SearchCriteriaAllUpdates = "IsInstalled=0 and DeploymentAction='Installation' and BrowseOnly=0 or $OptionalInstallationStr IsPresent=1 and DeploymentAction='Uninstallation' or IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1" $global:SearchResult = $Searcher.Search($SearchCriteriaAllUpdates).Updates $global:SearchResult | Format-Table if ($global:SearchResult.Count -eq 0) { Write-Warning 'There are currently no updates available.' } } function Download-WindowsUpdate() { if ($null -eq $global:SearchResult) { Check-WindowsUpdate } elseif ($global:SearchResult.Count -eq 0) { Write-Warning 'There are currently no updates available.' } $global:DownloadUpdate = 0 if (($null -ne $global:SearchResult) -and ($global:SearchResult.Count -gt 0)) { $Session = New-Object -ComObject Microsoft.Update.Session $Downloader = $Session.CreateUpdateDownloader() $Downloader.Updates = $global:SearchResult $Downloader.Download() $global:DownloadUpdate = $global:SearchResult.Count } } function Install-WindowsUpdate() { if ($global:DownloadUpdate -eq 0) { Download-WindowsUpdate } if ($global:DownloadUpdate -ne 0) { $Installer = New-Object -ComObject Microsoft.Update.Installer $Installer.Updates = $global:SearchResult $Result = $Installer.Install() $Result } } function Get-WindowsUpdateInstalledList() { $script:WindowsUpdates = @() function addRowUWindowsUpdate($HotfixID2, $InstalledDate2, $PS2) { $rowWindowsUpdate = New-Object -TypeName PSObject $rowWindowsUpdate | Add-Member -Name 'HotfixID' -MemberType Noteproperty -Value $HotfixID2 $rowWindowsUpdate | Add-Member -Name 'InstalledDate' -MemberType Noteproperty -Value $InstalledDate2 $rowWindowsUpdate | Add-Member -Name 'PS' -MemberType Noteproperty -Value $PS2 $script:WindowsUpdates += $rowWindowsUpdate } $Session = New-Object -ComObject 'Microsoft.Update.Session' $Searcher = $Session.CreateUpdateSearcher() $historyCount = $Searcher.GetTotalHistoryCount() $Updates = $Searcher.QueryHistory(0, $historyCount) | Select-Object Title, Description, Date, @{Name = 'Operation'; Expression = { switch ($_.operation) { 1 { 'Installation' }; 2 { 'Uninstallation' }; 3 { 'Other' } } } }, @{name = 'HotfixID'; expression = { [regex]::match($_.Title, '[a-zA-Z]{2}\d{6,7}').value } } | Sort-Object date -Descending $Updates = $Updates | Where-Object { ('KB2267602', 'KB890830', 'KB4052623') -NotContains $_.HotfixID } # KB2267602 Security Intelligence Update for Microsoft Defender Antivirus # KB890830 Windows Malicious Software Removal Tool x64 # KB4052623 Update for Windows Defender Antivirus antimalware platform foreach ($WindowsUpdate in $Updates) { addRowUWindowsUpdate $WindowsUpdate.HotfixID $WindowsUpdate.Date $WindowsUpdate.Title } $Updates = Get-CimInstance -Class 'win32_quickfixengineering' | Select-Object -Property 'Caption', 'Description', 'HotfixID', @{Name = 'InstalledOn'; Expression = { ([DateTime]($_.InstalledOn)).ToLocalTime() } } | Sort-Object InstalledOn -Descending foreach ($WindowsUpdate in $Updates) { $updateCatalog = Invoke-WebRequest -Uri $('https://www.catalog.update.microsoft.com/Search.aspx?q=' + $WindowsUpdate.HotfixID) -UseBasicParsing $startIndex = $updateCatalog.Content.IndexOf("<table class=""resultsBorder resultsBackGround""") if ($startIndex -gt 0) { $endIndex = $updateCatalog.Content.IndexOf('</table>', $startIndex) $updateCatalogTable = $updateCatalog.Content.Substring($startIndex, $endIndex - $startIndex + 8) $nl = [Environment]::NewLine $updateCatalogines = $updateCatalogTable.Split($nl, [System.StringSplitOptions]::RemoveEmptyEntries) $updateCatalogines = $updateCatalogines | Where-Object { [regex]::match($_, 'KB\d{6,7}').Success } | ForEach-Object { $_.Trim() } | Select-Object -First 1 | Out-String if ($updateCatalogines.IndexOf('for Windows') -ge 0) { $updateCatalogines = $updateCatalogines.Substring(0, $updateCatalogines.IndexOf('for Windows')) } else { $updateCatalogines = $WindowsUpdate.Description } } else { $updateCatalogines = $WindowsUpdate.Description } addRowUWindowsUpdate $WindowsUpdate.HotfixID $WindowsUpdate.InstalledOn $updateCatalogines } $script:WindowsUpdates | Sort-Object InstalledDate -Descending } function Get-OSVersion() { $CurrentVersion = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $CurrentVersion.ProductName + ' ' + $CurrentVersion.DisplayVersion + '(' + $CurrentVersion.CurrentBuild + '.' + $CurrentVersion.UBR + ')' } function Get-ServerPhysicalDisk() { $SDDCCimInstance = Get-CimInstance -Namespace 'root/sddc/management' -ClassName 'SDDC_Drive' $phds = Get-PhysicalDisk | Select-Object *,@{L='Server';E={$SerialNumber=$_.AdapterSerialNumber;if ($SerialNumber -eq $null){$SerialNumber=$_.SerialNumber};($SDDCCimInstance| Where-Object SerialNumber -eq $SerialNumber).Server}} | Sort-Object size,server $defaultDisplaySet = 'DeviceId','FriendlyName','SerialNumber','MediaType','CanPool','OperationalStatus','HealthStatus','Usage','Size','Server' $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet) $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet) $phds | Add-Member -MemberType MemberSet -Name PSStandardMembers $PSStandardMembers $phds } function Get-SecuredCore() { try { $ErrorActionPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue' Import-Module CimCmdlets Import-Module TrustedPlatformModule function CheckTpmVersion { $TpmObj = Get-CimInstance -classname Win32_Tpm -namespace root\cimv2\Security\MicrosoftTpm if ($null -ne $TpmObj) { return $TpmObj.SpecVersion[0] -eq '2' } return $false } function checkSecureBoot { if ($null -ne (Get-Command Confirm-SecureBootUEFI -ErrorAction SilentlyContinue)) { try { return Confirm-SecureBootUEFI } catch { return $false } } return $false } function CheckDGSecurityServicesRunning($_val) { $DGObj = Get-CimInstance -classname Win32_DeviceGuard -namespace root\Microsoft\Windows\DeviceGuard for ($i = 0; $i -lt $DGObj.SecurityServicesRunning.length; $i++) { if ($DGObj.SecurityServicesRunning[$i] -eq $_val) { return $true } } return $false } function CheckDGSecurityServicesConfigured($_val) { $DGObj = Get-CimInstance -classname Win32_DeviceGuard -namespace root\Microsoft\Windows\DeviceGuard if ($_val -in $DGObj.SecurityServicesConfigured) { return $true } return $false } function CheckVBS { return (Get-CimInstance -classname Win32_DeviceGuard -namespace root\Microsoft\Windows\DeviceGuard).VirtualizationBasedSecurityStatus } function FeatureResult($FeatureName, $configured, $status) { if ($configured -and $status) { $Result = 'On' } elseif ((-not $configured) -and $status) { if (($FeatureName -eq 'secureBoot') -or ($FeatureName -eq 'tpm20') -or ($FeatureName -eq 'bootDMAProtection')) { $Result = 'On' } else { $Result = 'Running but not configured' } } elseif ($configured -and (-not $status)) { $Result = 'Enabled but not running' } else { if (($FeatureName -eq 'hvci') -or ($FeatureName -eq 'secureBoot') -or ($FeatureName -eq 'systemGuard')) { $Result = 'Not configured' } else { $Result = 'Not supported' } } return $Result } $bootDMAProtectionCheck = @" namespace SystemInfo { using System; using System.Runtime.InteropServices; public static class NativeMethods { internal enum SYSTEM_DMA_GUARD_POLICY_INFORMATION : int { // </summary> SystemDmaGuardPolicyInformation = 202 } [DllImport("ntdll.dll")] internal static extern Int32 NtQuerySystemInformation( SYSTEM_DMA_GUARD_POLICY_INFORMATION SystemDmaGuardPolicyInformation, IntPtr SystemInformation, Int32 SystemInformationLength, out Int32 ReturnLength); public static byte BootDmaCheck() { Int32 result; Int32 SystemInformationLength = 1; IntPtr SystemInformation = Marshal.AllocHGlobal(SystemInformationLength); Int32 ReturnLength; result = NativeMethods.NtQuerySystemInformation( NativeMethods.SYSTEM_DMA_GUARD_POLICY_INFORMATION.SystemDmaGuardPolicyInformation, SystemInformation, SystemInformationLength, out ReturnLength); if (result == 0) { byte info = Marshal.ReadByte(SystemInformation, 0); return info; } return 0; } } } "@ Add-Type -TypeDefinition $bootDMAProtectionCheck $feature = 'tpm20' $status = CheckTpmVersion $description = FeatureResult $feature $null $status $TPM20Obj = @{'Status' = $status; 'Configured' = $null; 'Description' = $description } $feature = 'secureBoot' $status = checkSecureBoot $description = FeatureResult $feature $null $status $secureBoot = @{'Status' = $status; 'Configured' = $null; 'Description' = $description } $feature = 'bootDMAProtection' $status = ([SystemInfo.NativeMethods]::BootDmaCheck()) -ne 0 $description = FeatureResult $feature $null $status $bootDMAProtection = @{'Status' = $status; 'Configured' = $null; 'Description' = $description } $feature = 'vbs' $vbsStatus = [int](CheckVBS) $vbsRunning = if ($vbsStatus -eq 2) { $true } else { $false } $vbsConfigured = if ($vbsStatus -gt 0) { $true } else { $false } $description = FeatureResult $feature $vbsConfigured $vbsRunning $VBS = @{'Status' = $vbsRunning; 'Configured' = $vbsConfigured; 'Description' = $description } $feature = 'hvci' $status = CheckDGSecurityServicesRunning(2) $configured = CheckDGSecurityServicesConfigured(2) $description = FeatureResult $feature $configured $status $HVCI = @{'Status' = $status; 'Configured' = $configured; 'Description' = $description } $feature = 'systemGuard' $status = CheckDGSecurityServicesRunning(3) $configured = CheckDGSecurityServicesConfigured(3) $description = FeatureResult $feature $configured $status $systemGuard = @{'Status' = $status; 'Configured' = $configured; 'Description' = $description } $securedCoreFeatures = @{} $securedCoreFeatures.Add('tpm20', $TPM20Obj) $securedCoreFeatures.Add('secureBoot', $secureBoot) $securedCoreFeatures.Add('bootDMAProtection', $bootDMAProtection) $securedCoreFeatures.Add('vbs', $VBS) $securedCoreFeatures.Add('hvci', $HVCI) $securedCoreFeatures.Add('systemGuard', $systemGuard) $securedCoreFeatures # foreach ($key in $securedCoreFeatures.Keys) { $key; $securedCoreFeatures.$key | Format-Table ; } } catch { Write-Host $_.Exception.Message } } function Set-SecuredCore() { [CmdletBinding()] Param ( [Parameter(Mandatory = $false, Position = 0)] [ValidateSet('hvci', 'credentialGuard', 'systemGuard', 'vbs')] [string]$featureName = '', [Parameter(Mandatory = $false, Position = 1)] [ValidateSet(1, 0)] [int]$value, [Parameter(Mandatory = $false, Position = 2)] [ValidateSet(1, 0)] [int]$secureBoot ) try { $ErrorActionPreference = 'SilentlyContinue' $InformationPreference = 'SilentlyContinue' $WarningPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue' $Script:secureBoot = [int]$secureBoot if (-not($Script:secureBoot -in @(0, 1, 3))) { Throw "Invalid value for parameter $Script:secureBoot. Use 0 (Disable RequirePlatformSecurityFeatures), 1 (Secure Boot only) or 3 (Secure Boot and DMA protection)" } $Script:value = [int]$value $valueWord = if ($value -eq 0 ) { 'disable' } else { 'enable' }; if (-not($Script:value -in @(0, 1))) { Throw "Invalid value for parameter $Script:value. Use 0 to disable or 1 to enable a secured core feature" } function checkRemote { if ($PSSenderInfo -or (Get-Host).Name -eq 'ServerRemoteHost') { return $true } return $false } $Script:isRemoteConnection = checkRemote function ExecuteCommandAndLog($_cmd) { try { Invoke-Expression $_cmd | Out-String } catch { Write-Host "Exception while exectuing $_cmd" Throw $_.Exception.Message } } function SystemGuard { $path = 'Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\SystemGuard' if (-Not(Test-Path $path)) { New-Item $path -Force } Set-ItemProperty -path $path -name 'Enabled' -value $Script:value -Type 'DWORD' -Force } function HVCI { $path = 'Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity' if (-Not(Test-Path $path)) { New-Item $path -Force } $uefiLock = Get-ItemProperty -Path $path | ` Microsoft.PowerShell.Utility\Select-Object 'Locked' -ExpandProperty 'Locked' -ErrorAction SilentlyContinue if ($uefiLock -eq 1 -and $Script:isRemoteConnection) { Throw "UEFI lock enabled. Cannot $Script:actionWord HVCI remotely." } if ($Script:value -eq 0) { Remove-ItemProperty -Path $path -Name 'WasEnabledBy' -ErrorAction SilentlyContinue } if ($Script:value -eq 1) { # Note: VBS will automatically turn on if you enable VBS (HVCI, Cred Guard) VBS Set-ItemProperty -path $path -name 'WasEnabledBy' -value 0 -Type 'DWORD' -Force } # Enable HVCI Set-ItemProperty -path $path -name 'Enabled' -value $Script:value -Type 'DWORD' -Force } function VBS { $path = 'Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard' if (-Not(Test-Path $path)) { New-Item $path -Force } # For Windows 10 version 1607 and later $uefiLock = Get-ItemProperty -Path $path | ` Microsoft.PowerShell.Utility\Select-Object 'Locked' -ExpandProperty 'Locked' -ErrorAction SilentlyContinue # Check UEFI lock for Windows 10 version 1511 and earlier $uefiUnlock = Get-ItemProperty -Path $path | ` Microsoft.PowerShell.Utility\Select-Object 'Unocked' -ExpandProperty 'Unocked' -ErrorAction SilentlyContinue if (($uefiLock -eq 1 -or $uefiUnlock -eq 0) -and $Script:isRemoteConnection) { Throw "UEFI lock enabled. Cannot $valueWord VBS remotely." } $currentSecureBootValue = Get-ItemProperty -Path $path | ` Microsoft.PowerShell.Utility\Select-Object 'RequirePlatformSecurityFeatures' -ExpandProperty 'RequirePlatformSecurityFeatures' -ErrorAction SilentlyContinue if ($Script:value -eq 0) { # Note: all other VBS (HVCI, Cred Guard) need to be disabled as well, or VBS will automatically turn on HVCI CredentialGuard Set-ItemProperty -path $path -Name 'EnableVirtualizationBasedSecurity' -Value 0 -Type 'DWORD' -Force # Disable secure boot if ($currentSecureBootValue) { Remove-ItemProperty -Path $path -Name 'RequirePlatformSecurityFeatures' -ErrorAction SilentlyContinue } } if ($Script:value -eq 1) { Set-ItemProperty -path $path -Name 'EnableVirtualizationBasedSecurity' -Value 1 -Type 'DWORD' -Force # Enable Secure Boot if ($Script:secureBoot -in @(1, 3)) { <# - 1: Secure Boot only - 3: Secure Boot and DMA protection. #> Set-ItemProperty -path $path -name 'RequirePlatformSecurityFeatures' -value $Script:secureBoot -Type 'DWORD' -Force } } } function CredentialGuard { $securityServicesConfigured = (Get-CimInstance -classname Win32_DeviceGuard -namespace root\Microsoft\Windows\DeviceGuard).SecurityServicesConfigured if ($securityServicesConfigured.Length -gt 0 -and ($Script:value -eq 0 -and (-not(1 -in $securityServicesConfigured))) -or ($Script:value -eq 1 -and (1 -in $securityServicesConfigured))) { return; } $path = 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA' if (-Not(Test-Path $path)) { New-Item $path -Force } <# 0: (Disabled) Turns off Windows Defender Credential Guard remotely if configured previously without UEFI Lock 1: (Enabled with UEFI lock) Turns on Windows Defender Credential Guard with UEFI lock 2: (Enabled without lock) Turns on Windows Defender Credential Guard without UEFI lock #> $credGuardValue = Get-ItemProperty -Path $path | ` Microsoft.PowerShell.Utility\Select-Object 'LsaCfgFlags' -ExpandProperty 'LsaCfgFlags' -ErrorAction SilentlyContinue if ($credGuardValue -eq 1 -and $Script:isRemoteConnection) { Throw "UEFI lock enabled. Cannot $valueWord Credential Guard remotely." } if ($Script:value -eq 0) { Remove-ItemProperty -path $path -name 'LsaCfgFlags' -ErrorAction SilentlyContinue -Force # This setting is persisted in EFI (firmware) variables so we need to delete it $settingPath = 'Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\DeviceGuard' Remove-ItemProperty -Path $settingPath -Name 'LsaCfgFlags' -ErrorAction SilentlyContinue # Set of commands to run SecConfig.efi to delete UEFI variables if were set in pre OS $FreeDrive = Get-ChildItem function:[s-z]: -Name | Where-Object { !(Test-Path $_) } | Get-Random ExecuteCommandAndLog 'mountvol $FreeDrive /s' Copy-Item "$env:windir\System32\SecConfig.efi" $FreeDrive\EFI\Microsoft\Boot\SecConfig.efi -Force | Out-String ExecuteCommandAndLog 'bcdedit /create "{0cb3b571-2f2e-4343-a879-d86a476d7215}" /d DGOptOut /application osloader' ExecuteCommandAndLog 'bcdedit /set "{0cb3b571-2f2e-4343-a879-d86a476d7215}" path \EFI\Microsoft\Boot\SecConfig.efi' ExecuteCommandAndLog 'bcdedit /set "{bootmgr}" bootsequence "{0cb3b571-2f2e-4343-a879-d86a476d7215}"' ExecuteCommandAndLog 'bcdedit /set "{0cb3b571-2f2e-4343-a879-d86a476d7215}" loadoptions DISABLE-LSA-ISO,DISABLE-VBS' ExecuteCommandAndLog 'bcdedit /set "{0cb3b571-2f2e-4343-a879-d86a476d7215}" device partition=$FreeDrive' ExecuteCommandAndLog 'mountvol $FreeDrive /d' } else { VBS Set-ItemProperty -path $path -name 'LsaCfgFlags' -value 1 -Type 'DWORD' -Force } } switch ($featureName) { 'hvci' { HVCI } 'credentialGuard' { CredentialGuard } 'systemGuard' { SystemGuard } 'vbs' { VBS } } } catch { Write-Host $_.Exception.Message } } Export-ModuleMember Get-Driver_FW Export-ModuleMember Enable-DiskLocate Export-ModuleMember Disable-DiskLocate Export-ModuleMember Get-SmartInfo Export-ModuleMember Get-ClusterDriver_FW Export-ModuleMember Update-OEMFirmware-LSI Export-ModuleMember Update-OEMFirmware-Mellanox Export-ModuleMember Update-OEMFirmware-QLogic Export-ModuleMember Update-OEMFirmware-Disk Export-ModuleMember Reset-OEMFirmware-Mellanox Export-ModuleMember Query-OEMFirmware-Mellanox Export-ModuleMember Get-Driver_Information Export-ModuleMember Install-Driver Export-ModuleMember Check-WindowsUpdate Export-ModuleMember Download-WindowsUpdate Export-ModuleMember Install-WindowsUpdate Export-ModuleMember Get-WindowsUpdateInstalledList Export-ModuleMember Get-OSVersion Export-ModuleMember Get-ServerPhysicalDisk Export-ModuleMember Get-SecuredCore Export-ModuleMember Set-SecuredCore # SIG # Begin signature block # MIIbqgYJKoZIhvcNAQcCoIIbmzCCG5cCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUxna/13+WBb+wWOLw4DIEuedU # rtWgghYkMIIDFzCCAf+gAwIBAgIQFizPTEXNvrZBigm0vSZdcTANBgkqhkiG9w0B # AQsFADAXMRUwEwYDVQQDDAx3YWMud2RhYy5xY3QwHhcNMjIwODMxMDE1MjI5WhcN # MjUwODMxMDIwMjI0WjAXMRUwEwYDVQQDDAx3YWMud2RhYy5xY3QwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQD1T89HN6W7Jv+5yA+5fp0sukVzVUNeqsvg # Zu9TSDCK2wlHpvL8LPDSzp0xOSvnXus5YTs9EzclHFMCbE1IJZb5lDw9ffHgumXt # tXr5gddxtRaVBuZncD1noi8wfXk+Me6I2KGZEA2h5yCeV7fmpBXjp9ya5IkZEACc # 5Z6oJtUfye+PO0L5AMJGFgPT/5ELGO1wTHda2g4kJWX8IhEcTI+aTTeUKrMmJNX8 # emHdQANoTNmhNglm60i18py6ekZsGgORvRz/W/KJdVr63SYJxVzqLNwcr7iZuFKk # +up7ZsqdhLMly+qrmDDe5s3fn2/YamdyywrYCnv3IY87sapW7bvpAgMBAAGjXzBd # MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAXBgNVHREEEDAO # ggx3YWMud2RhYy5xY3QwHQYDVR0OBBYEFK12tsDjrwoBxzX4InAacHPyomM8MA0G # CSqGSIb3DQEBCwUAA4IBAQAeCnXUG5gZayX4SQsnVUw2tcuNh5O4GszbfuIDm13U # 52WgvpONGrz/NNhsAdcTR0giOGVNqVcUP21Qwa+SkogVSvHcpwoFruwy7DaXnRmO # r7tE+/URzmtULEEilnEiK8FfUZneat5M/tmIG7kD40HfCdLMUpB9bX4+jBeVZa1o # EhiErmfC7R3oHxwQfAcR4oZxQqZOOK+qsYh3csF65v0teG95iT0QDnIkZplQViDo # L1H0UT40qGor5M6EhRCC8Iq/gso29zMEmGEDJkdeaJioEa4tiqcadtvZAGGbcFs2 # gBVNQK/pWvAcTsWEuL1Nc+Q1KqQEQOJ8GiL70S+udRFWMIIFjTCCBHWgAwIBAgIQ # DpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEV # MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t # MSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAx # MDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM # RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQD # ExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4IC # DwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aa # za57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllV # cq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT # +CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd # 463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+ # EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92k # J7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5j # rubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 # f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJU # KSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+wh # X8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQAB # o4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5n # P+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0P # AQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29j # c3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDww # OqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ # RFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IB # AQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229 # GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FD # RJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVG # amlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCw # rFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvR # XKwYw02fc7cBqZ9Xql4o4rmUMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe # WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl # cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp # Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1 # OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5 # BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0 # YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz # wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL # f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C # 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5 # n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd # zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH # po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/ # oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV # A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg # 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM # DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E # VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB # /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud # IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV # HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f # BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1 # c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB # MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT # zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E # UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm # niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw # cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH # wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/ # JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9 # Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm # 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB # tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw # ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2 # 7dZ8/DCCBsIwggSqoAMCAQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJKoZIhvcNAQEL # BQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD # VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt # cGluZyBDQTAeFw0yMzA3MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTlaMEgxCzAJBgNV # BAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEgMB4GA1UEAxMXRGlnaUNl # cnQgVGltZXN0YW1wIDIwMjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQCjU0WHHYOOW6w+VLMj4M+f1+XS512hDgncL0ijl3o7Kpxn3GIVWMGpkxGnzaqy # at0QKYoeYmNp01icNXG/OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9YrIBzBl5S0pVCB # 8s/LB6YwaMqDQtr8fwkklKSCGtpqutg7yl3eGRiF+0XqDWFsnf5xXsQGmjzwxS55 # DxtmUuPI1j5f2kPThPXQx/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4KTlr4HhZl+NE # K0rVlc7vCBfqgmRN/yPjyobutKQhZHDr1eWg2mOzLukF7qr2JPUdvJscsrdf3/Du # dn0xmWVHVZ1KJC+sK5e+n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZBzcBkQ8ctVHN # qkxmg4hoYru8QRt4GW3k2Q/gWEH72LEs4VGvtK0VBhTqYggT02kefGRNnQ/fztFe # jKqrUBXJs8q818Q7aESjpTtC/XN97t0K/3k0EH6mXApYTAA+hWl1x4Nk1nXNjxJ2 # VqUk+tfEayG66B80mC866msBsPf7Kobse1I4qZgJoXGybHGvPrhvltXhEBP+YUcK # jP7wtsfVx95sJPC/QoLKoHE9nJKTBLRpcCcNT7e1NtHJXwikcKPsCvERLmTgyyIr # yvEoEyFJUX4GZtM7vvrrkTjYUQfKlLfiUKHzOtOKg8tAewIDAQABo4IBizCCAYcw # DgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYB # BQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQY # MBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSltu8T5+/N0GSh1Vap # ZTGj3tXjSTBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0Eu # Y3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au # ZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2Vy # dC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5n # Q0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1PijxonNgl/8ss # 5M3qXSKS7IwiAqm4z4Co2efjxe0mgopxLxjdTrbebNfhYJwr7e09SI64a7p8Xb3C # YTdoSXej65CqEtcnhfOOHpLawkA4n13IoC4leCWdKgV6hCmYtld5j9smViuw86e9 # NwzYmHZPVrlSwradOKmB521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXTUOREEr4gDZ6p # RND45Ul3CFohxbTPmJUaVLq5vMFpGbrPFvKDNzRusEEm3d5al08zjdSNd311RaGl # WCZqA0Xe2VC1UIyvVr1MxeFGxSjTredDAHDezJieGYkD6tSRN+9NUvPJYCHEVkft # 2hFLjDLDiOZY4rbbPvlfsELWj+MXkdGqwFXjhr+sJyxB0JozSqg21Llyln6XeThI # X8rC3D0y33XWNmdaifj2p8flTzU8AL2+nCpseQHc2kTmOt44OwdeOVj0fHMxVaCA # EcsUDH6uvP6k63llqmjWIso765qCNVcoFstp8jKastLYOrixRoZruhf9xHdsFWyu # q69zOuhJRrfVf8y2OMDY7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7KP845VJa1qwX # IiNO9OzTF/tQa/8Hdx9xl0RBybhG02wyfFgvZ0dl5Rtztpn5aywGRu9BHvDwX+Db # 2a2QgESvgBBBijGCBPAwggTsAgEBMCswFzEVMBMGA1UEAwwMd2FjLndkYWMucWN0 # AhAWLM9MRc2+tkGKCbS9Jl1xMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEMMQow # CKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC # AQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBRdUmXmRDzLNKy5EKf8 # ZblufXYcPDANBgkqhkiG9w0BAQEFAASCAQCPOnViorB+XoMRR2CFeSx2UrMEZ5/P # 7HeDNiGGUlfalzO6FVLihrTF+MuQa6GoD/6o4shIv2c/lRH7i3I4nlBS6S9wG77Q # UMeEE8aAoQIlcdyegRwfp+yP957FS4y9uKJsS1kxcgSqutiN+tMenikeJka/EQDF # 5qXIjs0p9GQf8WRQhtI0Wh4WVQA5WdhuocoPOSy6zViFOMbqbSZl9+Gd12s4EDV7 # r8jaSvoz0CefA6EBApM6Ffqz+DzQAnJRULRiU+LMJviOKcwV+Pp6SWFBLqrU6+kj # zenIPvzRmBGPzBkZnXRR8H4NV52oTVd6JodWgz3q0ZhLDnnRdG6TluAGoYIDIDCC # AxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNV # BAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0 # IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+Vh # FjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ # KoZIhvcNAQkFMQ8XDTIzMTEyMDA2MTYwMlowLwYJKoZIhvcNAQkEMSIEIIVNf9bm # 5Fe0KnpYrdr4rSESM3K7uk66I+ob6sZLHl/jMA0GCSqGSIb3DQEBAQUABIICAIeF # OkK7R7zhtWTlVNqYKCHYFHvmI4szm1cI1S5ACs0OQMNVtlmXAEkEZTj55lHPbm3k # SRx3Idv8AlJjb7fYMK4xiulNfoF405O40/nAU10P58A9d8zwk3l5Q2wwEal1HD47 # MNpu6ZPHNQvfK3XrWQdUpM+AyikGy190PWfNSkdid69H+TdFMXEM/cyDLzHdRpmm # Ywbti+Eyc/ABSOcGKd4aGkEJADkuUTwzHZJQU3414wLwEEsp7UTICU/Y4Iyb/Cg+ # P8oAtItoMlryv9XdH/Az45mQiQPCgbctgwyUb9bg97cawH+d5UpUUpxrhSQIw3nK # b2aZtWjH91y4Clj9q/RTwlFhDuH97WJXfz4COcmY862l6+AuNWrI4RY+c/GgdDmN # ymanR2d5h3sccbLbK7iB+j83x13DJgsc3rDmuoKQdmsQjT8R/lc1+WKgvjm0YIzD # kFCPHZ0+x6/D+6IpP7+sf8xteFgYcTjAtrScBFiJvk/FTOD1nm6hLR7xV0Pm0z1P # VEqHYaRyg/wLaXJFdPwnRbpw2axGKRQj2ORP3yiig1YsiUoi08c/JByX7y9/XE/L # IHyLe9OQXPCME50kCj09XbJqE8hMis27uQ8xdohh2bc+wUDhorv8Guw8c4FEjMx5 # NOgM/joJdFWiOg3LFoNEDU+n/xLvOaVaQci1aI+v # SIG # End signature block |