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:
- Configure the BIOS Settings by hand on a test Lenovo computer, reboot when finished.
- 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'
- 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.
- 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.
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.
hey naz I plan on re-writing this article and cleaning up the code as best I can. I slapped this together quickly and I feel its a bit overengineered. Though it works fine and has worked fine for me for as long as I’ve used it. Yes this presumes knowledge of PS. Its just a function that you use in a script. I recently discovered this tool: http://support.lenovo.com/us/en/downloads/ds014169 I suggest you try this first before venturing into wmi. It may just work for you, though I have yet to encounter any tool from Lenovo that actually works 100% Things like… Read more »
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.
Thats great, and about time! Hopefully it actually works. As I’ve said above, every single tool I’ve used from Lenovo has had problems\is buggy. Even their firmware upgrade packages are all done in different ways. No standardization at all. lmk how it works out for you. It could prove useful for password protected bioses.
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.
If you want to change the boot order: go to the target pc and set it manually. Then use get_lenovobiosesettings to get setting exactly as it should be written down. Then edit the script with that setting. If you are talking about a different setting that just outright disables booting from cds, the process is the same. You have to read thru the log and recognize the setting that you need. sometimes the strings of text are not very intuitive. Finally, there are some settings that are not remotely administrable, such as the bios password. This really comes down to… Read more »
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?
There is a parameter on one of the objects. It’s been years since I have configured this. I am sure it’s just a matter of throwing the parameter in one of the objects\cmdlets in the script. Keep in mind that your password would be in clear text, in the script itself. There are ways to input secure strings in ps, but the only way to guarantee a some level of confidentiality is to use a machine key. Problem with that is that the key is tied to the machine itself, so the script won’t execute properly on remote machines. Furthermore,… Read more »
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
NVM, I misread your question. The answer is yes, you can use a csv file. You can use Import-Csv and select the column of computer names. Then you can just iterate through that list with a foreach loop, calling set_lenovobiossettings for each name. All of this would be replace line 170. Something like : $pcnames = Import-Csv c:\pcnames.csv foreach ($name in $pcnames) {Set-LenovoBIOSSettings -ComputerName $name.ColumnName -SettingsToBeApplied $Settings} where ColumnName is the name of the column that holds the pc names. Problem though is that this is not the best way to run scripts, as some machines may be off, asleep,… Read more »
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!!!
The links are fixed. Thanks for letting me know.
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?