:::: MENU ::::

Deploy Lenovo BIOS Settings

Background

When it comes to BIOS settings deployments, you are typically going to be at the mercy of the PC manufacturer. For most Dell shops, this isn’t a problem because Dell has created a “Client Configuration Tool Kit” (CCTK) which can be used to deploy BIOS settings en masse. Intel’s answer to this problem is “Active Management Technology” (AMT) for vPro enabled chips. If you are fortunate enough to have an environment which is comprised of these AMT capable computers, it is possible to deploy BIOS settings using SCCM or something similar, regardless of make or model.

But this post isn’t about Dell or AMT, it’s about a problem I’ve encountered when trying to deploy Lenovo BIOS settings. Unlike Dell, Lenovo has not made a client configuration utility. Apparently, when it comes to Lenovo systems, you have to script the BIOS settings using something like VBS or Powershell. Now, to Lenovo’s credit, they’ve provided some documentation on how to do this. In essence, there’s a lenovo_biossetting WMI object which has a setbiossetting method that Lenovo expects you to use. You are supposed to feed the setbiossetting method strings of text, with each string representing a configurable BIOS setting.

Problem

The problem is that although these strings of values are documented, these values differ between models of Lenovo computers; and some settings that are not of the simple enable\disable\Boolean type are not documented well enough. If you were to, say, mistype the boot order and feed it to the setbiossetting, it would simply fail. So how are you supposed format the strings correctly even when there’s no documentation at all?

Solution

The crux of this solution is to simply let the computer format your strings for you. This can be done by first manually configuring the BIOS on a test Lenovo PC, then pulling those settings from WMI, saving them to a text file. On the text file, all of the BIOS settings for this particular model of Lenovo PC are written down with proper formatting.

For your convenience, I have created two PowerShell Functions to make this method even easier: Get_LenovoBIOSSettings and Set_LenovoBIOSSetings.

To deploy Lenovo BIOS settings:

  1. Configure the BIOS Settings by hand on a test Lenovo computer, reboot when finished.
  2. Use Get_LenovoBIOSSettings to dump all of the available BIOS settings on that test computer to a text file.

Get-LenovoBIOSSettings -ComputerName 'Test-Lenovo-PC'

  1. Open the text file named “GetLenovoBIOSSettings_(%DATE%).log” that is located under C:\windows\logs and copy the BIOS settings you would like to deploy.

LogFile

  1. Each setting on the log file is in its own line. Copy each line\setting that is of interest, and add it as a string to a list in the deployment script. Send the list to the Set_LenovoBIOSSettings function.

In the example below, the only settings to deploy are the PXE boot setting and the BIOS flash setting. The first 165 or so lines are just the set_lenovobiossettings function copied to the top of the script. And the bulk of that is just for logging and help text. It’s the last 5 lines that actually make use of the function and apply the settings of interest. To get this running quickly, just edit line #167 with your choice of settings. Also, change the computername on 170 to the name of the target machine i.e. instead of “lenovo-pc” make it: “pc-lab1”.

Function Set-LenovoBIOSSettings {
[CmdletBinding()]
    param(
    [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=0)]
    [string]$ComputerName = (Get-Content env:computername),
    [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=1)]
    $SettingsToBeApplied)

#Defining Write-Log and Create-LogFile within the Function, to make logging easier.
    Function Create-LogFile{
    [CmdletBinding()]
        param([Parameter(Mandatory=$true, ValueFromPipeline=$true,Position=0)]
        [string]$LogName,
        [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=1)]
        [string]$LogfileVarName = 'LogFile',
        [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=2)]
        [string]$LogFileScope = 'Global')

        $LogFileString = "c:\windows\logs\$($LogName)_$(get-date -UFormat %m-%d-%y).log"
        if (Test-Path $LogFileString){Remove-Item -Path $LogFileString -Force}
        Set-Variable -Name $LogfileVarName -Value $LogFileString -Scope $LogFileScope -Force}

    Function Write-Log {
    [CmdletBinding()]
        param(
        [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=0)]
        [string]$message,
        [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=1)]
        [string]$LogName = "REPLACE",
        [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=2)]
        [string]$LogfileVarName = 'LogFile',
        [Parameter(Mandatory=$false, ValueFromPipeline=$true,Position=3)]
        [string]$LogFileScope = 'Global')

    #The Messaging Script Block, to be executed only if $LogFile exists.
    $MessageScriptBlock = {
    # Call Write-Log followed by a qouted message to log it in the log file and display it on the screen.
    #Some output will not display on the host screen, but will display on the log file.
    if ($message.Length -eq 0){
        Write-Host " "
        " "| Out-File -FilePath $LogFile -Force -Append; Return}
    if ($message -ne $null) {
        $message = "--"+ $message + " - $(Get-Date -Format "hh:mm:ss tt")--"
        Write-Host $message
        $message | Out-File -FilePath $LogFile -Force -Append}}

    if ((Get-Variable $LogfileVarName) -eq $null) {
        if ((Get-Command -Name Create-LogFile) -ne $null) {Create-LogFile -LogName $LogName}
        else {
            Write-Host "There is no log file nor is there a `"Create-LogFile`" Function available"
            Write-Host "Creating a Logfile to continue the script: c:\windows\logs\REPLACE.log"
            $LogFileString = "c:\windows\logs\REPLACE.log"
            if (Test-Path $LogFileString){Remove-Item -Path $LogFileString -Force}
                Set-Variable -Name $LogfileVarName -Value $LogFileString -Scope $LogFileScope -Force}}

        Invoke-Command -ScriptBlock $MessageScriptBlock}


#Creating the logfile below:
Create-LogFile -LogName SetLenovoBiosSettings

Write-Log "Updating the Bios Settings"
Write-Log "Start of Script. Date: $(get-date -UFormat %m-%d-%y)"
Write-Log "The original location of this log file is: $LogFile and this file was run on this computer: $ComputerName"

#Checking PowerShell Version
Write-Log "Here is the current running version of PowerShell:"
$PSVersionTable
#$PSVersionTable | Out-File -FilePath $LogFile -Append

if ($PSVersionTable.PSVersion.Major -lt 4) {Write-Log "Warning, this script has only been tested on PowerShell 4."}
Write-Log
Write-Log
Write-Log "Here is the input parameter: `$SettingsToBeApplied: `"$SettingsToBeApplied`""


#Checking the ComputerName Parameter, before proceeding.
if (!(Test-Connection $ComputerName -Quiet)) {Write-Log "The computer $ComputerName is not pingable, make sure that it is on.";Break}

#Just making sure that the computer used is a Lenovo. If it's not, Break the script.
if ((Get-WmiObject win32_computersystem -ComputerName $ComputerName).Manufacturer -notmatch "LENOVO") {Write-Log "This computer is not a Lenovo, Breaking now";Break}

#Instantiating an instance of the lenovo_BIOSsetting wmiobject as a variable. This will be an instance of the currently running config of the BIOS.
Set-Variable -Name CurrentBIOSSettings -Value (Get-WmiObject lenovo_BIOSsetting -Namespace 'Root\wmi'-ComputerName $ComputerName) -Scope Global

#This allows us to declare\set BIOS settings on an instance of the lenovo_setBIOSsetting wmi object, which can later be applied\saved to the BIOS.
if ((Get-Variable -Name BIOSSettings) -eq $null) {Set-Variable -Name BIOSSettings -Value (Get-WmiObject lenovo_setBIOSsetting -namespace root\wmi -ComputerName $ComputerName) -Scope Global}

Write-Log "There is a total of $($SettingsToBeApplied.Count) setting(s) to be configured:"
$index = 1
Write-Log
Write-Log

$SetBIOSSettingScriptBlock = {
    param([string]$SettingToBeApplied)
    
    <# 
        $SetBIOSSettingScriptBlock is defined to apply only one Bios Setting at a time. But because the parent Set_LenovoBIOSSettings function is designed to handle both single Settings,
    and lists of settings, this script block will be used later on in an if\else block where this script block will be used normally for a single setting, but will be used in a
    foreach loop in case the function is fed a list of settings\strings.
    
    #>


    #Grabbing the Setting Name from the string which contains both the value and the name to make querying simpler, as we do not know what the value currently is.
    $SettingName = $SettingToBeApplied.split(",")[0]
    Write-Log
    Write-Log
    Write-Log
    Write-Log
    Write-Log "$index. Configuring $SettingName"
    Set-Variable -Name index -Value ($index += 1) -Scope 1
    
    #Saving the value currently applied to this BIOS setting to a variable for easier use.
    $CurrentBIOSSetting = $CurrentBIOSSettings | where {($_.currentsetting).split(",")[0] -eq $SettingName} | Select-Object -Property CurrentSetting

    #Checking the $SettingToBeApplied parameter.
    Write-Log "Checking the `$SettingName variable, which is: `"$SettingName`", to make sure that the setting exists in the BIOS."
    if ($CurrentBIOSSetting -eq $null) {Write-Log "The BIOS does not contain $SettingName as one of it's configurable BIOS Settings.";Return}

    #Logging the current value for this setting.
    Write-Log "Here is the currently running configuration for this setting:"
    $CurrentBIOSSetting | Out-File -FilePath $LogFile -Append -Force
 
    #If the CurrentBIOSSetting is different than what we want it to be, 
    if ($CurrentBIOSSetting.CurrentSetting -ne $SettingToBeApplied) {
        Write-Log "Trying to apply the Setting: $SettingToBeApplied now."
        $SuccessCode = $BIOSSettings.SetBIOSSetting($SettingToBeApplied).return
        
        #If the setting does not apply to this machine, or if the values are not correctly specified, then the success code would be equivalent to "Invalid Parameter".
        if ($SuccessCode -eq "Invalid Parameter") {Write-Log "The setting: `"$SettingToBeApplied`" is not properly configured for this Machine\BIOS version."}
        
        #If the setting applied successfully, the success code would be equivalent to "Success"
        elseif ($SuccessCode -eq "Success") {
            Write-Log "Successfully applied the setting, below is the newly configured value for this particluar setting:"
            
            #Grabing the latest instance of the BIOS setting object to log it's new value for confirmation\troubleshooting.
            Get-WmiObject lenovo_BIOSsetting -Namespace 'Root\wmi' -ComputerName $ComputerName | where {$($_.CurrentSetting.split(',')[0]) -eq $SettingName} | Select-Object -Property CurrentSetting | Out-File -FilePath $LogFile -Append}}
    
    #If the setting to be applied is the same as the current running config of the bios, log it.
    if ($CurrentBIOSSetting.CurrentSetting -eq $SettingToBeApplied) {
            Write-Log "The BIOS is already configured correctly. Below is the current values for both variables:"
            Write-Log "`"`$CurrentBIOSSetting.currentSetting`" is:"
            Write-Log
            $CurrentBIOSSetting.CurrentSetting | Out-File -FilePath $LogFile -Append -Force
            Write-Log
            Write-Log "`"`$SettingToBeApplied`" is:"
            Write-Log
            $SettingToBeApplied | Out-File -FilePath $LogFile -Append -Force}
            ((Get-WmiObject lenovo_savebiossettings -ComputerName $ComputerName -Namespace root\wmi).savebiossettings()).return}

if (($SettingsToBeApplied -isnot [string]) -and ($SettingsToBeApplied -isnot [array])) {$SettingsToBeApplied = $SettingsToBeApplied.tostring()}
if (($SettingsToBeApplied -is [string]) -and ($SettingsToBeApplied -match ",")) {Invoke-Command -ScriptBlock $SetBIOSSettingScriptBlock -ArgumentList $SettingsToBeApplied}
if ($SettingsToBeApplied -is [array]) {
    
    foreach ($SettingToBeApplied in $SettingsToBeApplied) {
        #Below is just some more error checking, making sure the input is correct
        if (($SettingToBeApplied -isnot [string]) -or (($SettingToBeApplied -is [string]) -and ($SettingsToBeApplied -notmatch ","))) {
        Write-Log "Each object defined in the array  as the SettingsToBeApplied paramter must be a string containing at least one comma to differentiate a setting name from it's corresponding value."
        Write-Log "The object: $SettingToBeApplied which is found at index#$($SettingsToBeApplied.IndexOf($SettingToBeApplied)) of the `"$SettingsToBeApplied`" fails meet the criteria. "}
        #Applying the BIOS Settings below.
        elseif (($SettingToBeApplied -is [string]) -and ($SettingToBeApplied -match ",")) {Invoke-Command -ScriptBlock $SetBIOSSettingScriptBlock -ArgumentList $SettingToBeApplied}}}}



#Copy each setting from the get-lenovobiossetting log file and add to this list:
$Settings = 'Allow Flashing BIOS to a Previous Version,Yes;[Optional:No,Yes]','Boot Agent,PXE;[Optional:Disabled,PXE]'

#Now, you may feed this list to Set-LenovoBIOSSettings.
Set-LenovoBIOSSettings -ComputerName 'Lenovo-PC' -SettingsToBeApplied $Settings

NOTE: It’s important to note that the values you find may only apply to specific models. For example, the settings in the sample above might only work for a T440s and not for an M93p. So you may have to use if\then conditionals, applying the list of values dependent upon the model of Lenovo PC. Or keep track of different .ps1 scripts, running them on a per model basis.

btw, this assumes no password is set on the bios.

Conclusion

So, to reiterate, use Get-LenovoBIOSSettings to discover the proper format of all BIOS settings on a properly configured Lenovo PC. On the deployment script, create a list containing some of those settings and send this list to the Set-LenovoBIOSSettings function. Leave comments below if you have any questions.


Script(s) Used in This Post:

Get_LenovoBIOSSettings

Set_LenovoBIOSSetings



Subscribe
Notify of

15 Comments
Inline Feedbacks
View all comments

Sorry I’m a noob to powershell, cannot believe this is the method Lenovo wants you to use – mind boggles…
Anyway, I don’t understand your explanation above, do I need to create a ps script PS1 file to run on the pc to both get and set the BIOS? I’m sorry your instructions presume a bit of fore-knowledge, please clarify.

I have seen lately that there is a new tool that Lenovo is starting to promote to configure BIOS changes. http://thinkdeploy.blogspot.com/2016/08/the-think-bios-config-tool.html The tool download is at the bottom of the blog post.

I am having a slight issue. The script works well, but I can;t get it to disable the option booting from a cd. It allows me to disable usb cd/dvd boot drives. but I cant find the setting to keep regular SATA cd drive from being in the boot list.

Question: how can I run it with Supervisor password enabled. I know th epassword is there a string I can add to run Set-LenovoBiosSettings to include the pass?

Hi

I am currently struggling to change the boot order on roughly 4k lenovo all in one machines. Is there a way to utilize this for multiple computers using a csv file perhaps? I have used the think bios config tool and it only allows one computer at a time… any feedback would be awesome

yes when the computers are not pingable the script ends. I wonder if there is a way to have the script overlook that?? i am looking into running it through gpo or other options like you recommended. I work at a school and we have some computers that do not have bios passwords set… so you can imagine what we have been dealing with haha

pdq deploy is clutch… Sorry I am a student as well as new to system administration so I ask alot of questions! However I learn a significant amount of information from websites such as yours as well as youtube etc… Thank for your advise!

I know its a really old post but the link to “https://adameyob.com/works/get-lenovo-bios-settings/” is broken any way getting this fixed :-)
Really helpful posts!!!

Hi, glad to have found this tool. Have not tried it, so looking forward to testing it. I plan to use this in SCCM and RES Automation so I can push it remotely. That said, how will this work since it relies on appending a computer name as a parameter?