Uncategorized

Managing BIOS/UEFI Settings from Configuration Manager Part 2

February 3, 2021

This is a multipart post:
Part 1: The code underneath
Part 2: Using as Configuration Items – You are here
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.

Setting BIOS/UEFI Settings via PowerShell

In Part 1 we covered checking BIOS/UEFI settings, but now we need to fix any systems not configured as we want.

For the Dells we still require the DCPP and you can find details on creating an application from it in Part 1. Once the DCPP is installed, we can call it to configure our UEFI settings. You could call the set-item cmdlet directly, but I opted to wrap it in some additional logic that verifies if a password is required and provided.

Function Set-DellUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Category,
    [Parameter(Position = 1,mandatory = $true)]$Setting,
    [Parameter(Position = 2,mandatory = $true)]$Value,
    [Parameter(Position = 3)]$Password
    )
    $PWReq = (Get-Item -Path DellSmbios:\Security\IsAdminPasswordSet).currentvalue -eq $TRUE
    if($PWReq -and ($password -eq $NULL -or $password -eq "")){Write-error "Password is set but no password was provided"}
    elseif($PWReq -and !($password -eq $NULL -or $password -eq "")){
        Set-Item -Path "DellSmbios:\$($Category)\$($Setting)" $Value -password $password
    }
    else{
        Set-Item -Path "DellSmbios:\$($Category)\$($Setting)" $Value
    }
}

For the HPs we will again use the WMI classes HP provides for us.

Function Set-HPUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value,
    [Parameter(Position = 2)]$Password
    )
    $PWReq = (Get-CIMInstance -Namespace root/hp/instrumentedBIOS -ClassName HP_BIOSSettingInterface) -eq 1
    if($PWReq -and ($password -eq $NULL -or $password -eq "")){Write-error "Password is set but no password was provided"}
    else{
        if($password){$formattedpassword = "<utf-16></utf>$($Password)"} else{$formattedpassword = ""}
        Invoke-CimMethod -InputObject (Get-CimInstance -classname HP_BIOSSettingInterface -namespace root/hp/instrumentedBIOS) -MethodName setbiossetting -Arguments @{ Name = "$($Setting)"; Value = "$($value)"; Password = "$($formattedpassword)" } | out-null
    }
}

Lenovo uses WMI again, but it is slightly different as we have to commit not just the setting but the overall settings. The commit all settings step will not accept a null password if no password is set, you can’t provide the parameter at all.

Function Set-LenovoUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value,
    [Parameter(Position = 2)]$Password
    )
    $PWReq = (get-ciminstance -ClassName Lenovo_BiosPasswordSettings -Namespace root\wmi).PasswordState -eq 1
    if($PWReq -and ($password -eq $NULL -or $password -eq "")){Write-error "Password is set but no password was provided"}
    elseif($PWReq -and $password){
        Invoke-CimMethod -InputObject (Get-CimInstance -classname Lenovo_SetBiosSetting -namespace root\wmi) -MethodName SetBiosSetting -Arguments @{ parameter = "$($Setting),$($Value),$($Password),ascii,us" } | out-null
        Invoke-CimMethod -InputObject (get-ciminstance -classname Lenovo_SaveBiosSettings -namespace root\wmi) -MethodName SaveBiosSettings  -Arguments @{ parameter = "$($Password),ascii,us" } | out-null
    }
    else{
        Invoke-CimMethod -InputObject (Get-CimInstance -classname Lenovo_SetBiosSetting -namespace root\wmi) -MethodName SetBiosSetting -Arguments @{ parameter = "$($Setting),$($Value)" } | out-null
        Invoke-CimMethod -InputObject (get-ciminstance -classname Lenovo_SaveBiosSettings -namespace root\wmi) -MethodName SaveBiosSettings | out-null
    }
}

In all cases, you can immediately re-query the set value and you will see your new setting reflected. A reboot will be required to put the new setting(s) into effect.

Using Configuration Items to Make Use of the Functions

Now that we have our functions worked out, we want to put them to use on our endpoints. The simplest delivery is via Configuration Items, but the issue here is we have no way (without additional code) to enforce a reboot. Luckily, SCCMF12TWICE figured that out and provided the details.

Let’s put the functions and reboot info all together to make our remediation script.

$password = ""

Function Set-LenovoUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value,
    [Parameter(Position = 2)]$Password
    )
    $PWReq = (get-ciminstance -ClassName Lenovo_BiosPasswordSettings -Namespace root\wmi).PasswordState -eq 1
    if($PWReq -and ($password -eq $NULL -or $password -eq "")){Write-error "Password is set but no password was provided"}
    elseif($PWReq -and $password){
        Invoke-CimMethod -InputObject (Get-CimInstance -classname Lenovo_SetBiosSetting -namespace root\wmi) -MethodName SetBiosSetting -Arguments @{ parameter = "$($Setting),$($Value),$($Password),ascii,us" } | out-null
        Invoke-CimMethod -InputObject (get-ciminstance -classname Lenovo_SaveBiosSettings -namespace root\wmi) -MethodName SaveBiosSettings  -Arguments @{ parameter = "$($Password),ascii,us" } | out-null
    }
    else{
        Invoke-CimMethod -InputObject (Get-CimInstance -classname Lenovo_SetBiosSetting -namespace root\wmi) -MethodName SetBiosSetting -Arguments @{ parameter = "$($Setting),$($Value)" } | out-null
        Invoke-CimMethod -InputObject (get-ciminstance -classname Lenovo_SaveBiosSettings -namespace root\wmi) -MethodName SaveBiosSettings | out-null
    }
}

Function Set-HPUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Setting,
    [Parameter(Position = 1,mandatory = $true)]$Value,
    [Parameter(Position = 2)]$Password
    )
    $PWReq = Get-CIMInstance -Namespace root/hp/instrumentedBIOS -ClassName HP_BIOSSettingInterface -eq 1
    if($PWReq -and ($password -eq $NULL -or $password -eq "")){Write-error "Password is set but no password was provided"}
    else{
        if($password){$formattedpassword = "<utf-16></utf>$($Password)"} else{$formattedpassword = ""}
        Invoke-CimMethod -InputObject (Get-CimInstance -classname HP_BIOSSettingInterface -namespace root/hp/instrumentedBIOS) -MethodName setbiossetting -Arguments @{ Name = "$($Setting)"; Value = "$($value)"; Password = "$($formattedpassword)" } | out-null
    }
}

Function Set-DellUEFISetting{
    param(
    [Parameter(Position = 0,mandatory = $true)]$Category,
    [Parameter(Position = 1,mandatory = $true)]$Setting,
    [Parameter(Position = 2,mandatory = $true)]$Value,
    [Parameter(Position = 3)]$Password
    )
    $PWReq = (Get-Item -Path DellSmbios:\Security\IsAdminPasswordSet).currentvalue -eq $TRUE
    if($PWReq -and ($password -eq $NULL -or $password -eq "")){Write-error "Password is set but no password was provided"}
    elseif($PWReq -and !($password -eq $NULL -or $password -eq "")){
        Set-Item -Path "DellSmbios:\$($Category)\$($Setting)" $Value -password $password
    }
    else{
        Set-Item -Path "DellSmbios:\$($Category)\$($Setting)" $Value
    }
}

Function Set-MSUEFISetting{
    param(
    [Parameter(mandatory = $true)]$Setting,
    [Parameter(mandatory = $true)]$Value
    ) 
    $UEFISetting = [Microsoft.Surface.FirmwareOption ]::Find($Setting)
    $UEFISetting.ProposedValue = $Value
}

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

$time = [DateTimeOffset]::Now.ToUnixTimeSeconds()
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'RebootBy' -Value $time -PropertyType QWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'RebootValueInUTC' -Value 1 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'NotifyUI' -Value 1 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'HardReboot' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'OverrideRebootWindowTime' -Value 0 -PropertyType QWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'OverrideRebootWindow' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'PreferredRebootWindowTypes' -Value @("4") -PropertyType MultiString -Force -ea SilentlyContinue;
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\SMS\Mobile Client\Reboot Management\RebootData' -Name 'GraceSeconds' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
Restart-Service ccmexec -force

Of course, we only want the CI to run on systems where we can interact with UEFI, so we want to add a Detection Method. It looks a lot like our compare statements in the discovery and remediation scripts.

$Manufacturer = (get-ciminstance -ClassName Win32_ComputerSystem).Manufacturer
if($Manufacturer -like "Dell*" -and (Get-Module -ListAvailable -Name DellBIOSProvider)){$TRUE}
elseif($Manufacturer -like "Hewlitt*" -or $Manufacturer -like "HP*"){$TRUE}
elseif($Manufacturer -eq "Lenovo"){$TRUE}
Else{}

There are a couple issues with this method. First, it is messy if we need to remediate multiple CIs. Do we want the CM service restarted that many times? Also, when the service restarts during remediation, the evaluation time does not get updated. Not a huge issue, but annoying. The big issue though is UEFI passwords. Most enterprises set them (as they should).

You may have noticed the “$password = “”” at the top of the script and at each set line. We are not going to put a password in the script, not even obfuscated. Granted those that could open the CI to see it likely have access to the endpoints already, but still, it is a no. We will circle back to this topic in a later portion of this series, I just wanted to make sure it is noted here.

Next – Part 3: Using as Applications