##################### | |
## 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 |
Ok, i find the problem. You must modify this line
{
$Process = Start-Process -FilePath "$WorkingDirectory$BIOSUpdateDirectoryName$BIOSExecutable" -WorkingDirectory $WorkingDirectory -ArgumentList "-s -p.\password.bin -f.$BIOSUpdateDirectoryName -r -b" -NoNewWindow -PassThru -Wait -ErrorAction Stop
}
With
{
$Process = Start-Process -FilePath "$BIOSExecutable" -WorkingDirectory $WorkingDirectory -ArgumentList "-s -p.\password.bin -f.\$BIOSUpdateDirectoryName -r -b" -NoNewWindow -PassThru -Wait -ErrorAction Stop
}
And it works!
Thanks for the find @dimitrov6! I have verified it and updated the code in the gist. I think originally I was extracting just the filename in the $BIOSExecutable variable - don't know how I missed that off.
@christophergithub2021 @JrothSKV
This is absolutely brilliant. In initial testing this has worked a treat with one exception; the password.bin file is being created in the correct folder but looks to be 0kb in size. I amended the Line 7 $BIOSPassword = "MyPassword" to the actual BIOS password.
I can't see any errors in the logs:
<![LOG[The BIOS has a password set]LOG]!><time="09:42:25.773+000" date="11-24-2022" component="BIOSPassword" context="" type="1" thread="" file="">
<![LOG[Creating an encrypted password file]LOG]!><time="09:42:25.778+000" date="11-24-2022" component="BIOSPassword" context="" type="1" thread="" file="">
<![LOG[File successfully created]LOG]!><time="09:42:31.877+000" date="11-24-2022" component="BIOSPassword" context="" type="1" thread="" file="">
<![LOG[Staging BIOS firmware update]LOG]!><time="09:42:31.877+000" date="11-24-2022" component="BIOSFlash" context="" type="1" thread="" file="">
Upon reboot, the device then prompts for a BIOS password to complete the update - what the heck am I doing wrong? :(
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!
Yes I have verified I am entered the correct password, thanks. I have tested script on about 10 laptops, all of which have the same BIOS password. It is creating a 0kb password.bin file, which is a blank password.
My work around at present is to manually pop in a copy of a good password.bin file to each computer and script changed to look for the file locally instead of first creating it. This is fine, but wouldn't mind it being created automatically where possible.
Hello,
I have the same problem than JrothSKV.
The problem seems to come from the directory of the line
{
$Process = Start-Process -FilePath "$WorkingDirectory$BIOSUpdateDirectoryName$BIOSExecutable" -WorkingDirectory $WorkingDirectory -ArgumentList "-s -p.\password.bin -f.$BIOSUpdateDirectoryName -r -b" -NoNewWindow -PassThru -Wait -ErrorAction Stop
}
If i start a cmd and lanch manually the bios exe with the password.bin with manual links pointing where the program and files swhere downloaded byt the script : it works///
So how could we solve the problem?