Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #Requires -Version 4
- <#
- .SYNOPSIS
- The purpose of this script is to help with the installation of the HP Firmware Updates which were distributed from
- Microsoft Update on 2/23/2018 in the event that the system's firmware is locked down with a password.
- .DESCRIPTION
- This script is designed to be run as a Computer targeted Shutdown and Startup script via Group Policy. This script
- uses the following external executables to accomplish its tasks: BiosConfigUtility.exe, BiosConfigUtility64.exe, and
- DevCon.exe.
- To run this as a Shutdown script, use the -RemovePassword parameter. This will do the following:
- - Parse the system's event log for Windows Update - Installation events and check if the "HP*Firmware*" string exists
- in any of the Installation Started events.
- - If there are any "HP*Firmware*" matches, the script then checks if there is a corresponding Installation Successful
- event.
- - If no corresponding Installation Successful events are found, the script will attempt to remove the BIOS password
- prior to the system shutting down.
- - Before trying to remove the password, the script will query the HP Firmware 'device' from the Win32_PnPEntity WMI
- class for the 'DEVPKEY_Device_ProblemStatus' property.
- - If the value of this property is anything OTHER than 0 ('The operation completed successfully') or
- 3221225560 ('The device will not start without a reboot'), the script will run DevCon.exe to 'remove' the Firmware
- and then rescan for hardware changes. This puts the Firmware into a state that will allow it to attempt installation
- again.
- - After fixing the Firmware operational status, the script will attempt to remove the current BIOS password using
- BiosConfigUtility.exe and the supplied password file.
- - Lastly, if the removal is successful, the script sets a registry key as a flag to let the Startup portion of the script
- know that the script should attempt to reinstate the password.
- To run this as a Startup script, use the -ReplacePassword parameter. The will do the following:
- - Read the registry for the registry key flag set by the Shutdown portion of the script.
- - If the flag is found, the script will attempt to set the BIOS password using the provided password file.
- - If setting the password is successful, the script will remove the registry key flag.
- The script keeps a log of its activity and has the option to copy the log to a remote log share upon exiting.
- ***** IMPORTANT NOTE ABOUT SCRIPT'S TESTING ENVIRONMENT *****
- This script was tested on UEFI Windows 10 Enterprise x64 devices with the BiosConfigUtility, BiosConfigUtility64, DevCon,
- and password.bin files all located in the same directory as the script (within the GPO's Machine\Scripts\Shutdown and Machine\Scripts\Startup directories).
- This script SHOULD work with remote, local, or relative paths supplied for all path parameters.
- The account running the script (ideally SYSTEM) will need to have appropriate access if using remote paths.
- .PARAMETER RemovePassword
- Switch parameter to be used when running the script as a Shutdown script.
- .PARAMETER ReplacePassword
- Switch parameter to be used when running the script as a Startup script.
- .PARAMETER BCUDirectoryPath
- Path to the directory containing BiosConfigUtility and BiosConfigUtility64. This parameter is not mandatory. The default
- path for BCUDirectoryPath is $PSScriptRoot.
- .PARAMETER RemovePasswordFilePath
- Path to the password file(s) to use with the BiosConfigUtility to remove the current BIOS password. This parameter is
- mandatory when using the RemovePassword switch. If multiple file paths are supplied, the script will try removing the
- BIOS password using each file sequentially.
- .PARAMETER SetPasswordFilePath
- Path to the password file to use with the BiosConfigUtility to set the BIOS password. This parameter is mandatory when
- using the ReplacePassword switch. It is expected that the BIOS will NOT have a password present when this parameter
- is used.
- .PARAMETER DevConPath
- Path to DevCon.exe. This parameter is not mandatory. The default path for DevConPath is $PSScriptRoot.
- .PARAMETER RemoteLogSharePath
- Path to the remote log share where the log is copied after script completion. This parameter is mandatory if CopyLog
- switch is used.
- .PARAMETER CopyLog
- Switch parameter to be used if you wish to copy the log to a remote log share.
- .PARAMETER LogPath
- Path to the log file. This parameter is not mandatory. The default location for the log file is
- "$env:windir\Temp\Facilitate-HPBIOSUpdate_$Action.log"
- .INPUTS
- None
- .OUTPUTS
- None
- .NOTES
- Version: 1.0
- Author: Max Bado
- Creation Date: 2/26/2018
- Purpose/Change: Initial script development
- .EXAMPLE
- 1. Run as a Shutdown script, trying to remove the BIOS password using 2 separate password files and then copying the log to
- \\server01\LogShare$. BCUDirectoryPath and DevConPath are defaulted to $PSScriptRoot and $PSScriptRoot\DevCon.exe respectively.
- .\Facilitate-HPBIOSUpdate -RemovePassword -RemovePasswordFilePath 'pass1.bin','pass2.bin' -CopyLog -RemoteLogSharePath '\\server01\LogShare$'
- 2. Run as a Startup script, trying to set the password using \\server01\BIOSPasswords$\pass1.bin. The CopyLog switch is not
- specified so logs won't be copied. BCUDirectoryPath is defaulted to $PSScriptRoot.
- .\Facilitate-HPBIOSUpdate -ReplacePassword -SetPasswordFilePath '\\server01\BIOSPasswords$\pass1.bin'
- 3. Run as a Shutdown script, trying to remove the BIOS password using pass1.bin. BCUDirectoryPath is specified as \\server01\BCUDirectory$
- so the script will try to find BiosConfigUtility.exe and BiosConfigUtility64.exe in that directory. DevConPath is specified as
- \\server02\DevCon$\DevCon.exe. No CopyLog switch so logs won't be copied.
- .\Facilitate-HPBIOSUpdate -RemovePassword -RemovePasswordFilePath 'pass1.bin' -BCUDirectoryPath '\\server01\BCUDirectory$' -DevConPath '\\server02\DevCon$\DevCon.exe'
- #>
- [CmdletBinding()]
- #region ---------------------------------------------------------[Script Parameters]------------------------------------------------------
- # https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_functions_advanced_parameters
- Param (
- [Parameter(Mandatory = $true, ParameterSetName = 'RemovePassword')]
- [switch]$RemovePassword,
- [Parameter(Mandatory = $true, ParameterSetName = 'RemovePassword')]
- [ValidateScript( {
- foreach ($item in $_) {
- if (Test-Path -Path $item -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Unable to validate $item as a valid path in RemovePasswordFilePath property."
- }
- }
- })]
- [string[]]$RemovePasswordFilePath,
- [Parameter(Mandatory = $true, ParameterSetName = 'ReplacePassword')]
- [switch]$ReplacePassword,
- [Parameter(Mandatory = $true, ParameterSetName = 'ReplacePassword')]
- [ValidateScript( {
- if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Unable to validate $_ as a valid path in SetPasswordFilePath property."
- }
- })]
- [string]$SetPasswordFilePath,
- [Parameter(Mandatory = $false)]
- [ValidateScript( {
- if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Unable to validate $_ as a valid path in BCUDirectoryPath property."
- }
- })]
- [string]$BCUDirectoryPath,
- [Parameter(Mandatory = $false, ParameterSetName = 'RemovePassword')]
- [ValidateScript( {
- if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Unable to validate $_ as a valid path in DevConPath property."
- }
- })]
- $DevConPath,
- [Parameter(Mandatory = $false)]
- [ValidateScript( {
- if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Unable to validate $_ as a valid path in RemoteLogSharePath property."
- }
- })]
- $RemoteLogSharePath,
- [Parameter(Mandatory = $false)]
- [switch]$CopyLog = $false,
- [Parameter(Mandatory = $false)]
- [ValidateScript( {
- if (Test-Path -IsValid -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Unable to validate $_ as a valid path in LogPath property."
- }
- })]
- $LogPath
- )
- #endregion
- #region ----------------------------------------------------------[Declarations]----------------------------------------------------------
- #Any Global Declarations go here
- $ScriptName = "Facilitate-HPBIOSUpdate"
- $ErrorActionPreference = 'SilentlyContinue'
- if ($RemovePassword) {
- $Action = 'RemovePassword'
- }
- if ($ReplacePassword) {
- $Action = 'ReplacePassword'
- }
- #endregion
- #region -----------------------------------------------------------[Functions]------------------------------------------------------------
- #region -------------------------------------------------------[Write-Log Function]-------------------------------------------------------
- function Write-Log {
- <#
- .Synopsis
- Write-Log writes a message to a specified log file with the current time stamp.
- .DESCRIPTION
- The Write-Log function is designed to add logging capability to other scripts.
- In addition to writing output and/or verbose you can write to a log file for
- later debugging.
- .NOTES
- Created by: Jason Wasser @wasserja
- Modified: 11/24/2015 09:30:19 AM
- Changelog:
- * Code simplification and clarification - thanks to @juneb_get_help
- * Added documentation.
- * Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks
- * Revised the Force switch to work as it should - thanks to @JeffHicks
- To Do:
- * Add error handling if trying to create a log file in a inaccessible location.
- * Add ability to write $Message to $Verbose or $Error pipelines to eliminate
- duplicates.
- .PARAMETER Message
- Message is the content that you wish to add to the log file.
- .PARAMETER Path
- The path to the log file to which you would like to write. By default the function will
- create the path and file if it does not exist.
- .PARAMETER Level
- Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
- .PARAMETER NoClobber
- Use NoClobber if you do not wish to overwrite an existing file.
- .EXAMPLE
- Write-Log -Message 'Log message'
- Writes the message to c:\Logs\PowerShellLog.log.
- .EXAMPLE
- Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
- Writes the content to the specified log file and creates the path and file specified.
- .EXAMPLE
- Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
- Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
- .LINK
- https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
- #>
- [CmdletBinding()]
- Param
- (
- [Parameter(Mandatory = $true,
- ValueFromPipelineByPropertyName = $true)]
- [ValidateNotNullOrEmpty()]
- [Alias("LogContent")]
- [string]$Message,
- [Parameter(Mandatory = $false)]
- [Alias('LogPath')]
- [string]$Path = 'C:\Logs\PowerShellLog.log',
- [Parameter(Mandatory = $false)]
- [ValidateSet("Error", "Warn", "Info")]
- [string]$Level = "Info",
- [Parameter(Mandatory = $false)]
- [switch]$NoClobber
- )
- Begin {
- # Set VerbosePreference to Continue so that verbose messages are displayed.
- $VerbosePreference = 'Continue'
- }
- Process {
- # If the file already exists and NoClobber was specified, do not write to the log.
- if ((Test-Path $Path) -AND $NoClobber) {
- Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
- Return
- }
- # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
- elseif (!(Test-Path $Path)) {
- Write-Verbose "Creating $Path."
- $NewLogFile = New-Item $Path -Force -ItemType File
- }
- else {
- # Nothing to see here yet.
- }
- # Format Date for our Log File
- $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
- # Write message to error, warning, or verbose pipeline and specify $LevelText
- switch ($Level) {
- 'Error' {
- Write-Error $Message
- $LevelText = 'ERROR:'
- }
- 'Warn' {
- Write-Warning $Message
- $LevelText = 'WARNING:'
- }
- 'Info' {
- Write-Verbose $Message
- $LevelText = 'INFO:'
- }
- }
- # Write log entry to $Path
- "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
- }
- End {
- }
- }
- #endregion
- #region -------------------------------------------------------[Get-WindowsUpdateInstallEvents Function]-------------------------------------------------------
- function Get-WindowsUpdateInstallEvents {
- [CmdletBinding()]
- Param
- (
- [Parameter(Mandatory = $false)][datetime]$StartDate = [datetime]'01/01/1980',
- [Parameter(Mandatory = $false)][datetime]$StopDate = [datetime]::Now
- )
- $InstallStartedID = 43
- $InstallFailedID = 20
- $InstallSuccessID = 19
- $DateDifference = $StopDate - $StartDate
- $WUStartedEvents = Get-WinEvent -ProviderName 'Microsoft-Windows-WindowsUpdateClient' -FilterXPath "*[System[(EventID=$InstallStartedID) and TimeCreated[timediff(@SystemTime) <= $($DateDifference.TotalMilliseconds)]]]"
- $WUFailedEvents = Get-WinEvent -ProviderName 'Microsoft-Windows-WindowsUpdateClient' -FilterXPath "*[System[(EventID=$InstallFailedID) and TimeCreated[timediff(@SystemTime) <= $($DateDifference.TotalMilliseconds)]]]"
- $WUSuccessEvents = Get-WinEvent -ProviderName 'Microsoft-Windows-WindowsUpdateClient' -FilterXPath "*[System[(EventID=$InstallSuccessID) and TimeCreated[timediff(@SystemTime) <= $($DateDifference.TotalMilliseconds)]]]"
- $WUEventsObject = [pscustomobject]@{
- InstallationStartedEvents = $WUStartedEvents
- InstallationFailedEvents = $WUFailedEvents
- InstallationSuccessEvents = $WUSuccessEvents
- }
- return $WUEventsObject
- }
- #endregion
- #region -------------------------------------------------------[Remove-BIOSPassword Function]-------------------------------------------------------
- function Remove-HPBIOSPassword {
- [CmdletBinding()]
- Param
- (
- [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Please provide a valid Password File path."
- }})][string]$PasswordFilePath,
- [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Please provide a valid BiosConfigUtility path."
- }})][string]$BCUPath
- )
- $PasswordFile = (Resolve-Path -Path $PasswordFilePath).ProviderPath
- $BCU = (Resolve-Path -Path $BCUPath).ProviderPath
- # Try to remove BIOS password using the BIOS password file
- $Arg01 = "/cspwdfile:`"$PasswordFile`""
- $Arg02 = '/nspwdfile:""'
- Write-Log -Path $LogPath -Level Info -Message "Trying to remove the BIOS password using '$PasswordFile'."
- Write-Log -Path $LogPath -Level Info -Message "Executing: $BCU $Arg01 $Arg02"
- $BCUResult = & $BCU $Arg01 $Arg02
- return $BCUResult
- }
- #endregion
- #region -------------------------------------------------------[Set-BIOSPassword Function]-------------------------------------------------------
- function Set-HPBIOSPassword {
- [CmdletBinding()]
- Param
- (
- [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Please provide a valid New Password File path."
- }})][string]$NewPasswordFilePath,
- [Parameter(Mandatory = $false)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Please provide a valid Current Password File path."
- }})][string]$CurrentPasswordFilePath,
- [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
- $true
- } else {
- Throw "Please provide a valid BiosConfigUtility path."
- }})][string]$BCUPath
- )
- $NewPasswordFile = (Resolve-Path -Path $NewPasswordFilePath).ProviderPath
- $BCU = (Resolve-Path -Path $BCUPath).ProviderPath
- if ($CurrentPasswordFilePath) {
- # Try to set a new BIOS password using a provided Current Password file.
- $CurrentPasswordFile = (Resolve-Path -Path $CurrentPasswordFilePath).ProviderPath
- $Arg01 = "/nspwdfile:`"$NewPasswordFile`""
- $Arg02 = "/cspwdfile:`"$CurrentPasswordFile`""
- Write-Log -Path $LogPath -Level Info -Message "Trying to set the BIOS password to the BIOS password contained in '$NewPasswordFile'. Provided password file is '$CurrentPasswordFile'."
- Write-Log -Path $LogPath -Level Info -Message "Executing: $BCU $Arg01 $Arg02"
- $BCUResult = & $BCU $Arg01 $Arg02
- } else {
- # Try to set a new BIOS password without a provided Current Password file. This should work if there is no BIOS password currently set.
- $Arg01 = "/nspwdfile:`"$NewPasswordFile`""
- Write-Log -Path $LogPath -Level Info -Message "Trying to set the BIOS password to the BIOS password contained in '$NewPasswordFile'. No password file provided."
- Write-Log -Path $LogPath -Level Info -Message "Executing: $BCU $Arg01"
- $BCUResult = & $BCU $Arg01
- }
- return $BCUResult
- }
- #endregion
- #endregion
- #region -----------------------------------------------------------[Execution]------------------------------------------------------------
- ## VARIABLE DECLARATION
- # Set path to the log.
- if (!$LogPath) {
- $LogPath = "$env:windir\Temp\$($ScriptName)_$Action.log"
- } else {
- $LogPath = (Resolve-Path -Path $LogPath).ProviderPath
- }
- Write-Log -Path $LogPath -Level Warn -Message "-----------------------------------------------------------START $ScriptName------------------------------------------------------------"
- # Call the appropriate utility binary per the current environment.
- if ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64') {
- $BiosConfigUtility = 'BiosConfigUtility64.exe'
- } else {
- $BiosConfigUtility = 'BiosConfigUtility.exe'
- }
- # Set path to the BiosConfigUtility
- if (!$BCUDirectoryPath) {
- # Default is $PSScriptRoot
- $BCUPath = "$PSScriptRoot\$BiosConfigUtility"
- } else {
- $BCUPath = "$((Resolve-Path -Path $BCUDirectoryPath).ProviderPath)\$BiosConfigUtility"
- }
- # Set path to DevCon
- if (!$DevConPath) {
- # Default is $PSScriptRoot
- $DevConPath = "$PSScriptRoot\DevCon.exe"
- } else {
- $DevConPath = (Resolve-Path -Path $DevConPath).ProviderPath
- }
- # Set path to the password file used with the ReplacePassword switch
- if ($SetPasswordFilePath) {
- $PasswordFile = (Resolve-Path -Path $SetPasswordFilePath).ProviderPath
- }
- # Set path to the password file(s) used with the RemovePassword switch
- if ($RemovePasswordFilePath) {
- [array]$PasswordFiles = @()
- $PasswordFiles += foreach ($item in $RemovePasswordFilePath) {
- $PasswordFile = Resolve-Path -Path $item
- $PasswordFile.ProviderPath
- }
- }
- # Set path to the Remote Log Share. If this is not provided in initial script parameters, log will not be copied.
- if ($CopyLog) {
- if (!$RemoteLogSharePath) {
- Write-Log -Path $LogPath -Level Warn -Message "RemoteLogSharePath parameter is NULL. Script will not be copying logs."
- $CopyLog = $false
- } else {
- $RemoteLogSharePath = (Resolve-Path -Path $RemoteLogSharePath -ErrorAction Stop).ProviderPath
- }
- }
- # This class does not exist on all HP models
- $CurrentBIOS = Get-CimInstance -ClassName Win32_BIOS
- $CurrentBIOSVersion = ($CurrentBIOS.Name -split 'Ver.')[1].Trim()
- $RemovePasswordFlag = $false
- $PasswordError = $false
- $PasswordDoublePlusError = $false
- $BCUResult = $null
- $State = $null
- $WUEvents = $null
- $HPFirmwareStartedEvents = $null
- $HPFirmware = $null
- $HPFirmwareProperties = $null
- $HPFirmwareProblemStatus = $null
- Write-Log -Path $LogPath -Level Info -Message "
- Script Start Time: $(Get-Date)
- Log Path: $LogPath
- Processor Architecture: $env:PROCESSOR_ARCHITECTURE
- BiosConfigUtility Path: $BCUPath
- DevCon Path: $DevConPath
- $(if($SetPasswordFilePath){"Password File Path: $PasswordFile"})
- Password Files: $(if($RemovePasswordFilePath){$PasswordFiles | ForEach-Object {"$($_);"}})
- Current BIOS Version: $CurrentBIOSVersion
- Remote Log Share: $RemoteLogSharePath"
- ## MAIN LOGIC
- Try {
- Write-Log -Path $LogPath -Level Info -Message "Script was invoked with Action: $Action."
- Write-Log -Path $LogPath -Level Warn -Message "-----------------------------------------------------------$Action------------------------------------------------------------"
- if ($Action -eq 'RemovePassword') {
- # Remove the registry key flag it's remaining from a previous script run.
- Write-Log -Path $LogPath -Level Info -Message "Removing 'HKLM:\SOFTWARE\HP BIOS Update Script' flag registry key if it exists."
- $null = Remove-Item -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Force -Recurse
- # Get Windows Update install events to see if the HP Firmware installation had been attempted
- Write-Log -Path $LogPath -Level Info -Message "Getting Windows Update install events to determine if the HP Firmware installation had been attempted."
- $WUEvents = Get-WindowsUpdateInstallEvents -StartDate '02/20/2018'
- # Parse the Installation Started events for the HP Firmware entry
- Write-Log -Path $LogPath -Level Info -Message "Parsing the Installation Started events for the HP Firmware entry."
- $HPFirmwareStartedEvents = $WUEvents.InstallationStartedEvents | ForEach-Object {
- $event = $null
- $eventXML = $null
- $eventObj = $null
- $event = $_
- $eventXML = [xml]$event.toXML()
- if (($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text' -like '*HP*Firmware*') {
- Write-Log -Path $LogPath -Level Info -Message "Found event with EventID $($eventXML.Event.System.EventID) and UpdateTitle '$(($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text')'."
- $eventObj = [pscustomobject]@{
- EventID = $eventXML.Event.System.EventID
- Message = $event.Message
- OPCodeDisplayName = $event.OpcodeDisplayName
- TimeCreated = Get-Date -Date $eventXML.Event.System.TimeCreated.SystemTime
- UpdateTitle = ($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text'
- UpdateGUID = ($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateGuid'}).'#text'
- UpdateRevisionNumber = ($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateRevisionNumber'}).'#text'
- UpdateVersion = ((($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text') -split '-')[-1].Trim() # This likely only works for this batch of Firmware updates
- }
- $eventObj
- }
- }
- # Check if these installations completed successfully
- if ($HPFirmwareStartedEvents) {
- Write-Log -Path $LogPath -Level Info -Message "Checking if these HP Firmware Installation Started events have a corresponding Installation Successful event."
- foreach ($HPFirmwareStartedEvent in $HPFirmwareStartedEvents) {
- if ($HPFirmwareStartedEvent.UpdateGUID -in (($WUEvents.InstallationSuccessEvents | ForEach-Object {$event = $_; $eventXML = [xml]$event.ToXml(); $eventXML}).Event.EventData.Data | Where-Object {$_.Name -eq 'updateGuid'}).'#text') {
- # This Firmware update installed successfully
- Write-Log -Path $LogPath -Level Info -Message "HP Firmware Update '$($HPFirmwareStartedEvent.UpdateTitle)' was successfully installed."
- } else {
- # This Firmware update DOES NOT have a corresponding Installation Success event.
- # It will attempt to install upon reboot, so we need to remove the BIOS password in preparation.
- Write-Log -Path $LogPath -Level Warn -Message "HP Firmware Update '$($HPFirmwareStartedEvent.UpdateTitle)' does not have a corresponding Installation Successful event."
- Write-Log -Path $LogPath -Level Warn -Message "Setting RemovePasswordFlag to True."
- $RemovePasswordFlag = $true
- break
- }
- }
- } else {
- # No Installation Started events for HP Firmware.
- Write-Log -Path $LogPath -Level Info -Message "No Installation Started events for HP Firmware."
- }
- if (!$RemovePasswordFlag) {
- # RemovePasswordFlag set to False. Exiting.
- Write-Log -Path $LogPath -Level Info -Message "RemovePasswordFlag is set to False. Exiting script without removing the BIOS password."
- $State = 'NA'
- } else {
- # The BIOS password needs to be removed for the firmware update to install correctly...
- # We need to check the state of the Firmware 'device' and correct it if it's in some error state besides 'Success' or 'Reboot Required'.
- # Otherwise the Firmware won't try to update upon reboot.
- Write-Log -Path $LogPath -Level Info -Message "BIOS password must be removed in order for the HP Firmware Update to complete successfully..."
- Write-Log -Path $LogPath -Level Info -Message "Starting check to see if the HP Firmware 'device' is in an error state..."
- # Get the Firmware 'device' object
- $HPFirmware = Get-CimInstance -ClassName Win32_PnPEntity -Filter "PnPDeviceID like 'UEFI%' AND PNPClass='Firmware' AND (Manufacturer like 'HP%' OR Manufacturer like 'Hewlett%')"
- # Get the Firmware 'device' object's properties
- $HPFirmwareProperties = (Invoke-CimMethod -InputObject $HPFirmware -MethodName GetDeviceProperties).deviceProperties
- # Try to grab the 'DEVPKEY_Device_ProblemStatus' key from the properties
- $HPFirmwareProblemStatus = $HPFirmwareProperties | Where-Object {$_.KeyName -eq 'DEVPKEY_Device_ProblemStatus'}
- # We are interested in any problem status that ISN'T 0 or 3221225560 which is the base 10 representation of 0xC00002D2 which translates to:
- # 'The device will not start without a reboot.' (NTSTATUS 0xC00002D2: STATUS_PNP_REBOOT_REQUIRED)
- # Reference: https://msdn.microsoft.com/en-us/library/cc704588.aspx
- # Reference: https://support.microsoft.com/en-us/help/310123/error-codes-in-device-manager-in-windows
- if ($HPFirmwareProblemStatus) {
- if ($HPFirmwareProblemStatus.Data -eq '0') {
- # STATUS_SUCCESS: The operation completed successfully.
- Write-Log -Path $LogPath -Level Info -Message "HP Firmware has a Problem Status code of $($HPFirmwareProblemStatus.Data) which translates to 'The operation completed successfully.' No need to use DevCon for anything."
- } elseif ($HPFirmwareProblemStatus.Data -eq '3221225560') {
- # STATUS_PNP_REBOOT_REQUIRED: The device will not start without a reboot.
- Write-Log -Path $LogPath -Level Info -Message "HP Firmware has a Problem Status code of $($HPFirmwareProblemStatus.Data) which translates to 'The device will not start without a reboot.' No need to use DevCon for anything."
- } else {
- # The Firmware is in an error state that isn't 0 or 3221225560. Need to 'remove' it and then rescan for hardware changes.
- # This will allow the Firmware to attempt the update again.
- Write-Log -Path $LogPath -Level Warn -Message "HP Firmware has a Problem Status code of $($HPFirmwareProblemStatus.Data). Using DevCon.exe to 'remove' it and then rescanning for hardware changes. This will allow the Firmware to attempt to update itself again."
- & $DevConPath '/remove' "@$($HPFirmware.DeviceID)"
- & $DevConPath '/rescan'
- }
- } else {
- Write-Log -Path $LogPath -Level Warn -Message "HP Firmware does not have a Problem Status code."
- }
- # Iterate through each supplied BIOS password file to try with the current Firmware
- for ($i = 0; $i -lt $PasswordFiles.Count; $i++) {
- $PasswordFile = $null
- $PasswordFile = $PasswordFiles[$i]
- # Remove the BIOS password
- $BCUResult = Remove-HPBIOSPassword -PasswordFilePath $PasswordFile -BCUPath $BCUPath
- # Parse BiosConfigUtility output.
- Write-Log -Path $LogPath -Level Info -Message "Parsing BiosConfigUtility output..."
- foreach ($line in $BCUResult) {
- if ($line -like '*<SUCCESS msg*') {
- $split = $null
- $text = $null
- $split = $line -split 'msg='
- $text = $split[1].Remove(($split[1].Length - 3), 3)
- Write-Log -Path $LogPath -Level Info -Message "BCU returned SUCCESS: $text"
- }
- if ($line -like '*<ERROR msg*') {
- $split = $null
- $text = $null
- $split = $line -split 'msg='
- $text = $split[1].Remove(($split[1].Length - 3), 3)
- Write-Log -Path $LogPath -Level Warn -Message "BCU returned ERROR: $text"
- # Set PasswordError variable to True so that we can try again with a different password
- Write-Log -Path $LogPath -Level Warn -Message "BiosConfigUtility returned an error running the command with the BIOS password contained in '$PasswordFile'. Setting PasswordError flag."
- $PasswordError = $true
- }
- } #END Parse output
- if (!$PasswordError) {
- # No PasswordError which means the password was removed successfully. Stop trying passwords by breaking out of the loop.
- Write-Log -Path $LogPath -Level Info -Message "Successfully changed the BIOS password. No need to try any more passwords."
- break
- } else {
- # Changing BIOS password failed.
- if ($i -eq ($PasswordFiles.Count - 1)) {
- # If this was the last password in the list, system may need manual attention.
- Write-Log -Path $LogPath -Level Warn -Message "Failed to changed the BIOS password. This was the final password file in the list. This system may require manual troubleshooting to remove the BIOS password..."
- $PasswordDoublePlusError = $true
- } else {
- # Move on to next password file in the list.
- Write-Log -Path $LogPath -Level Warn -Message "Failed to changed the BIOS password. Trying next password file in the list."
- $PasswordError = $false
- }
- }
- }
- # If none of the passwords worked, set State to 'SUPERERROR' so that the log that's copied to the Remote Log Share is very visible.
- if ($PasswordDoublePlusError) {
- $PasswordDoublePlusError = $false
- $State = 'SUPERERROR'
- } else {
- # BIOS Password has been successfully removed.
- Write-Log -Path $LogPath -Level Info -Message "BIOS Password has been successfully removed."
- # Set registry key to let the script know to set the password upon next boot.
- Write-Log -Path $LogPath -Level Info -Message "Creating 'HKLM:\SOFTWARE\HP BIOS Update Script' flag registry key with property 'Set BIOS Flag' so that the script knows to set the BIOS password upon next boot."
- $null = New-Item -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Force
- $null = New-ItemProperty -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Name 'Set BIOS Flag'
- Write-Log -Path $LogPath -Level Info -Message "Ending script."
- $State = 'SUCCESS'
- }
- }
- } #END RemovePassword
- if ($Action -eq 'ReplacePassword') {
- # Check if this section should run by looking for the 'HKLM:\SOFTWARE\HP BIOS Update Script' registry key
- if (!(Get-ItemProperty -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Name 'Set BIOS Flag' -ErrorAction SilentlyContinue)) {
- # The reg key doesn't exist, end the script.
- Write-Log -Path $LogPath -Level Warn -Message "'HKLM:\SOFTWARE\HP BIOS Update Script | Set BIOS Flag' does not exist. Ending script."
- $State = 'NA'
- } else {
- # Try to replace password
- $BCUResult = Set-HPBIOSPassword -NewPasswordFilePath $PasswordFile -BCUPath $BiosConfigUtility
- # Parse BiosConfigUtility output.
- Write-Log -Path $LogPath -Level Info -Message "Parsing BiosConfigUtility output..."
- foreach ($line in $BCUResult) {
- if ($line -like '*<SUCCESS msg*') {
- $split = $null
- $text = $null
- $split = $line -split 'msg='
- $text = $split[1].Remove(($split[1].Length - 3), 3)
- Write-Log -Path $LogPath -Level Info -Message "BCU returned SUCCESS: $text"
- }
- if ($line -like '*<ERROR msg*') {
- $split = $null
- $text = $null
- $split = $line -split 'msg='
- $text = $split[1].Remove(($split[1].Length - 3), 3)
- Write-Log -Path $LogPath -Level Warn -Message "BCU returned ERROR: $text"
- # Neither password worked. That's not good...
- Write-Log -Path $LogPath -Level Warn -Message "Setting the BIOS password with no current password file provided did not work. This system may require manual troubleshooting."
- $PasswordDoublePlusError = $true
- }
- } #END FOREACH Parse output
- # If setting password without a provided password file did not work, set State to 'SUPERERROR' so that the log that's copied to the Remote Log Share is very visible.
- if ($PasswordDoublePlusError) {
- $PasswordDoublePlusError = $false
- $State = 'SUPERERROR'
- } else {
- # BIOS Password has been successfully set.
- Write-Log -Path $LogPath -Level Info -Message "BIOS Password has been successfully set."
- # Remove the registry key flag
- Write-Log -Path $LogPath -Level Info -Message "Removing 'HKLM:\SOFTWARE\HP BIOS Update Script' flag registry key."
- $null = Remove-Item -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Force -Recurse
- Write-Log -Path $LogPath -Level Info -Message "Ending script."
- $State = 'SUCCESS'
- }
- }
- } #END ReplacePassword
- } Catch {
- Write-Log -Path $LogPath -Level Error -Message "Error: $($_.Exception)"
- $State = 'EXCEPTION'
- # Copy logs to the log share if CopyLog parameter is set
- if ($CopyLog) {
- $null = Copy-Item -Path $LogPath -Destination "$RemoteLogSharePath\$($env:COMPUTERNAME)_$($Action)_$State.log" -Force
- }
- return
- }
- #clean up any variables, closing connection to databases, or exporting data
- If ($?) {
- Write-Log -Path $LogPath -Level Info -Message 'Completed Successfully.'
- # Copy logs to the log share if CopyLog parameter is set
- if ($CopyLog) {
- if ($State -eq 'NA') {
- # Don't copy logs if state is NA
- } else {
- $null = Copy-Item -Path $LogPath -Destination "$RemoteLogSharePath\$($env:COMPUTERNAME)_$($Action)_$State.log" -Force
- }
- }
- }
- #endregion
Add Comment
Please, Sign In to add comment