Guest User

Untitled

a guest
Feb 28th, 2018
534
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #Requires -Version 4
  2. <#
  3. .SYNOPSIS
  4.   The purpose of this script is to help with the installation of the HP Firmware Updates which were distributed from
  5.   Microsoft Update on 2/23/2018 in the event that the system's firmware is locked down with a password.
  6. .DESCRIPTION
  7.   This script is designed to be run as a Computer targeted Shutdown and Startup script via Group Policy. This script
  8.   uses the following external executables to accomplish its tasks: BiosConfigUtility.exe, BiosConfigUtility64.exe, and
  9.   DevCon.exe.
  10.   To run this as a Shutdown script, use the -RemovePassword parameter. This will do the following:
  11.    - Parse the system's event log for Windows Update - Installation events and check if the "HP*Firmware*" string exists
  12.      in any of the Installation Started events.
  13.    - If there are any "HP*Firmware*" matches, the script then checks if there is a corresponding Installation Successful
  14.      event.
  15.    - If no corresponding Installation Successful events are found, the script will attempt to remove the BIOS password
  16.      prior to the system shutting down.
  17.    - Before trying to remove the password, the script will query the HP Firmware 'device' from the Win32_PnPEntity WMI
  18.      class for the 'DEVPKEY_Device_ProblemStatus' property.
  19.    - If the value of this property is anything OTHER than 0 ('The operation completed successfully') or
  20.      3221225560 ('The device will not start without a reboot'), the script will run DevCon.exe to 'remove' the Firmware
  21.      and then rescan for hardware changes. This puts the Firmware into a state that will allow it to attempt installation
  22.      again.
  23.    - After fixing the Firmware operational status, the script will attempt to remove the current BIOS password using
  24.      BiosConfigUtility.exe and the supplied password file.
  25.    - Lastly, if the removal is successful, the script sets a registry key as a flag to let the Startup portion of the script
  26.      know that the script should attempt to reinstate the password.
  27.  
  28.   To run this as a Startup script, use the -ReplacePassword parameter. The will do the following:
  29.    - Read the registry for the registry key flag set by the Shutdown portion of the script.
  30.    - If the flag is found, the script will attempt to set the BIOS password using  the provided password file.
  31.    - If setting the password is successful, the script will remove the registry key flag.
  32.  
  33.   The script keeps a log of its activity and has the option to copy the log to a remote log share upon exiting.
  34.  
  35.   ***** IMPORTANT NOTE ABOUT SCRIPT'S TESTING ENVIRONMENT *****
  36.   This script was tested on UEFI Windows 10 Enterprise x64 devices with the BiosConfigUtility, BiosConfigUtility64, DevCon,
  37.   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).
  38.   This script SHOULD work with remote, local, or relative paths supplied for all path parameters.
  39.   The account running the script (ideally SYSTEM) will need to have appropriate access if using remote paths.
  40. .PARAMETER RemovePassword
  41.   Switch parameter to be used when running the script as a Shutdown script.
  42. .PARAMETER ReplacePassword
  43.   Switch parameter to be used when running the script as a Startup script.
  44. .PARAMETER BCUDirectoryPath
  45.   Path to the directory containing BiosConfigUtility and BiosConfigUtility64. This parameter is not mandatory. The default
  46.   path for BCUDirectoryPath is $PSScriptRoot.
  47. .PARAMETER RemovePasswordFilePath
  48.   Path to the password file(s) to use with the BiosConfigUtility to remove the current BIOS password. This parameter is
  49.   mandatory when using the RemovePassword switch. If multiple file paths are supplied, the script will try removing the
  50.   BIOS password using each file sequentially.
  51. .PARAMETER SetPasswordFilePath
  52.   Path to the password file to use with the BiosConfigUtility to set the BIOS password. This parameter is mandatory when
  53.   using the ReplacePassword switch. It is expected that the BIOS will NOT have a password present when this parameter
  54.   is used.
  55. .PARAMETER DevConPath
  56.   Path to DevCon.exe. This parameter is not mandatory. The default path for DevConPath is $PSScriptRoot.
  57. .PARAMETER RemoteLogSharePath
  58.   Path to the remote log share where the log is copied after script completion. This parameter is mandatory if CopyLog
  59.   switch is used.
  60. .PARAMETER CopyLog
  61.   Switch parameter to be used if you wish to copy the log to a remote log share.
  62. .PARAMETER LogPath
  63.   Path to the log file. This parameter is not mandatory. The default location for the log file is
  64.   "$env:windir\Temp\Facilitate-HPBIOSUpdate_$Action.log"
  65. .INPUTS
  66.   None
  67. .OUTPUTS
  68.   None
  69. .NOTES
  70.   Version:        1.0
  71.   Author:         Max Bado
  72.   Creation Date:  2/26/2018
  73.   Purpose/Change: Initial script development
  74. .EXAMPLE  
  75.   1. Run as a Shutdown script, trying to remove the BIOS password using 2 separate password files and then copying the log to
  76.      \\server01\LogShare$. BCUDirectoryPath and DevConPath are defaulted to $PSScriptRoot and $PSScriptRoot\DevCon.exe respectively.
  77.      .\Facilitate-HPBIOSUpdate -RemovePassword -RemovePasswordFilePath 'pass1.bin','pass2.bin' -CopyLog -RemoteLogSharePath '\\server01\LogShare$'
  78.  
  79.   2. Run as a Startup script, trying to set the password using \\server01\BIOSPasswords$\pass1.bin. The CopyLog switch is not
  80.      specified so logs won't be copied. BCUDirectoryPath is defaulted to $PSScriptRoot.
  81.      .\Facilitate-HPBIOSUpdate -ReplacePassword -SetPasswordFilePath '\\server01\BIOSPasswords$\pass1.bin'
  82.  
  83.   3. Run as a Shutdown script, trying to remove the BIOS password using pass1.bin. BCUDirectoryPath is specified as \\server01\BCUDirectory$
  84.      so the script will try to find BiosConfigUtility.exe and BiosConfigUtility64.exe in that directory. DevConPath is specified as
  85.      \\server02\DevCon$\DevCon.exe. No CopyLog switch so logs won't be copied.
  86.      .\Facilitate-HPBIOSUpdate -RemovePassword -RemovePasswordFilePath 'pass1.bin' -BCUDirectoryPath '\\server01\BCUDirectory$' -DevConPath '\\server02\DevCon$\DevCon.exe'
  87. #>
  88.  
  89. [CmdletBinding()]
  90. #region ---------------------------------------------------------[Script Parameters]------------------------------------------------------
  91. # https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_functions_advanced_parameters
  92. Param (
  93.     [Parameter(Mandatory = $true, ParameterSetName = 'RemovePassword')]
  94.     [switch]$RemovePassword,
  95.     [Parameter(Mandatory = $true, ParameterSetName = 'RemovePassword')]
  96.     [ValidateScript( {
  97.             foreach ($item in $_) {
  98.                 if (Test-Path -Path $item -ErrorAction SilentlyContinue) {
  99.                     $true
  100.                 } else {
  101.                     Throw "Unable to validate $item as a valid path in RemovePasswordFilePath property."
  102.                 }
  103.             }
  104.         })]
  105.     [string[]]$RemovePasswordFilePath,
  106.     [Parameter(Mandatory = $true, ParameterSetName = 'ReplacePassword')]
  107.     [switch]$ReplacePassword,
  108.     [Parameter(Mandatory = $true, ParameterSetName = 'ReplacePassword')]
  109.     [ValidateScript( {
  110.             if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  111.                 $true
  112.             } else {
  113.                 Throw "Unable to validate $_ as a valid path in SetPasswordFilePath property."
  114.             }        
  115.         })]
  116.     [string]$SetPasswordFilePath,
  117.     [Parameter(Mandatory = $false)]
  118.     [ValidateScript( {
  119.             if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  120.                 $true
  121.             } else {
  122.                 Throw "Unable to validate $_ as a valid path in BCUDirectoryPath property."
  123.             }
  124.         })]
  125.     [string]$BCUDirectoryPath,
  126.     [Parameter(Mandatory = $false, ParameterSetName = 'RemovePassword')]
  127.     [ValidateScript( {
  128.             if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  129.                 $true
  130.             } else {
  131.                 Throw "Unable to validate $_ as a valid path in DevConPath property."
  132.             }
  133.         })]
  134.     $DevConPath,
  135.     [Parameter(Mandatory = $false)]
  136.     [ValidateScript( {
  137.             if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  138.                 $true
  139.             } else {
  140.                 Throw "Unable to validate $_ as a valid path in RemoteLogSharePath property."
  141.             }
  142.         })]
  143.     $RemoteLogSharePath,
  144.     [Parameter(Mandatory = $false)]
  145.     [switch]$CopyLog = $false,
  146.     [Parameter(Mandatory = $false)]
  147.     [ValidateScript( {
  148.             if (Test-Path -IsValid -Path $_ -ErrorAction SilentlyContinue) {
  149.                 $true
  150.             } else {
  151.                 Throw "Unable to validate $_ as a valid path in LogPath property."
  152.             }
  153.         })]
  154.     $LogPath
  155. )
  156. #endregion
  157.  
  158. #region ----------------------------------------------------------[Declarations]----------------------------------------------------------
  159.  
  160. #Any Global Declarations go here
  161. $ScriptName = "Facilitate-HPBIOSUpdate"
  162. $ErrorActionPreference = 'SilentlyContinue'
  163.  
  164. if ($RemovePassword) {
  165.     $Action = 'RemovePassword'
  166. }
  167. if ($ReplacePassword) {
  168.     $Action = 'ReplacePassword'
  169. }
  170. #endregion
  171.  
  172. #region -----------------------------------------------------------[Functions]------------------------------------------------------------
  173. #region -------------------------------------------------------[Write-Log Function]-------------------------------------------------------
  174. function Write-Log {
  175.    
  176.     <#
  177.     .Synopsis
  178.        Write-Log writes a message to a specified log file with the current time stamp.
  179.     .DESCRIPTION
  180.        The Write-Log function is designed to add logging capability to other scripts.
  181.        In addition to writing output and/or verbose you can write to a log file for
  182.        later debugging.
  183.     .NOTES
  184.        Created by: Jason Wasser @wasserja
  185.        Modified: 11/24/2015 09:30:19 AM  
  186.    
  187.        Changelog:
  188.         * Code simplification and clarification - thanks to @juneb_get_help
  189.         * Added documentation.
  190.         * Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks
  191.         * Revised the Force switch to work as it should - thanks to @JeffHicks
  192.    
  193.        To Do:
  194.         * Add error handling if trying to create a log file in a inaccessible location.
  195.         * Add ability to write $Message to $Verbose or $Error pipelines to eliminate
  196.           duplicates.
  197.     .PARAMETER Message
  198.        Message is the content that you wish to add to the log file.
  199.     .PARAMETER Path
  200.        The path to the log file to which you would like to write. By default the function will
  201.        create the path and file if it does not exist.
  202.     .PARAMETER Level
  203.        Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
  204.     .PARAMETER NoClobber
  205.        Use NoClobber if you do not wish to overwrite an existing file.
  206.     .EXAMPLE
  207.        Write-Log -Message 'Log message'
  208.        Writes the message to c:\Logs\PowerShellLog.log.
  209.     .EXAMPLE
  210.        Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
  211.        Writes the content to the specified log file and creates the path and file specified.
  212.     .EXAMPLE
  213.        Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
  214.        Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
  215.     .LINK
  216.        https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
  217.     #>
  218.    
  219.     [CmdletBinding()]
  220.     Param
  221.     (
  222.         [Parameter(Mandatory = $true,
  223.             ValueFromPipelineByPropertyName = $true)]
  224.         [ValidateNotNullOrEmpty()]
  225.         [Alias("LogContent")]
  226.         [string]$Message,
  227.    
  228.         [Parameter(Mandatory = $false)]
  229.         [Alias('LogPath')]
  230.         [string]$Path = 'C:\Logs\PowerShellLog.log',
  231.            
  232.         [Parameter(Mandatory = $false)]
  233.         [ValidateSet("Error", "Warn", "Info")]
  234.         [string]$Level = "Info",
  235.            
  236.         [Parameter(Mandatory = $false)]
  237.         [switch]$NoClobber
  238.     )
  239.    
  240.     Begin {
  241.         # Set VerbosePreference to Continue so that verbose messages are displayed.
  242.         $VerbosePreference = 'Continue'
  243.     }
  244.     Process {
  245.            
  246.         # If the file already exists and NoClobber was specified, do not write to the log.
  247.         if ((Test-Path $Path) -AND $NoClobber) {
  248.             Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
  249.             Return
  250.         }
  251.    
  252.         # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
  253.         elseif (!(Test-Path $Path)) {
  254.             Write-Verbose "Creating $Path."
  255.             $NewLogFile = New-Item $Path -Force -ItemType File
  256.         }
  257.    
  258.         else {
  259.             # Nothing to see here yet.
  260.         }
  261.    
  262.         # Format Date for our Log File
  263.         $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
  264.    
  265.         # Write message to error, warning, or verbose pipeline and specify $LevelText
  266.         switch ($Level) {
  267.             'Error' {
  268.                 Write-Error $Message
  269.                 $LevelText = 'ERROR:'
  270.             }
  271.             'Warn' {
  272.                 Write-Warning $Message
  273.                 $LevelText = 'WARNING:'
  274.             }
  275.             'Info' {
  276.                 Write-Verbose $Message
  277.                 $LevelText = 'INFO:'
  278.             }
  279.         }
  280.            
  281.         # Write log entry to $Path
  282.         "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
  283.     }
  284.     End {
  285.     }
  286. }
  287. #endregion
  288. #region -------------------------------------------------------[Get-WindowsUpdateInstallEvents Function]-------------------------------------------------------
  289. function Get-WindowsUpdateInstallEvents {
  290.     [CmdletBinding()]
  291.     Param
  292.     (
  293.         [Parameter(Mandatory = $false)][datetime]$StartDate = [datetime]'01/01/1980',
  294.         [Parameter(Mandatory = $false)][datetime]$StopDate = [datetime]::Now
  295.     )
  296.  
  297.     $InstallStartedID = 43
  298.     $InstallFailedID = 20
  299.     $InstallSuccessID = 19
  300.  
  301.     $DateDifference = $StopDate - $StartDate
  302.  
  303.     $WUStartedEvents = Get-WinEvent -ProviderName 'Microsoft-Windows-WindowsUpdateClient' -FilterXPath "*[System[(EventID=$InstallStartedID) and TimeCreated[timediff(@SystemTime) &lt;= $($DateDifference.TotalMilliseconds)]]]"
  304.     $WUFailedEvents = Get-WinEvent -ProviderName 'Microsoft-Windows-WindowsUpdateClient' -FilterXPath "*[System[(EventID=$InstallFailedID) and TimeCreated[timediff(@SystemTime) &lt;= $($DateDifference.TotalMilliseconds)]]]"
  305.     $WUSuccessEvents = Get-WinEvent -ProviderName 'Microsoft-Windows-WindowsUpdateClient' -FilterXPath "*[System[(EventID=$InstallSuccessID) and TimeCreated[timediff(@SystemTime) &lt;= $($DateDifference.TotalMilliseconds)]]]"
  306.  
  307.     $WUEventsObject = [pscustomobject]@{
  308.         InstallationStartedEvents = $WUStartedEvents
  309.         InstallationFailedEvents  = $WUFailedEvents
  310.         InstallationSuccessEvents = $WUSuccessEvents
  311.     }
  312.  
  313.     return $WUEventsObject
  314. }
  315. #endregion
  316. #region -------------------------------------------------------[Remove-BIOSPassword Function]-------------------------------------------------------
  317. function Remove-HPBIOSPassword {
  318.     [CmdletBinding()]
  319.     Param
  320.     (
  321.         [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  322.                     $true
  323.                 } else {
  324.                     Throw "Please provide a valid Password File path."
  325.                 }})][string]$PasswordFilePath,
  326.         [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  327.                     $true
  328.                 } else {
  329.                     Throw "Please provide a valid BiosConfigUtility path."
  330.                 }})][string]$BCUPath
  331.     )
  332.  
  333.     $PasswordFile = (Resolve-Path -Path $PasswordFilePath).ProviderPath
  334.     $BCU = (Resolve-Path -Path $BCUPath).ProviderPath
  335.  
  336.     # Try to remove BIOS password using the BIOS password file
  337.     $Arg01 = "/cspwdfile:`"$PasswordFile`""
  338.     $Arg02 = '/nspwdfile:""'
  339.  
  340.     Write-Log -Path $LogPath -Level Info -Message "Trying to remove the BIOS password using '$PasswordFile'."
  341.     Write-Log -Path $LogPath -Level Info -Message "Executing: $BCU $Arg01 $Arg02"
  342.  
  343.     $BCUResult = & $BCU $Arg01 $Arg02
  344.  
  345.     return $BCUResult
  346. }
  347. #endregion
  348. #region -------------------------------------------------------[Set-BIOSPassword Function]-------------------------------------------------------
  349. function Set-HPBIOSPassword {
  350.     [CmdletBinding()]
  351.     Param
  352.     (
  353.         [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  354.                     $true
  355.                 } else {
  356.                     Throw "Please provide a valid New Password File path."
  357.                 }})][string]$NewPasswordFilePath,
  358.         [Parameter(Mandatory = $false)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  359.                     $true
  360.                 } else {
  361.                     Throw "Please provide a valid Current Password File path."
  362.                 }})][string]$CurrentPasswordFilePath,
  363.         [Parameter(Mandatory = $true)][ValidateScript( {if (Test-Path -Path $_ -ErrorAction SilentlyContinue) {
  364.                     $true
  365.                 } else {
  366.                     Throw "Please provide a valid BiosConfigUtility path."
  367.                 }})][string]$BCUPath
  368.     )
  369.  
  370.     $NewPasswordFile = (Resolve-Path -Path $NewPasswordFilePath).ProviderPath
  371.     $BCU = (Resolve-Path -Path $BCUPath).ProviderPath
  372.  
  373.     if ($CurrentPasswordFilePath) {
  374.         # Try to set a new BIOS password using a provided Current Password file.
  375.         $CurrentPasswordFile = (Resolve-Path -Path $CurrentPasswordFilePath).ProviderPath
  376.  
  377.         $Arg01 = "/nspwdfile:`"$NewPasswordFile`""
  378.         $Arg02 = "/cspwdfile:`"$CurrentPasswordFile`""
  379.  
  380.         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'."
  381.         Write-Log -Path $LogPath -Level Info -Message "Executing: $BCU $Arg01 $Arg02"
  382.  
  383.         $BCUResult = & $BCU $Arg01 $Arg02
  384.  
  385.     } else {
  386.         # Try to set a new BIOS password without a provided Current Password file. This should work if there is no BIOS password currently set.
  387.         $Arg01 = "/nspwdfile:`"$NewPasswordFile`""
  388.  
  389.         Write-Log -Path $LogPath -Level Info -Message "Trying to set the BIOS password to the BIOS password contained in '$NewPasswordFile'. No password file provided."
  390.         Write-Log -Path $LogPath -Level Info -Message "Executing: $BCU $Arg01"
  391.  
  392.         $BCUResult = & $BCU $Arg01
  393.     }
  394.  
  395.     return $BCUResult
  396. }
  397. #endregion
  398.  
  399. #endregion
  400.  
  401. #region -----------------------------------------------------------[Execution]------------------------------------------------------------
  402.  
  403. ## VARIABLE DECLARATION
  404.  
  405. # Set path to the log.
  406. if (!$LogPath) {
  407.     $LogPath = "$env:windir\Temp\$($ScriptName)_$Action.log"
  408. } else {
  409.     $LogPath = (Resolve-Path -Path $LogPath).ProviderPath
  410. }
  411.  
  412. Write-Log -Path $LogPath -Level Warn -Message "-----------------------------------------------------------START $ScriptName------------------------------------------------------------"
  413.  
  414. # Call the appropriate utility binary per the current environment.
  415. if ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64') {
  416.     $BiosConfigUtility = 'BiosConfigUtility64.exe'
  417. } else {
  418.     $BiosConfigUtility = 'BiosConfigUtility.exe'
  419. }
  420.  
  421. # Set path to the BiosConfigUtility
  422. if (!$BCUDirectoryPath) {
  423.     # Default is $PSScriptRoot
  424.     $BCUPath = "$PSScriptRoot\$BiosConfigUtility"
  425. } else {
  426.     $BCUPath = "$((Resolve-Path -Path $BCUDirectoryPath).ProviderPath)\$BiosConfigUtility"
  427. }
  428.  
  429. # Set path to DevCon
  430. if (!$DevConPath) {
  431.     # Default is $PSScriptRoot
  432.     $DevConPath = "$PSScriptRoot\DevCon.exe"
  433. } else {
  434.     $DevConPath = (Resolve-Path -Path $DevConPath).ProviderPath
  435. }
  436.  
  437. # Set path to the password file used with the ReplacePassword switch
  438. if ($SetPasswordFilePath) {
  439.     $PasswordFile = (Resolve-Path -Path $SetPasswordFilePath).ProviderPath
  440. }
  441.  
  442. # Set path to the password file(s) used with the RemovePassword switch
  443. if ($RemovePasswordFilePath) {
  444.     [array]$PasswordFiles = @()
  445.     $PasswordFiles += foreach ($item in $RemovePasswordFilePath) {
  446.         $PasswordFile = Resolve-Path -Path $item
  447.         $PasswordFile.ProviderPath
  448.     }
  449. }
  450.  
  451. # Set path to the Remote Log Share. If this is not provided in initial script parameters, log will not be copied.
  452. if ($CopyLog) {
  453.     if (!$RemoteLogSharePath) {
  454.         Write-Log -Path $LogPath -Level Warn -Message "RemoteLogSharePath parameter is NULL. Script will not be copying logs."
  455.         $CopyLog = $false
  456.     } else {
  457.         $RemoteLogSharePath = (Resolve-Path -Path $RemoteLogSharePath -ErrorAction Stop).ProviderPath
  458.     }
  459. }
  460.  
  461. # This class does not exist on all HP models
  462. $CurrentBIOS = Get-CimInstance -ClassName Win32_BIOS
  463. $CurrentBIOSVersion = ($CurrentBIOS.Name -split 'Ver.')[1].Trim()
  464.  
  465. $RemovePasswordFlag = $false
  466. $PasswordError = $false
  467. $PasswordDoublePlusError = $false
  468. $BCUResult = $null
  469. $State = $null
  470. $WUEvents = $null
  471. $HPFirmwareStartedEvents = $null
  472. $HPFirmware = $null
  473. $HPFirmwareProperties = $null
  474. $HPFirmwareProblemStatus = $null
  475.  
  476. Write-Log -Path $LogPath -Level Info -Message "
  477. Script Start Time: $(Get-Date)
  478. Log Path: $LogPath
  479. Processor Architecture: $env:PROCESSOR_ARCHITECTURE
  480. BiosConfigUtility Path:  $BCUPath
  481. DevCon Path: $DevConPath
  482. $(if($SetPasswordFilePath){"Password File Path: $PasswordFile"})
  483. Password Files: $(if($RemovePasswordFilePath){$PasswordFiles | ForEach-Object {"$($_);"}})
  484. Current BIOS Version: $CurrentBIOSVersion
  485. Remote Log Share: $RemoteLogSharePath"
  486.  
  487. ## MAIN LOGIC
  488.  
  489. Try {
  490.     Write-Log -Path $LogPath -Level Info -Message "Script was invoked with Action: $Action."
  491.     Write-Log -Path $LogPath -Level Warn -Message "-----------------------------------------------------------$Action------------------------------------------------------------"
  492.  
  493.     if ($Action -eq 'RemovePassword') {    
  494.         # Remove the registry key flag it's remaining from a previous script run.
  495.         Write-Log -Path $LogPath -Level Info -Message "Removing 'HKLM:\SOFTWARE\HP BIOS Update Script' flag registry key if it exists."            
  496.         $null = Remove-Item -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Force -Recurse
  497.        
  498.         # Get Windows Update install events to see if the HP Firmware installation had been attempted
  499.         Write-Log -Path $LogPath -Level Info -Message "Getting Windows Update install events to determine if the HP Firmware installation had been attempted."
  500.         $WUEvents = Get-WindowsUpdateInstallEvents -StartDate '02/20/2018'
  501.  
  502.         # Parse the Installation Started events for the HP Firmware entry
  503.         Write-Log -Path $LogPath -Level Info -Message "Parsing the Installation Started events for the HP Firmware entry."
  504.         $HPFirmwareStartedEvents = $WUEvents.InstallationStartedEvents | ForEach-Object {
  505.             $event = $null
  506.             $eventXML = $null
  507.             $eventObj = $null
  508.  
  509.             $event = $_
  510.             $eventXML = [xml]$event.toXML()
  511.                
  512.             if (($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text' -like '*HP*Firmware*') {
  513.                 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')'."
  514.  
  515.                 $eventObj = [pscustomobject]@{
  516.                     EventID              = $eventXML.Event.System.EventID
  517.                     Message              = $event.Message
  518.                     OPCodeDisplayName    = $event.OpcodeDisplayName
  519.                     TimeCreated          = Get-Date -Date $eventXML.Event.System.TimeCreated.SystemTime
  520.                     UpdateTitle          = ($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text'
  521.                     UpdateGUID           = ($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateGuid'}).'#text'
  522.                     UpdateRevisionNumber = ($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateRevisionNumber'}).'#text'
  523.                     UpdateVersion        = ((($eventXML.Event.EventData.Data | Where-Object {$_.Name -eq 'updateTitle'}).'#text') -split '-')[-1].Trim() # This likely only works for this batch of Firmware updates
  524.                 }
  525.                 $eventObj
  526.             }            
  527.         }            
  528.  
  529.         # Check if these installations completed successfully
  530.         if ($HPFirmwareStartedEvents) {
  531.             Write-Log -Path $LogPath -Level Info -Message "Checking if these HP Firmware Installation Started events have a corresponding Installation Successful event."
  532.             foreach ($HPFirmwareStartedEvent in $HPFirmwareStartedEvents) {
  533.                 if ($HPFirmwareStartedEvent.UpdateGUID -in (($WUEvents.InstallationSuccessEvents | ForEach-Object {$event = $_; $eventXML = [xml]$event.ToXml(); $eventXML}).Event.EventData.Data | Where-Object {$_.Name -eq 'updateGuid'}).'#text') {
  534.                     # This Firmware update installed successfully
  535.                     Write-Log -Path $LogPath -Level Info -Message "HP Firmware Update '$($HPFirmwareStartedEvent.UpdateTitle)' was successfully installed."
  536.                 } else {                        
  537.                     # This Firmware update DOES NOT have a corresponding Installation Success event.
  538.                     # It will attempt to install upon reboot, so we need to remove the BIOS password in preparation.
  539.                     Write-Log -Path $LogPath -Level Warn -Message "HP Firmware Update '$($HPFirmwareStartedEvent.UpdateTitle)' does not have a corresponding Installation Successful event."
  540.                     Write-Log -Path $LogPath -Level Warn -Message "Setting RemovePasswordFlag to True."
  541.  
  542.                     $RemovePasswordFlag = $true
  543.                     break
  544.                 }                                    
  545.             }
  546.         } else {
  547.             # No Installation Started events for HP Firmware.
  548.             Write-Log -Path $LogPath -Level Info -Message "No Installation Started events for HP Firmware."
  549.         }
  550.  
  551.         if (!$RemovePasswordFlag) {
  552.             # RemovePasswordFlag set to False. Exiting.
  553.             Write-Log -Path $LogPath -Level Info -Message "RemovePasswordFlag is set to False. Exiting script without removing the BIOS password."
  554.             $State = 'NA'
  555.            
  556.         } else {
  557.  
  558.             # The BIOS password needs to be removed for the firmware update to install correctly...
  559.             # 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'.
  560.             # Otherwise the Firmware won't try to update upon reboot.
  561.             Write-Log -Path $LogPath -Level Info -Message "BIOS password must be removed in order for the HP Firmware Update to complete successfully..."
  562.             Write-Log -Path $LogPath -Level Info -Message "Starting check to see if the HP Firmware 'device' is in an error state..."
  563.  
  564.             # Get the Firmware 'device' object
  565.             $HPFirmware = Get-CimInstance -ClassName Win32_PnPEntity -Filter "PnPDeviceID like 'UEFI%' AND PNPClass='Firmware' AND (Manufacturer like 'HP%' OR Manufacturer like 'Hewlett%')"
  566.  
  567.             # Get the Firmware 'device' object's properties
  568.             $HPFirmwareProperties = (Invoke-CimMethod -InputObject $HPFirmware -MethodName GetDeviceProperties).deviceProperties
  569.  
  570.             # Try to grab the 'DEVPKEY_Device_ProblemStatus' key from the properties
  571.             $HPFirmwareProblemStatus = $HPFirmwareProperties | Where-Object {$_.KeyName -eq 'DEVPKEY_Device_ProblemStatus'}
  572.  
  573.             # We are interested in any problem status that ISN'T 0 or 3221225560 which is the base 10 representation of 0xC00002D2 which translates to:
  574.             # 'The device will not start without a reboot.' (NTSTATUS 0xC00002D2: STATUS_PNP_REBOOT_REQUIRED)
  575.             # Reference: https://msdn.microsoft.com/en-us/library/cc704588.aspx
  576.             # Reference: https://support.microsoft.com/en-us/help/310123/error-codes-in-device-manager-in-windows
  577.             if ($HPFirmwareProblemStatus) {
  578.                 if ($HPFirmwareProblemStatus.Data -eq '0') {
  579.                     # STATUS_SUCCESS: The operation completed successfully.
  580.                     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."
  581.                 } elseif ($HPFirmwareProblemStatus.Data -eq '3221225560') {
  582.                     # STATUS_PNP_REBOOT_REQUIRED: The device will not start without a reboot.
  583.                     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."
  584.                 } else {
  585.                     # The Firmware is in an error state that isn't 0 or 3221225560. Need to 'remove' it and then rescan for hardware changes.
  586.                     # This will allow the Firmware to attempt the update again.
  587.                     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."
  588.    
  589.                     & $DevConPath '/remove' "@$($HPFirmware.DeviceID)"
  590.                     & $DevConPath '/rescan'
  591.                 }
  592.             } else {
  593.                 Write-Log -Path $LogPath -Level Warn -Message "HP Firmware does not have a Problem Status code."
  594.             }
  595.  
  596.             # Iterate through each supplied BIOS password file to try with the current Firmware
  597.             for ($i = 0; $i -lt $PasswordFiles.Count; $i++) {
  598.                 $PasswordFile = $null
  599.                 $PasswordFile = $PasswordFiles[$i]
  600.  
  601.                 # Remove the BIOS password
  602.                 $BCUResult = Remove-HPBIOSPassword -PasswordFilePath $PasswordFile -BCUPath $BCUPath
  603.  
  604.                 # Parse BiosConfigUtility output.
  605.                 Write-Log -Path $LogPath -Level Info -Message "Parsing BiosConfigUtility output..."
  606.                 foreach ($line in $BCUResult) {
  607.                     if ($line -like '*<SUCCESS msg*') {
  608.                         $split = $null
  609.                         $text = $null
  610.  
  611.                         $split = $line -split 'msg='
  612.                         $text = $split[1].Remove(($split[1].Length - 3), 3)
  613.                         Write-Log -Path $LogPath -Level Info -Message "BCU returned SUCCESS: $text"
  614.                     }
  615.  
  616.                     if ($line -like '*<ERROR msg*') {
  617.                         $split = $null
  618.                         $text = $null
  619.                        
  620.                         $split = $line -split 'msg='
  621.                         $text = $split[1].Remove(($split[1].Length - 3), 3)
  622.                         Write-Log -Path $LogPath -Level Warn -Message "BCU returned ERROR: $text"
  623.  
  624.                         # Set PasswordError variable to True so that we can try again with a different password
  625.                         Write-Log -Path $LogPath -Level Warn -Message "BiosConfigUtility returned an error running the command with the BIOS password contained in '$PasswordFile'. Setting PasswordError flag."
  626.                         $PasswordError = $true
  627.                     }
  628.                 } #END Parse output
  629.  
  630.                 if (!$PasswordError) {
  631.                     # No PasswordError which means the password was removed successfully. Stop trying passwords by breaking out of the loop.
  632.                     Write-Log -Path $LogPath -Level Info -Message "Successfully changed the BIOS password. No need to try any more passwords."
  633.                     break
  634.                 } else {
  635.                     # Changing BIOS password failed.
  636.                     if ($i -eq ($PasswordFiles.Count - 1)) {
  637.                         # If this was the last password in the list, system may need manual attention.
  638.                         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..."
  639.                         $PasswordDoublePlusError = $true                        
  640.                     } else {
  641.                         # Move on to next password file in the list.
  642.                         Write-Log -Path $LogPath -Level Warn -Message "Failed to changed the BIOS password. Trying next password file in the list."
  643.                         $PasswordError = $false
  644.                     }                    
  645.                 }                
  646.             }
  647.        
  648.             # 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.
  649.             if ($PasswordDoublePlusError) {
  650.                 $PasswordDoublePlusError = $false
  651.                 $State = 'SUPERERROR'
  652.             } else {            
  653.                 # BIOS Password has been successfully removed.
  654.                 Write-Log -Path $LogPath -Level Info -Message "BIOS Password has been successfully removed."
  655.  
  656.                 # Set registry key to let the script know to set the password upon next boot.
  657.                 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."
  658.                 $null = New-Item -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Force
  659.                 $null = New-ItemProperty -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Name 'Set BIOS Flag'
  660.  
  661.                 Write-Log -Path $LogPath -Level Info -Message "Ending script."
  662.                 $State = 'SUCCESS'
  663.             }
  664.         }
  665.     } #END RemovePassword
  666.  
  667.     if ($Action -eq 'ReplacePassword') {
  668.         # Check if this section should run by looking for the 'HKLM:\SOFTWARE\HP BIOS Update Script' registry key
  669.         if (!(Get-ItemProperty -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Name 'Set BIOS Flag' -ErrorAction SilentlyContinue)) {
  670.             # The reg key doesn't exist, end the script.
  671.             Write-Log -Path $LogPath -Level Warn -Message "'HKLM:\SOFTWARE\HP BIOS Update Script | Set BIOS Flag' does not exist. Ending script."
  672.             $State = 'NA'
  673.         } else {
  674.             # Try to replace password
  675.             $BCUResult = Set-HPBIOSPassword -NewPasswordFilePath $PasswordFile -BCUPath $BiosConfigUtility
  676.  
  677.             # Parse BiosConfigUtility output.
  678.             Write-Log -Path $LogPath -Level Info -Message "Parsing BiosConfigUtility output..."
  679.             foreach ($line in $BCUResult) {
  680.                 if ($line -like '*<SUCCESS msg*') {
  681.                     $split = $null
  682.                     $text = $null
  683.            
  684.                     $split = $line -split 'msg='
  685.                     $text = $split[1].Remove(($split[1].Length - 3), 3)
  686.                     Write-Log -Path $LogPath -Level Info -Message "BCU returned SUCCESS: $text"
  687.                 }
  688.            
  689.                 if ($line -like '*<ERROR msg*') {
  690.                     $split = $null
  691.                     $text = $null
  692.            
  693.                     $split = $line -split 'msg='
  694.                     $text = $split[1].Remove(($split[1].Length - 3), 3)
  695.                     Write-Log -Path $LogPath -Level Warn -Message "BCU returned ERROR: $text"
  696.            
  697.                     # Neither password worked. That's not good...
  698.                     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."
  699.                     $PasswordDoublePlusError = $true
  700.                 }
  701.             } #END FOREACH Parse output
  702.  
  703.             # 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.
  704.             if ($PasswordDoublePlusError) {
  705.                 $PasswordDoublePlusError = $false
  706.                 $State = 'SUPERERROR'
  707.             } else {
  708.                 # BIOS Password has been successfully set.
  709.                 Write-Log -Path $LogPath -Level Info -Message "BIOS Password has been successfully set."
  710.                
  711.                 # Remove the registry key flag
  712.                 Write-Log -Path $LogPath -Level Info -Message "Removing 'HKLM:\SOFTWARE\HP BIOS Update Script' flag registry key."            
  713.                 $null = Remove-Item -Path 'HKLM:\SOFTWARE\HP BIOS Update Script' -Force -Recurse
  714.  
  715.                 Write-Log -Path $LogPath -Level Info -Message "Ending script."
  716.                 $State = 'SUCCESS'
  717.             }
  718.         }
  719.     } #END ReplacePassword  
  720. } Catch {
  721.     Write-Log -Path $LogPath -Level Error -Message "Error: $($_.Exception)"
  722.     $State = 'EXCEPTION'
  723.     # Copy logs to the log share if CopyLog parameter is set
  724.     if ($CopyLog) {
  725.         $null = Copy-Item -Path $LogPath -Destination "$RemoteLogSharePath\$($env:COMPUTERNAME)_$($Action)_$State.log" -Force
  726.     }
  727.     return
  728. }
  729.  
  730. #clean up any variables, closing connection to databases, or exporting data
  731. If ($?) {
  732.     Write-Log -Path $LogPath -Level Info -Message 'Completed Successfully.'
  733.    
  734.     # Copy logs to the log share if CopyLog parameter is set
  735.     if ($CopyLog) {
  736.         if ($State -eq 'NA') {
  737.             # Don't copy logs if state is NA
  738.         } else {
  739.             $null = Copy-Item -Path $LogPath -Destination "$RemoteLogSharePath\$($env:COMPUTERNAME)_$($Action)_$State.log" -Force
  740.         }
  741.     }
  742. }
  743.  
  744. #endregion
Add Comment
Please, Sign In to add comment