How to update BIOS on laptops that have Bitlocker Encrypted drives.
Download Script from here(updateBios)
Completing an unattended update of computers BIOS was something I never thought I would be championing but with hardware vendors now releasing updates every other month which include performance and stability fixes we have been forced into investigating how to complete this.
So my initial plan was to finally get SCUP up and running and then utilise the BIOS updates that are included in the HP pack. This was great for our desktops and laptops that a.) didn’t have a BIOS password, and b.) didn’t have BitLocker enabled.
So from here we needed to come up with a different route, that could handle bother the BIOS password and BitLocker, what also needed to be taking into consideration is to check to make sure the laptop has AC power connected, as most vendors have put safe guards in place to stop BIOS installing if not connected. Along with the re-enablement of BitLocker encryption after the BIOS has been updated.
So lets get into the code, for simplicity it is all written in vbs, and has been tested on Windows 7 & 8. Below is a break down of the Sub Routines & Functions.
Application Execution
sn = wscript.scriptname ' gets the script name fn = wscript.scriptfullname 'gets the scripts full UNC Path fp = replace(fn, "\" & sn, "") 'provides the path of all of the files. Set objShell = WScript.CreateObject("WScript.Shell") opt = WScript.Arguments.Item(0) 'returns the Argument that has been entered in at script calling. if opt = "flash" then 'is the option to Flash turned on if power then 'is Power Attached? manageBDE ("disable") 'Suspend BitLocker Drive Encryption install (fp & "\hpqflash.exe -s -p" & fp & _ "\pwfile.bin") 'Call the HP Flash applicaiton - Change this to match your Vendor writekey "SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" _ , "ReenableBDE", fn & " encrypt" 'Create regkey to unsuspend BDE after reboot install ("c:\windows\system32\shutdown.exe -r -f -t" _ " 600") 'Restart the computer change the timeout to match you need end if elseif opt = "encrypt" then 'is the option to encrypt? manageBDE ("enable") 'unsuspend BDE elseif opt = "/?" then wscript.echo "Usage of script, to kick off Flashing" _ " call script with the argument "& chr(34) & "Flash" & chr(34) end if
This is the code that is used to call the Sub-Routines & Functions detailed below.
Checking the AC:
'################################################# ' Function Name: power ' Outputs: ' power (boolen) = TRUE means power is plugged in, ' FALSE means power it not plugged in '################################################# function power Set objWMIService = GetObject("winmgmts:\\.\root\wmi") Set colItems = objWMIService.ExecQuery("Select *" _ " From BatteryStatus Where Voltage > 0") For Each objItem in colItems if objItem.Discharging = FALSE then 'wscript.echo "AC plugged in" power = true else 'wscript.echo "AC NOT plugged in" power = false end if Next end function
The reason why we use Discharging rather then Charging is that we found if the Laptop was fully charged it would return that it wasn’t charging, but if the AC was plugged in, the laptop isn’t discharging (have a think about it and it makes sense).
Manage BDE (BitLocker Drive Encryption)
'################################################# ' Routine Name: manageBDE ' Inputs: ' switch (String) = options are "Enable" or "Disable" ' checks to see if BitLocker is enabled and if it is it will ' Suspend if required. it will also report the various ' states with different exit codes as listed in the script. '################################################# sub manageBDE(switch) dim nProtStatus, intRC strConnectionStr2 = "winmgmts:{impersonationLevel=impersonate," _ "authenticationLevel=pktPrivacy}!root\cimv2\Security\" _ "MicrosoftVolumeEncryption" Set objWMIBDE = GetObject(strConnectionStr2) Set colEnVol = objWMIBDE.ExecQuery("Select * from" _ " Win32_EncryptableVolume") if colEnVol.count > 0 then For Each objEnVol in colEnVol if objEnVol.DriveLetter = "C:" then intRC = objEnVol.GetConversionStatus(nProtStatus) if switch = "disable" then select case nProtStatus case 0 'fully decrypted wscript.quit 663 case 1 'fully encrypted objShell.Run ("c:\windows\system32\manage-bde.exe" _ " -protectors -disable C:") case 2 'Encryption in Progress wscript.quit 665 case 3 'Decryption in progress wscript.quit 666 case 4 'Encryption Paused case 5 'Decryption Paused wscript.quit 664 end select elseif switch = "enable" then select case nProtStatus case 0 'fully decrypted wscript.quit 663 case 1 'fully encrypted objShell.Run ("c:\windows\system32\manage-bde.exe" _ " -protectors -disable C:") objShell.Run ("c:\windows\system32\manage-bde.exe" _ " -protectors -enable C:") case 2 'Encryption in Progress wscript.quit 665 case 3 'Decryption in progress wscript.quit 666 case 4 'Encryption Paused objShell.Run ("c:\windows\system32\manage-bde.exe" _ " -protectors -enable C:") case 5 'Decryption Paused wscript.quit 664 end select end if end if next end if end sub
We ended up using the GetProvisionStatus rather then the GetEncryptionStatus option for this solution to be able to handle if the hard drive is currently in the process of being encrypted, for example when a system is recently been built. When using GetEncryptionStatus WMI reported correctly that BDE was enabled, it just didn’t report that it was currently being encrypted.
Install Sub Routine
'################################################# ' Routine Name: install ' Inputs: ' exestring (string) = command you want to call. ' this Routine will start an application and wait ' until the application has stopped before moveing ' on to the next task in the script. useful for 'applications that close automaticly with exit code 0 '################################################# sub install(exestring) running = 1 strComputer = "." strCommand = exestring Set objWMIService = GetObject("winmgmts:" _ "{impersonationLevel=impersonate}!\\" & strComputer _ & "\root\cimv2") Set objProcess = objWMIService.Get("Win32_Process") errReturn = objProcess.Create(strCommand, null, null, _ intProcessID) If errReturn = 0 Then 'wscript.echo exestring & " : " & intprocessid do while running = 1 Set objProcess = objWMIService.execquery("select *" _ " from Win32_Process") for each objproc in objprocess if objproc.processid = intprocessid then running = 1 exit for else running = 0 end if next loop Else End If end sub
This routine is something I wrote a few years back to deal with those awesome applications that report exit code 0 immediately on execution for the application. (contact me if you need a routine to handle an executable inside an executable….)
Write RegKey
'################################################# ' Routine Name: WriteKey ' Inputs: ' key (string) = this is the path to the key to update ' vn (string) = this is the new Value name ' v (string) = this is the new Value ' Creates a DWORD value at the KEY path. '################################################# sub writekey(key,vn,v) const HKEY_LOCAL_MACHINE = &H80000002 Set oReg=GetObject("winmgmts:" _ "{impersonationLevel=impersonate}!\\.\root\default:StdRegProv") oReg.SetStringValue HKEY_LOCAL_MACHINE,key,vn,v end sub
This writes a DWORD into the registry in this script we use it to handle the re-enablement of BDE after a restart.
One thing to note is that to complete the HP Bios update from the command line you are required to create a pwfile.bin file by using this application http://ftp.hp.com/pub/caps-softpaq/cmit/softpaq/sp62065.exe when installing the application it displays as HP ElitePad 900 from my experience this file will work for all HP Laptops that utilise the HPQFlash application
I think that pretty much covers it off, we have successfully tried this on multiple HP laptop models to great success.
Good Luck
Steve