-
-
Save SMSAgentSoftware/314e366df0fbe2637c4e562c7835413a to your computer and use it in GitHub Desktop.
##################### | |
## HP BIOS UPDATER ## | |
##################### | |
# Params | |
$HPIAWebUrl = "https://ftp.hp.com/pub/caps-softpaq/cmit/HPIA.html" # Static web page of the HP Image Assistant | |
$BIOSPassword = "MyPassword" | |
$script:ContainerURL = "https://mystorageaccount.blob.core.windows.net/mycontainer" # URL of your Azure blob storage container | |
$script:FolderPath = "HP_BIOS_Updates" # the subfolder to put logs into in the storage container | |
$script:SASToken = "mysastoken" # the SAS token string for the container (with write permission) | |
$ProgressPreference = 'SilentlyContinue' # to speed up web requests | |
################################ | |
## Create Directory Structure ## | |
################################ | |
$RootFolder = $env:ProgramData | |
$ParentFolderName = "Contoso" | |
$ChildFolderName = "HP_BIOS_Update" | |
$ChildFolderName2 = Get-Date -Format "yyyy-MMM-dd_HH.mm.ss" | |
$script:WorkingDirectory = "$RootFolder\$ParentFolderName\$ChildFolderName\$ChildFolderName2" | |
try | |
{ | |
[void][System.IO.Directory]::CreateDirectory($WorkingDirectory) | |
} | |
catch | |
{ | |
throw | |
} | |
# Function write to a log file in ccmtrace format | |
Function script:Write-Log { | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string]$Message, | |
[Parameter()] | |
[ValidateSet(1, 2, 3)] # 1-Info, 2-Warning, 3-Error | |
[int]$LogLevel = 1, | |
[Parameter(Mandatory = $true)] | |
[string]$Component, | |
[Parameter(Mandatory = $false)] | |
[object]$Exception | |
) | |
$LogFile = "$WorkingDirectory\HP_BIOS_Update.log" | |
If ($Exception) | |
{ | |
[String]$Message = "$Message" + "$Exception" | |
} | |
$TimeGenerated = "$(Get-Date -Format HH:mm:ss).$((Get-Date).Millisecond)+000" | |
$Line = '<![LOG[{0}]LOG]!><time="{1}" date="{2}" component="{3}" context="" type="{4}" thread="" file="">' | |
$LineFormat = $Message, $TimeGenerated, (Get-Date -Format MM-dd-yyyy), $Component, $LogLevel | |
$Line = $Line -f $LineFormat | |
# Write to log | |
Add-Content -Value $Line -Path $LogFile -ErrorAction SilentlyContinue | |
} | |
# Function to upload log file to Azure Blob storage | |
Function Upload-LogFilesToAzure { | |
$Date = Get-date -Format "yyyy-MM-dd_HH.mm.ss" | |
$HpFirmwareUpdRecLog = Get-ChildItem -Path $WorkingDirectory -Include HpFirmwareUpdRec.log -Recurse -ErrorAction SilentlyContinue | |
$HPBIOSUPDRECLog = Get-ChildItem -Path $WorkingDirectory -Include HPBIOSUPDREC64.log -Recurse -ErrorAction SilentlyContinue | |
If ($HpFirmwareUpdRecLog) | |
{ | |
$File = $HpFirmwareUpdRecLog | |
} | |
ElseIf ($HPBIOSUPDRECLog) | |
{ | |
$File = $HPBIOSUPDRECLog | |
} | |
Else{} | |
If ($File) | |
{ | |
$Body = Get-Content $($File.FullName) -Raw -ErrorAction SilentlyContinue | |
If ($Body) | |
{ | |
$URI = "$ContainerURL/$FolderPath/$($Env:COMPUTERNAME)`_$Date`_$($File.Name)$SASToken" | |
$Headers = @{ | |
'x-ms-content-length' = $($File.Length) | |
'x-ms-blob-type' = 'BlockBlob' | |
} | |
Invoke-WebRequest -Uri $URI -Method PUT -Headers $Headers -Body $Body -ErrorAction SilentlyContinue | |
} | |
} | |
$File2 = Get-Item $WorkingDirectory\HP_BIOS_Update.log -ErrorAction SilentlyContinue | |
$Body2 = Get-Content $($File2.FullName) -Raw -ErrorAction SilentlyContinue | |
If ($Body2) | |
{ | |
$URI2 = "$ContainerURL/$FolderPath/$($Env:COMPUTERNAME)`_$Date`_$($File2.Name)$SASToken" | |
$Headers2 = @{ | |
'x-ms-content-length' = $($File2.Length) | |
'x-ms-blob-type' = 'BlockBlob' | |
} | |
Invoke-WebRequest -Uri $URI2 -Method PUT -Headers $Headers2 -Body $Body2 -ErrorAction SilentlyContinue | |
} | |
} | |
Write-Log -Message "#######################" -Component "Preparation" | |
Write-Log -Message "## Starting BIOS update run ##" -Component "Preparation" | |
Write-Log -Message "#######################" -Component "Preparation" | |
################################# | |
## Disable IE First Run Wizard ## | |
################################# | |
# This prevents an error running Invoke-WebRequest when IE has not yet been run in the current context | |
Write-Log -Message "Disabling IE first run wizard" -Component "Preparation" | |
$null = New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft" -Name "Internet Explorer" -Force | |
$null = New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Internet Explorer" -Name "Main" -Force | |
$null = New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Internet Explorer\Main" -Name "DisableFirstRunCustomize" -PropertyType DWORD -Value 1 -Force | |
########################## | |
## Get latest HPIA Info ## | |
########################## | |
Write-Log -Message "Finding info for latest version of HP Image Assistant (HPIA)" -Component "DownloadHPIA" | |
try | |
{ | |
$HTML = Invoke-WebRequest -Uri $HPIAWebUrl -ErrorAction Stop | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to download the HPIA web page. $($_.Exception.Message)" -Component "DownloadHPIA" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
$HPIASoftPaqNumber = ($HTML.Links | Where {$_.href -match "hp-hpia-"}).outerText | |
$HPIADownloadURL = ($HTML.Links | Where {$_.href -match "hp-hpia-"}).href | |
$HPIAFileName = $HPIADownloadURL.Split('/')[-1] | |
Write-Log -Message "SoftPaq number is $HPIASoftPaqNumber" -Component "DownloadHPIA" | |
Write-Log -Message "Download URL is $HPIADownloadURL" -Component "DownloadHPIA" | |
################### | |
## Download HPIA ## | |
################### | |
Write-Log -Message "Downloading the HPIA" -Component "DownloadHPIA" | |
try | |
{ | |
$ExistingBitsJob = Get-BitsTransfer -Name "$HPIAFileName" -AllUsers -ErrorAction SilentlyContinue | |
If ($ExistingBitsJob) | |
{ | |
Write-Log -Message "An existing BITS tranfer was found. Cleaning it up." -Component "DownloadHPIA" -LogLevel 2 | |
Remove-BitsTransfer -BitsJob $ExistingBitsJob | |
} | |
$BitsJob = Start-BitsTransfer -Source $HPIADownloadURL -Destination $WorkingDirectory\$HPIAFileName -Asynchronous -DisplayName "$HPIAFileName" -Description "HPIA download" -RetryInterval 60 -ErrorAction Stop | |
do { | |
Start-Sleep -Seconds 5 | |
$Progress = [Math]::Round((100 * ($BitsJob.BytesTransferred / $BitsJob.BytesTotal)),2) | |
Write-Log -Message "Downloaded $Progress`%" -Component "DownloadHPIA" | |
} until ($BitsJob.JobState -in ("Transferred","Error")) | |
If ($BitsJob.JobState -eq "Error") | |
{ | |
Write-Log -Message "BITS tranfer failed: $($BitsJob.ErrorDescription)" -Component "DownloadHPIA" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
Write-Log -Message "Download is finished" -Component "DownloadHPIA" | |
Complete-BitsTransfer -BitsJob $BitsJob | |
Write-Log -Message "BITS transfer is complete" -Component "DownloadHPIA" | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to start a BITS transfer for the HPIA: $($_.Exception.Message)" -Component "DownloadHPIA" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
################## | |
## Extract HPIA ## | |
################## | |
Write-Log -Message "Extracting the HPIA" -Component "Analyze" | |
try | |
{ | |
$Process = Start-Process -FilePath $WorkingDirectory\$HPIAFileName -WorkingDirectory $WorkingDirectory -ArgumentList "/s /f .\HPIA\ /e" -NoNewWindow -PassThru -Wait -ErrorAction Stop | |
Start-Sleep -Seconds 5 | |
If (Test-Path $WorkingDirectory\HPIA\HPImageAssistant.exe) | |
{ | |
Write-Log -Message "Extraction complete" -Component "Analyze" | |
} | |
Else | |
{ | |
Write-Log -Message "HPImageAssistant not found!" -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to extract the HPIA: $($_.Exception.Message)" -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
############################################## | |
## Analyze available BIOS updates with HPIA ## | |
############################################## | |
Write-Log -Message "Analyzing system for available BIOS updates" -Component "Analyze" | |
try | |
{ | |
$Process = Start-Process -FilePath $WorkingDirectory\HPIA\HPImageAssistant.exe -WorkingDirectory $WorkingDirectory -ArgumentList "/Operation:Analyze /Category:BIOS /Selection:All /Action:List /Silent /ReportFolder:$WorkingDirectory\Report" -NoNewWindow -PassThru -Wait -ErrorAction Stop | |
If ($Process.ExitCode -eq 0) | |
{ | |
Write-Log -Message "Analysis complete" -Component "Analyze" | |
} | |
elseif ($Process.ExitCode -eq 256) | |
{ | |
Write-Log -Message "The analysis returned no recommendation. No BIOS update is available at this time" -Component "Analyze" -LogLevel 2 | |
Upload-LogFilesToAzure | |
Exit 0 | |
} | |
elseif ($Process.ExitCode -eq 4096) | |
{ | |
Write-Log -Message "This platform is not supported!" -Component "Analyze" -LogLevel 2 | |
Upload-LogFilesToAzure | |
throw | |
} | |
Else | |
{ | |
Write-Log -Message "Process exited with code $($Process.ExitCode). Expecting 0." -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to start the HPImageAssistant.exe: $($_.Exception.Message)" -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
# Read the XML report | |
Write-Log -Message "Reading xml report" -Component "Analyze" | |
try | |
{ | |
$XMLFile = Get-ChildItem -Path "$WorkingDirectory\Report" -Recurse -Include *.xml -ErrorAction Stop | |
If ($XMLFile) | |
{ | |
Write-Log -Message "Report located at $($XMLFile.FullName)" -Component "Analyze" | |
try | |
{ | |
[xml]$XML = Get-Content -Path $XMLFile.FullName -ErrorAction Stop | |
$Recommendation = $xml.HPIA.Recommendations.BIOS.Recommendation | |
If ($Recommendation) | |
{ | |
$CurrentBIOSVersion = $Recommendation.TargetVersion | |
$ReferenceBIOSVersion = $Recommendation.ReferenceVersion | |
$DownloadURL = "https://" + $Recommendation.Solution.Softpaq.Url | |
$SoftpaqFileName = $DownloadURL.Split('/')[-1] | |
Write-Log -Message "Current BIOS version is $CurrentBIOSVersion" -Component "Analyze" | |
Write-Log -Message "Recommended BIOS version is $ReferenceBIOSVersion" -Component "Analyze" | |
Write-Log -Message "Softpaq download URL is $DownloadURL" -Component "Analyze" | |
} | |
Else | |
{ | |
Write-Log -Message "Failed to find a BIOS recommendation in the XML report" -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to parse the XML file: $($_.Exception.Message)" -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
Else | |
{ | |
Write-Log -Message "Failed to find an XML report." -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to find an XML report: $($_.Exception.Message)" -Component "Analyze" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
############################### | |
## Download the BIOS softpaq ## | |
############################### | |
Write-Log -Message "Downloading the Softpaq" -Component "DownloadBIOSUpdate" | |
try | |
{ | |
$ExistingBitsJob = Get-BitsTransfer -Name "$SoftpaqFileName" -AllUsers -ErrorAction SilentlyContinue | |
If ($ExistingBitsJob) | |
{ | |
Write-Log -Message "An existing BITS tranfer was found. Cleaning it up." -Component "DownloadBIOSUpdate" -LogLevel 2 | |
Remove-BitsTransfer -BitsJob $ExistingBitsJob | |
} | |
$BitsJob = Start-BitsTransfer -Source $DownloadURL -Destination $WorkingDirectory\$SoftpaqFileName -Asynchronous -DisplayName "$SoftpaqFileName" -Description "BIOS update download" -RetryInterval 60 -ErrorAction Stop | |
do { | |
Start-Sleep -Seconds 5 | |
$Progress = [Math]::Round((100 * ($BitsJob.BytesTransferred / $BitsJob.BytesTotal)),2) | |
Write-Log -Message "Downloaded $Progress`%" -Component "DownloadBIOSUpdate" | |
} until ($BitsJob.JobState -in ("Transferred","Error")) | |
If ($BitsJob.JobState -eq "Error") | |
{ | |
Write-Log -Message "BITS tranfer failed: $($BitsJob.ErrorDescription)" -Component "DownloadBIOSUpdate" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
Write-Log -Message "Download is finished" -Component "DownloadBIOSUpdate" | |
Complete-BitsTransfer -BitsJob $BitsJob | |
Write-Log -Message "BITS transfer is complete" -Component "DownloadBIOSUpdate" | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to start a BITS transfer for the BIOS update: $($_.Exception.Message)" -Component "DownloadBIOSUpdate" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
######################### | |
## Extract BIOS Update ## | |
######################### | |
Write-Log -Message "Extracting the BIOS Update" -Component "ExtractBIOSUpdate" | |
$BIOSUpdateDirectoryName = $SoftpaqFileName.Split('.')[0] | |
try | |
{ | |
$Process = Start-Process -FilePath $WorkingDirectory\$SoftpaqFileName -WorkingDirectory $WorkingDirectory -ArgumentList "/s /f .\$BIOSUpdateDirectoryName\ /e" -NoNewWindow -PassThru -Wait -ErrorAction Stop | |
Start-Sleep -Seconds 5 | |
$HpFirmwareUpdRec = Get-ChildItem -Path $WorkingDirectory -Include HpFirmwareUpdRec.exe -Recurse -ErrorAction SilentlyContinue | |
$HPBIOSUPDREC = Get-ChildItem -Path $WorkingDirectory -Include HPBIOSUPDREC.exe -Recurse -ErrorAction SilentlyContinue | |
If ($HpFirmwareUpdRec) | |
{ | |
$BIOSExecutable = $HpFirmwareUpdRec | |
} | |
ElseIf ($HPBIOSUPDREC) | |
{ | |
$BIOSExecutable = $HPBIOSUPDREC | |
} | |
Else | |
{ | |
Write-Log -Message "BIOS update executable not found!" -Component "ExtractBIOSUpdate" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
Write-Log -Message "Extraction complete" -Component "ExtractBIOSUpdate" | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to extract the softpaq: $($_.Exception.Message)" -Component "ExtractBIOSUpdate" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
############################# | |
## Check for BIOS password ## | |
############################# | |
try | |
{ | |
$SetupPwd = (Get-CimInstance -Namespace ROOT\HP\InstrumentedBIOS -ClassName HP_BIOSPassword -Filter "Name='Setup Password'" -ErrorAction Stop).IsSet | |
If ($SetupPwd -eq 1) | |
{ | |
Write-Log -Message "The BIOS has a password set" -Component "BIOSPassword" | |
$BIOSPasswordSet = $true | |
} | |
Else | |
{ | |
Write-Log -Message "No password has been set on the BIOS" -Component "BIOSPassword" | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Unable to determine if a BIOS password has been set: $($_.Exception.Message)" -Component "BIOSPassword" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
########################## | |
## Create password file ## | |
########################## | |
If ($BIOSPasswordSet) | |
{ | |
Write-Log -Message "Creating an encrypted password file" -Component "BIOSPassword" | |
$HpqPswd = Get-ChildItem -Path $WorkingDirectory -Include HpqPswd.exe -Recurse -ErrorAction SilentlyContinue | |
If ($HpqPswd) | |
{ | |
try | |
{ | |
$Process = Start-Process -FilePath $HpqPswd.FullName -WorkingDirectory $WorkingDirectory -ArgumentList "-p""$BIOSPassword"" -f.\password.bin -s" -NoNewWindow -PassThru -Wait -ErrorAction Stop | |
Start-Sleep -Seconds 5 | |
If (Test-Path $WorkingDirectory\password.bin) | |
{ | |
Write-Log -Message "File successfully created" -Component "BIOSPassword" | |
} | |
Else | |
{ | |
Write-Log -Message "Encrypted password file could not be found!" -Component "BIOSPassword" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to create an encrypted password file: $($_.Exception.Message)" -Component "BIOSPassword" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
else | |
{ | |
Write-Log -Message "Failed to locate HP password encryption utility!" -Component "BIOSPassword" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
########################### | |
## Stage the BIOS update ## | |
########################### | |
Write-Log -Message "Staging BIOS firmware update" -Component "BIOSFlash" | |
try | |
{ | |
If ($BIOSPasswordSet) | |
{ | |
$Process = Start-Process -FilePath "$($BIOSExecutable.FullName)" -WorkingDirectory $WorkingDirectory -ArgumentList "-s -p.\password.bin -f.\$BIOSUpdateDirectoryName -r -b" -NoNewWindow -PassThru -Wait -ErrorAction Stop | |
} | |
Else | |
{ | |
$Process = Start-Process -FilePath "$($BIOSExecutable.FullName)" -WorkingDirectory $WorkingDirectory -ArgumentList "-s -f.\$BIOSUpdateDirectoryName -r -b" -NoNewWindow -PassThru -Wait -ErrorAction Stop | |
} | |
If ($Process.ExitCode -eq 3010) | |
{ | |
Write-Log -Message "The update has been staged. The BIOS will be updated on restart" -Component "BIOSFlash" | |
} | |
Else | |
{ | |
Write-Log -Message "An unexpected exit code was returned: $($Process.ExitCode)" -Component "BIOSFlash" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
} | |
catch | |
{ | |
Write-Log -Message "Failed to stage BIOS update: $($_.Exception.Message)" -Component "BIOSFlash" -LogLevel 3 | |
Upload-LogFilesToAzure | |
throw | |
} | |
Write-Log -Message "This BIOS update run is complete. Have a nice day!" -Component "Completion" | |
Upload-LogFilesToAzure |
SMSAgentSoftware
commented
Nov 24, 2022
via email
This is an excellent script. My org doesn't use Azure blob so I removed that and changed the logging. I've tested now on a few workstations with good success. Nice one!
👍
I have verified I entered the correct password. I have tested this script on about 10 laptops, all of which have the same BIOS password. It creates a 0kb password.bin file, which is a blank password - but SOMETIMES is creates it correctly which is even stranger. The problem is if it uses a blank password, the end user will be prompted for the password when the computer reboots to actually apply the update.
I have a potentially more secure workaround; the BIOS password would be in cleartext in this script which may not be everyone's cup of tea. So an alternative option is to:
- remove Line 7: $BIOSPassword = "MyPassword"
- remove the Create Password File section from the script
- manually create a password.bin file using the HpqPswd.exe (it uses AES 256 if anyone cares to know)
- store the password.bin on a suitably accessible file share
- add the following somewhere after "Create Directory Structure" and before "Check for BIOS Password"
###############################
## Copy Encrypted BIOS Password #####
###############################
Copy-Item -Path \\server\share\password.bin -Destination $WorkingDirectory\password.bin
Write-Log -Message "Copied the encrypted BIOS password file (password.bin) to local computer" -Component "Preparation"
Of course, if the BIOS password changes and/or your organisation have multiple BIOS passwords floating about out there, then you'll need to consider having multiple scripts if not find a way to update all outdated devices to the newer password (https://www.danielengberg.com/hp-bios-configuration-utility-sccm/#Update_password_file_and_configuration).