Uncategorized

Managing BIOS/UEFI Settings from Configuration Manager Part 1

February 1, 2021

This is a multipart post:
Part 1: The code underneath – You are here
Part 2: Using as Configuration Items
Part 3: Using as Applications
Part 4: Using in a task sequence

This topic has no doubt been covered many times before. My work on it started when I stumbled upon Damien Van Robaeys’ post a couple weeks ago. I wanted to take Damien’s work and apply it to deploying with Configuration Manager.

Part 1: The code underneath

The system vendors have created methods to interact with system firmware from within Windows. HP and Lenovo went with standard WMI classes with methods, while Dell went with a custom PowerShell module. While both methods are easy to work with, I wanted to minimize the number of CIs or Applications I would need to make should I use multiple hardware vendors.

Dell Command PowerShell Provider (DCPP)

As Dell had the extra requirement for the PowerShell module, I started there. I considered saving the module to a network path and just importing on demand, but decided I wanted to avoid systems needing to download from a share that may or may not be local. Instead, I opted to create an application. I downloaded the module from Dell’s webpage, although using Install-Module to download via NuGet also works. I put the x64 module and a small install script in a folder.

$path=Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path
copy-item $path\DellBIOSProvider "$($Env:ProgramFiles)\WindowsPowerShell\Modules" -Recurse -force

I also added an uninstall script.

remove-item "$($Env:ProgramFiles)\WindowsPowerShell\Modules\DellBIOSProvider" -Recurse -Force

I set the requirements to be Dell as a manufacturer, and then a detection method to verify the module is available.

if(Get-Module -ListAvailable -Name DellBIOSProvider){$TRUE}
else{}

I also set the application’s icon to a Dell logo as I dislike generic icons in Software Center.

I opted to deploy the application to all workstations as the deployment type’s requirement would ensure it only installed on Dell systems.

Getting the correct settings

Before I can interact with a specific setting, I need to know the name of the setting. There is no standard between the three vendors I was testing with, and I would not be surprised to learn there are differences between models from the same vendor. I only have one model from each, so I cannot verify that. I used three functions based upon Damien’s but changed them to fit standard PS naming conventions and tried to simplify the code.

Dell systems also require knowing the setting category, so I also write that out before listing the settings in each category. I hate to use write-host, but I do not intend for this function to be used in a script, but just to list out the settings.

Function Get-AllDellUEFISettings{
    get-command -module DellBIOSProvider | out-null
    $categories = get-childitem -path DellSmbios:\ | select-object category
    foreach($cat in $categories){
        write-host $cat.Category -foregroudcolor green
        get-childitem -path @("DellSmbios:\" + $cat.Category)  | select-object attribute, currentvalue 
    }
}

HP was nice enough to also include the possible settings in the WMI class, so I return those as well.

Function Get-AllHPUEFISettings{
    Get-CIMInstance -Namespace root/hp/instrumentedBIOS -ClassName hp_biosEnumeration -ErrorAction SilentlyContinue | ?{$_.DisplayInUI -eq 1} |
    ForEach-Object{
        New-Object psobject -Property @{    
        Setting = $_."Name"
        Value = $_."currentvalue"
        possiblevalues = $_."possiblevalues"
        }
    }  | select-object Setting, Value, possiblevalues
}

Lenovo

Function Get-AllLenovoUEFISettings{
    Get-CimInstance -classname Lenovo_BiosSetting -namespace root\wmi  | select-object currentsetting | Where-Object {$_.CurrentSetting -ne ""} |
    select-object @{label = "Setting"; expression = {$_.currentsetting.split(",")[0]}} , 
    @{label = "Value"; expression = {$_.currentsetting.split(",*;[")[1]}} 
}

As you can imagine, there are a lot of settings for each model and many scenarios you may find yourself needing to change a setting for. I am going to focus on security features that get us to a point we can use BitLocker and Credential Guard. That means UEFI, SecureBoot, Virtualization Extensions, and TPM.

Using the functions above I was able to find the following settings for enabling the TPM

  • Dell: TpmSecurity\TpmSecurity = Enabled
  • HP: TPM State = Enable
  • Lenovo: SecurityChip = Enable

Checking a UEFI Setting

Now we need a way to verify the systems are set as we desire. I threw together three quick functions to return a Boolean result of if a setting is set correctly.

Dell

Function Confirm-DellUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Category,
    [Parameter(Position = 1,mandatory = $true)]$Setting,
    [Parameter(Position = 2,mandatory = $true)]$Value
    ) 
    (get-item "Dellsmbios:\$($Category)\$($Setting)").CurrentValue -eq $Value
}

HP

Function Confirm-HPUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value
    ) 
    (get-ciminstance -classname hp_biosEnumeration -namespace root/hp/instrumentedBIOS | Where-Object{$_.Name -eq "$($Setting)"} | select-object -ExpandProperty currentValue) -eq $Value
}

Lenovo

Function Confirm-LenovoUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value
    ) 
    (get-ciminstance -classname Lenovo_BiosSetting -namespace root\wmi | Where-Object{$_.CurrentSetting -like "$($Setting),*"} | select-object -ExpandProperty CurrentSetting).split(',')[-1] -eq $Value
}

When we put those functions and setting together, we get three lines.

Confirm-DellUEFISetting TpmSecurity TpmSecurity Enabled
Confirm-HPUEFISetting "TPM State" Enable
Confirm-LenovoUEFISetting SecurityChip Enable

Of course, we only want one of those lines to run based on the Manufacturer. So, we’ll wrap it in some conditional statements. These conditional statements are based on the devices I have available to me and educated guesses on how to ensure I cover variations. You may need to tweak them if one of the manufacturers tweaked how they list their name.

$Manufacturer = (get-ciminstance -ClassName Win32_ComputerSystem).Manufacturer
if($Manufacturer -like "Dell*"){Confirm-DellUEFISetting TpmSecurity TpmSecurity Enabled}
elseif($Manufacturer -like "Hewlitt*" -or $Manufacturer -like "HP*"){Confirm-HPUEFISetting "TPM State" Enable}
elseif($Manufacturer -eq "Lenovo"){Confirm-LenovoUEFISetting SecurityChip Enable}
else{write-error "Unable to read BIOS\UEFI for this brand or model"}

Let’s look at that as one script that we could use as a standalone CI or (with slight modification) a detection method on an application.

Function Confirm-DellUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Category,
    [Parameter(Position = 1,mandatory = $true)]$Setting,
    [Parameter(Position = 2,mandatory = $true)]$Value
    ) 
    (get-item "Dellsmbios:\$($Category)\$($Setting)").CurrentValue -eq $Value
}

Function Confirm-HPUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value
    ) 
    (get-ciminstance -classname hp_biosEnumeration -namespace root/hp/instrumentedBIOS | Where-Object{$_.Name -eq "$($Setting)"} | select-object -ExpandProperty currentValue) -eq $Value
}

Function Confirm-LenovoUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value
    ) 
    (get-ciminstance -classname Lenovo_BiosSetting -namespace root\wmi | Where-Object{$_.CurrentSetting -like "$($Setting),*"} | select-object -ExpandProperty CurrentSetting).split(',')[-1] -eq $Value
}

$Manufacturer = (get-ciminstance -ClassName Win32_ComputerSystem).Manufacturer
if($Manufacturer -like "Dell*"){Confirm-DellUEFISetting TpmSecurity TpmSecurity Enabled}
elseif($Manufacturer -like "Hewlitt*" -or $Manufacturer -like "HP*"){Confirm-HPUEFISetting "TPM State" Enable}
elseif($Manufacturer -eq "Lenovo"){Confirm-LenovoUEFISetting SecurityChip Enable}
else{write-error "Unable to read BIOS\UEFI for this brand or model"}

Next – Part 2: Using as Configuration Items