10 min read

ConfigMgr - Dynamic* HP BIOS Config

ConfigMgr - Dynamic* HP BIOS Config

UPDATE 2022-01-20

Added a function to the script to set a BIOS password if there is none and you config the .JSON to do so. Read more about it further down.

Dynamically choose BIOS config and set config

When i first started working with MECM / SCCM the TS's i worked with had a bios config part that looked something like this, and i still see this at other customers

Sure, it worked / works, it didn't require ALOT of work with each new model, but it required some thought when adding new models, updating settings, or updated HP's BCU tool.

  • Create new / export BIOS configuration to file
  • Edit settings
  • Create package
  • Edit TS
  • Test

And i thought that there must be a better and more smoother way to do this and started working on this Powershell script to make the process a bit dynamic and cutting out some steps from the process.

The goal was to set it up, and then being able to "forget" about everything except creating new configuration files, not needing to update any packages, not creating new ones, not needing to reconfig the TS or anything like that. I wanted to be able to make an change in a settingsfile and it will be immediate, and if i have a new settingsfile just copy it to the right place, and it's done.

What i came up with, something that will look like this:

The files being used eg.

The script

<#.DESCRIPTION
    This script will use the gathered BIOS config file from HP BiosConfigUtility and set your systems BIOS preferences. 
    A BIOS Settings file can have .REPSET or .txt filextension.
    Make sure your settings file is named the same as your Win32_ComputerSystem property Model, 
    eg "HP ZBook 15 G4.REPSET" as the script will call this WMI Class to match up with a settigsfile.
    
    Script expects these files to be in the same directory as BiosConfigUtility64.exe :
        Passwordfile.bin
        BiosConfigUtility64.exe
        BIOSSettings.REPSET (or .txt)
		Config.json

    To Download HP BIOS Configuration Utility
    https://ftp.hp.com/pub/caps-softpaq/cmit/HP_BCU.html
    
    For more information on the BIOSConfigUtility:
    http://ftp.hp.com/pub/caps-softpaq/cmit/whitepapers/BIOS_Configuration_Utility_User_Guide.pdf


.PARAMETER Config

    Enter the UNCPath to the config.JSON file, eg "\\SCCMSERVER\OSD$\Bios Settings\Config.JSON"

.EXAMPLE
\\SCCMSERVER\BIOS$\Set-HPBIOSConfig.ps1 -Config .\config.JSON

.EXAMPLE
\\SCCMSERVER\BIOS$\Set-HPBIOSConfig.ps1 -Config \\SCCMSERVER\OSD$\Bios Settings\config.JSON

.UPDATES
    2022-01-20 - Added section to configure biospassword with .BIN if set in .JSON config. If json is set to configure password, the script will now check if password is set or not
                if there is no password the script will use your PASSWORD.BIN and set the password in there. If there is a password set it will skipt this part.

#>

param(
    [Parameter(HelpMessage = 'UNC-Path to JSON Configuration File', Mandatory = $True)]
    [ValidateNotNullOrEmpty()]
    [ValidateScript( { Test-Path -Path $_ -PathType Leaf })]
    [ValidatePattern('.json')]
    [string]$Config
)

#Get config and BCU ExitCodes from .JSON

Try {
    $ScriptConfig = Get-Content $Config | ConvertFrom-Json
}
Catch {
    $_.Exception.Message
}

$BCUPath = $ScriptConfig.ScriptConfig.HPBCUPath
$SetPassword = $ScriptConfig.ScriptConfig.SetPassword
$PasswordFile = $ScriptConfig.ScriptConfig.PWDFileName
$RepsetExt = $ScriptConfig.ScriptConfig.FileExt
$SetGet = $ScriptConfig.ScriptConfig.SetGet
$GetPath = $ScriptConfig.ScriptConfig.GetPath


# Gather System preferences
$ComputerSystemModel = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model

#Construct TSEnv
if ($SetGet -eq "set") {
    Try {
        $TSEnvironment = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction Continue 
        Write-Output "Task Sequence Variables loaded"
    }
    Catch {
        $_.Exception.Message
        Write-Output "Task Sequence Variables not loaded, assuming run in full OS, outside of task sequence environment.."
        $TSEnvironment = $null
    }
}
   
#Set BIOS Variables
If ($SetGet -eq "Set") {
    If ($TSEnvironment -ne $null) {
        $LogPath = $TSEnvironment.Value("_SMSTSLogPath")
    }
    else {
        $LogPath = "$env:windir\CCM\Logs"
    }
    $REPSETFile = "$BCUPath\$ComputerSystemModel$RepsetExt"
    Write-Output "Trying to find $REPSETFile..."
    $LogFile = "$LogPath\$ComputerSystemModel.REPSETLog.log"
    Write-Output "Output will be logged to $LogFile..."
}
Else {
    $REPSETFile = "$GetPath\$ComputerSystemModel.GET$RepsetExt"
    Write-Output "Config file will be saved as $REPSETFILE..."
    $LogFile = "$GetPath\$ComputerSystemModel.REPSETCreate.log"
    Write-Output "Output will be logged to $LogFile..."
}

$CurrentPassword = "$BCUPath\$PasswordFile.bin"
Write-Output "Password.bin file set as $CurrentPassword..."

## Check if BIOS Password is set, else set it
#Connect to the HP_BIOSSetting WMI class
if($SetPassword -eq "True"){
Try{
    $HPBIOSWMI = Get-WmiObject -Namespace root\HP\InstrumentedBIOS -Class HP_BIOSSetting
}
Catch{
    $_.Exception.Message
    Write-output "Could not load BIOS WMI Object..."
    End
}

Try{
    Write-Output "Checking if BIOS password is set or if it needs to be set..."
    $PasswordState = $HPBIOSWMI| Select-Object Name,IsSet | Where-Object Name -eq "Setup Password" | Select -expand IsSet 
}
Catch{Write-output "$Setting not present in BIOS"}
switch($SettingState){
        "0" {
            # Set HP BCU arguments for BCU cmdline
            $HPBCUPWDSet = @{
                FilePath               = "$BCUPath\BiosConfigUtility64.exe"
                ArgumentList           = @(
                        "`"/npwdfile:$CurrentPassword`""
                )
                wait                   = $True
                PassThru               = $True
                RedirectStandardOutput = $LogFile
                WindowStyle            = "Hidden"
            }
            (Start-Process @HPBCUPWDSet).ExitCode
        
        }
        "1" {Write-output "Password already set..."}
    }
}
    

# Set HP BCU arguments for BCU cmdline
$HPBCU = @{
    FilePath               = "$BCUPath\BiosConfigUtility64.exe"
    ArgumentList           = @(
        "`"/$SetGet`:$REPSETFile`"", `
            "`"/cpwdfile:$CurrentPassword`"", `
            "`"/verbose`""
    )
    wait                   = $True
    PassThru               = $True
    RedirectStandardOutput = $LogFile
    WindowStyle            = "Hidden"
}

# Run HP BCU
Try {
    if ($SetGet -eq "set") {
        Write-Output "HP BCU Parameters set, will try to config BIOS..."
    }
    else {
        Write-Output "Will create HP BIOS config file..."
    }
            $BCUExitCode = (Start-Process @HPBCU).ExitCode
}
Catch {
    $_.Exception.Message ; Exit 1
}

#Exit and write exitmessage
Write-OutPut "$($ScriptConfig.ExitCodes.$BCUExitCode)"
Exit $BCUExitCode 

The .JSON Config file

[
    {
        "ScriptConfig": {
            "HPBCUPath": "\\\\SERVER\\Source$\\OSD\\Bios Settings",
            "SetPassword": "true",
            "PWDFileName": "Password",
            "FileExt": ".REPSET",
            "SetGet": "Get",
            "GetPath": "\\\\SERVER\\Source$\\OSD\\Bios Settings\\Get"
        },
        "ExitCodes": {
            "0": "0 - Success - Opertation Successfull.",
            "1": "1 - Not Supported - WmI result code - Setting is not supported on system.",
            "2": "2 - Unknown - WmI result code - Operation failed for unknown reason.",
            "3": "3 - Timeout - WmI result code - Operation timed out.",
            "4": "4 - Failed - WmI result code - Operation failed.",
            "5": "5 - Invalid Parameter - WmI result code - A parameter is missing or wrong type.",
            "6": "6 - Access Denied - WmI result code - Setting modification failed due to BIOS permissions.",
            "10": "10 - Valid password not provided",
            "11": "11 - Config file not valid - BcU was unable to locate the configuration file or unable to read the file at the specified path.",
            "12": "12 - First line in the configuration file must be the word “BIOSconfig” followed by the file format version, currently “1.0”.",
            "13": "13 - BcU failed to change one or more settings. Use /verbose or /WarningAserr to get status per setting.",
            "14": "14 - BcU not ready to write file.",
            "15": "15 - commandline syntax error.",
            "16": "16 - -Unable to write to file or system. BcU was unable to connect to HP BIOS WmI. WmI classes are corruptedor the system is not supported.",
            "17": "17 - Help is invoked.",
            "18": "18 - Setting is unchanged.",
            "19": "19 - Setting is read-only",
            "20": "20 - Invalid setting name.",
            "21": "21 - Invalid setting value.",
            "22": "22 - Unable to connect to HP BIOS WmI namespace.",
            "23": "23 - Unable to connect to HP WmI namespace",
            "24": "24 - Unable to connect to PUBLIc WmI namespace",
            "30": "30 - Password file error.",
            "31": "31 - Password is not F10 compatible.",
            "32": "32 - Platform does not support Unicodepasswords",
            "33": "33 - No settings to apply found in config file.",
            "35": "35 - missing parameter. BcU_OneTime_Not_Found.",
            "36": "36 - missing parameter. BcU_AntiReplayValue_Not_Found.",
            "37": "37 - missing parameter. BcU_PrivateKey_Not_Found.",
            "38": "38 - corrupt or missing file. BcU_Unable_LoadDll_BcUsignature.",
            "39": "39 - BcU_Unable_Getclass_entry.",
            "40": "40 - BcU_Unable_Getclass_Point.",
            "41": "41 - Invalid UID. Invalid Universal Unique Identifier."
        }
    }
]

Breaking it down

What the folder contains (and need to contain) is this:

You will have to name your configurations files the exactly the same as the "Win32_ComputerSystem" property "Model", as the script will use this to to match what config file it will apply to the BIOS. To look it up, you can use powershell

Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model

The files

  • BiosConfigUtility64.exe - The HP Bios Configuration Utility
  • Set-HPBIOSConfig.ps1 - The script which is working the magic and selecting which .REPSET to use
  • Password.bin - Your encrypted BIOS Password (if you use one), generated with "HPQPswd64.exe", which is installed when you install HP BCU.
  • .REPSET -files - Your bios configuration files containing the config for each of your HP models, yours might end with .txt, it doesn't matter.
  • Config-HPBIOSConfig.JSON - The config file you use to determine your fileextenstion (eg. ".repset", ".txt"), the name of your Password.Bin, if you want to "/Set" or "/Get" (set a bios config, or generate a new config file), the path to BCU and your config's and the path to use when you generate a new config file. It also contains the different exitcodes and their respective message which will be output in the log.

Putting it to use

What you will want to do is create a folder on your CM server, which devices being deployed can reach, in order to read your script config and your BIOS configuration files.

In it, place the files needed (see the files-screenshot above), the HP BCU, .Json Config, you different config files for your supported models, your encrypted password. The Script itself is not needed to be placed there, but it's a good place to save it.

The .JSON Configuration file

I decided to run with a configuration file for the script, so that you shouldn't have to edit anything directly in the script-file. What You will need to edit in "Config-HPBIOSConfig.JSON" .

"ScriptConfig": {
            "HPBCUPath": "\\\\SERVER\\Source$\\OSD\\Bios Settings",
            "SetPassword": "true",
            "PWDFileName": "Password",
            "FileExt": ".REPSET",
            "SetGet": "Get",
            "GetPath": "\\\\SERVER\\Source$\\OSD\\Bios Settings\\Get"
        },
  • HPBCUPath - the folder containing the HP BCU and config files.
  • SetPassword - Set to "true" if you want the script to also use your Password.bin to set a bios setup password if there is none already set, if the script finds that a password is already set, this part will be skipped.
  • PWDFileName - the name of your Password.bin, leave out the filext ".bin"
  • FileExt - the extension you use for your configuration files, eg .REPSET or maybe .txt
  • SetGet - if you want this config to user "/Set" or "/Get", setting a config or generating a new config file.
  • GetPath - If you use /Get, where it will save the generated configuration file.

The HP Configuration files

I'm not going to go through these at depth, as i expect you to have some knowledge about it and have managed them before, if not, the HP Documentation about the BCU quite good and doesn't take much reading to grasp.

But, you generate an config-file either by copying the HP BCU onto a device and then running the /Get command to export the settings into a text file, and then you edit it by removing what you don't need to configure, the things you want to change, etc etc. and then save and place it at a good space.

The Script

The script shouldn't need any editing, as the parameters are set through the Config.json file, but, shortly what it does:

Get script config from the .JSON

#Get config and BCU ExitCodes from .JSON

Try {
    $ScriptConfig = Get-Content $Config | ConvertFrom-Json
}
Catch {
    $_.Exception.Message
}

$BCUPath = $ScriptConfig.ScriptConfig.HPBCUPath
$PasswordFile = $ScriptConfig.ScriptConfig.PWDFileName
$RepsetExt = $ScriptConfig.ScriptConfig.FileExt
$SetGet = $ScriptConfig.ScriptConfig.SetGet
$GetPath = $ScriptConfig.ScriptConfig.GetPath

Determine what model the script is running on by checking the "Win32_ComputerSystem" class in WMI.

# Gather System preferences
$ComputerSystemModel = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty Model

Try to import the Microsoft.SMS.TSenvironment ComObject, if it fails with this, it will assume you are running the script directly on a device, outside a TS.

#Construct TSEnv
if ($SetGet -eq "set") {
    Try {
        $TSEnvironment = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction Continue 
        Write-Output "Task Sequence Variables loaded"
    }
    Catch {
        $_.Exception.Message
        Write-Output "Task Sequence Variables not loaded, assuming run in full OS, outside of task sequence environment.."
        $TSEnvironment = $null
    }
}


Determine where to store the .log, what model config it will use and what Passwordfile.

#Set BIOS Variables
If ($SetGet -eq "Set") {
    If ($TSEnvironment -ne $null) {
        $LogPath = $TSEnvironment.Value("_SMSTSLogPath")
    }
    else {
        $LogPath = "$env:windir\CCM\Logs"
    }
    $REPSETFile = "$BCUPath\$ComputerSystemModel$RepsetExt"
    Write-Output "Trying to find $REPSETFile..."
    $LogFile = "$LogPath\$ComputerSystemModel.REPSETLog.log"
    Write-Output "Output will be logged to $LogFile..."
}
Else {
    $REPSETFile = "$GetPath\$ComputerSystemModel.GET$RepsetExt"
    Write-Output "Config file will be saved as $REPSETFILE..."
    $LogFile = "$GetPath\$ComputerSystemModel.REPSETCreate.log"
    Write-Output "Output will be logged to $LogFile..."
}

$CurrentPassword = "$BCUPath\$PasswordFile.bin"
Write-Output "Password.bin file set as $CurrentPassword set..."

Using WMI to determine if a BIOS Setup password is already set or not, if none is set and the JSON is configured to set a password, this is where it will do so

## Check if BIOS Password is set, else set it
#Connect to the HP_BIOSSetting WMI class
if($SetPassword -eq "True"){
Try{
    $HPBIOSWMI = Get-WmiObject -Namespace root\HP\InstrumentedBIOS -Class HP_BIOSSetting
}
Catch{
    $_.Exception.Message
    Write-output "Could not load BIOS WMI Object..."
}

Try{
    Write-Output "Checking if BIOS password is set or if it needs to be set..."
    $PasswordState = $HPBIOSWMI| Select-Object Name,IsSet | Where-Object Name -eq "Setup Password" | Select -expand IsSet 
}
Catch{Write-output "$Setting not present in BIOS"}
switch($SettingState){
        "0" {
            # Set HP BCU arguments for BCU cmdline
            $HPBCUPWDSet = @{
                FilePath               = "$BCUPath\BiosConfigUtility64.exe"
                ArgumentList           = @(
                        "`"/npwdfile:$CurrentPassword`""
                )
                wait                   = $True
                PassThru               = $True
                RedirectStandardOutput = $LogFile
                WindowStyle            = "Hidden"
            }
            (Start-Process @HPBCUPWDSet).ExitCode
        
        }
        "1" {Write-output "Password already set..."}
    }
}

Setting up the cmdline to use with the BCU

# Set HP BCU arguments for BCU cmdline
$HPBCU = @{
    FilePath               = "$BCUPath\BiosConfigUtility64.exe"
    ArgumentList           = @(
        "`"/$SetGet`:$REPSETFile`"", `
            "`"/cpwdfile:$CurrentPassword`"", `
            "`"/verbose`""
    )
    wait                   = $True
    PassThru               = $True
    RedirectStandardOutput = $LogFile
    WindowStyle            = "Hidden"
}

Try to run the BCU with all the gathered parameters and configure the BIOS

# Run HP BCU
Try {
    if ($SetGet -eq "set") {
        Write-Output "HP BCU Parameters set, will try to config BIOS..."
    }
    else {
        Write-Output "Will create HP BIOS config file..."
    }
	$BCUExitCode = (Start-Process @HPBCU).ExitCode
}
Catch {
    $_.Exception.Message ; Exit 1
}

#Exit and write exitmessage
Write-OutPut "$($ScriptConfig.ExitCodes.$BCUExitCode)"
Exit $BCUExitCode

Setting up the Task Sequence

This is actually kinda the smallest part about this, as it should only require you to add one "Run powershell Script"-step, copy / paste the script, add the -config parameter and change executionpolicy to "bypass", as such:

Depending on how its run, it will output a logfile showing how each setting configuration went, if it failed, success or the setting doesn't exist etc.
The log should be found under "C:\Windows\CCM\Logs" and called "MODEL.REPSETLog.Log", or the CM logdirectory depending on what stage in the TS its run.

And that's about it!

I hope this can save you some time and hassle, as much as it has me.

References

HP BIOS Configuration Utility | HP Client Management Solutions
HP BIOS Configuration Utility
EndpointManagement/Set-HPBIOSConfig/JSON at master · Love-A/EndpointManagement
Endpoint Management stuff galore! Contribute to Love-A/EndpointManagement development by creating an account on GitHub.