Lenovo has provided numerous series of devices, which apparently do not have an aligned standard of handling BIOS / UEFI settings or BIOS / UEFI upgrades. As part of this headache and to ease the burden of jumping between the different device types I started to dive in to harmonize our scripts. As previously written Lenovo has a few odd design decisions – you can for example not reduce security via script. To disable TPM or SecureBoot you would be required to alter this via a keyboard. However, setting the initial password (increasing the security) also requires that you do this by manually typing it into the settings. In addition to the above challenges I reached the following conclusion;
1. It is most likely only relevant to enable any setting.
2. Handling passwords stinks (setting them etc)
3. Settings between different models in a series can vary by name and vary by name of the possible values to set
4. WMI-classes used differs between series (ThinkStation, ThinkPad and ThinkCentre)
For example, ThinkPad has the GetBIOSSelections class, which can present the different possibilities for a specific setting. However, ThinkCentre does not have this class, but instead present options via a [bracket] indicator in the output of the currentsetting.
How would one deal with all this mess?
Two functions are provided – one for figuring our howto enable a specific setting. The other one for setting each setting. Its ugly – as the more models, bios-versions and whatnot I get a hold of the uglier variations of this I find. The initial attempt was to keep it as generic as possible.The best way to explain what I have todo sometimes;
TPM? You can call it one of these: 'TCG*Security*Feature*','Security*Chip*','TCG*Security*Device*'
It can be set to active, but not if the option Enable or Enabled is there. And Security Chip may be one option, however if it is read-only – use TCG Security Feature— Nicke Källén (@znackattack) June 20, 2018
Function Get-LenovoBIOSSettingValue {
param(
[Alias("BIOSSetting")]
[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Array]$Setting
)
begin {
$complex = $null
if(Get-WmiObject -namespace root\wmi -List | where { $_.Name -eq "Lenovo_GetBiosSelections"})
{
$complex = $false
Write-verbose "Lenovo GetBiosSelections does exist"
}
else
{
$complex = $true
Write-verbose "Lenovo GetBiosSelections does not exist"
}
}
process {
foreach ($s in $setting) {
$value = $null
$selections = $null
$findsetting = $null
If ($complex -eq $true) {
$findsetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi | Where-Object {$_.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries) -like $s} | Select-Object CurrentSetting
try {
$value = $findsetting.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries)[0]
}
catch {
# write-output "Unable to convert $s to value"
}
$r = [regex] '(?<=\[).+?(?=\])'
try {
[string]$checkvalue = $findsetting.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries)[1].split(";",[StringSplitOptions]::RemoveEmptyEntries)[0]
$appliedselection = $checkvalue
if ([string]$($findsetting.CurrentSetting.split(";",[StringSplitOptions]::RemoveEmptyEntries)[1]) -Match 'Status:ShowOnly') {
if($checkvalue -like "Active*" -or $checkvalue -like "Enabl*" -or $checkvalue -like 'Discrete*TPM*' )
{
if ($value -like 'Security*Chip*') {
$value = $null
$selections = $null
$findsetting = $null
}
}
else {
$value = $null
$selections = $null
$findsetting = $null
}
}
}
catch {
#noerror
}
$selections = ($r.match($findsetting.currentsetting).value).split(":").split(",") | Where-Object {$_ -like "Active*" -or $_ -like "Enabl*" -or $_ -like 'Discrete*TPM*' }
if ($selections -is [array] -and $selections -contains 'Enable') {
$selections = $selections| Where-Object { $_ -like "Enabl*" }
}
}
else {
try {
$findsetting = Get-WmiObject -Class Lenovo_BiosSetting -Namespace root\WMI | Where-Object {$_.CurrentSetting -match $s} | Select-Object CurrentSetting
$value = $findsetting.currentsetting.split(",")[0]
$appliedselection = $findsetting.currentsetting.split(",")[1]
}
catch {
#write-output "Unable to convert $s to value"
}
$selections = ((gwmi –class Lenovo_GetBiosSelections –namespace root\wmi).GetBiosSelections($value).selections).split(",") | Where-Object {$_ -like "Active*" -or $_ -like "Enabl*" }
if ($selections -is [array] -and $selections -contains 'Enable') {
$selections = $selections| Where-Object { $_ -like "Enabl*" }
}
}
if ($value -ne $null -and $selections -ne $null) {
$object = New-Object –TypeName PSObject
$object | Add-Member –MemberType NoteProperty –Name Setting –Value $($value)
$object | Add-Member –MemberType NoteProperty –Name Current –Value $($appliedselection)
$object | Add-Member –MemberType NoteProperty –Name Selection –Value $($selections)
$object
#write-output "$s provided $value, which can be set to $selections"
}
}
}
}
Function Set-LenovoBIOSSetting {
param(
[Alias("BIOSSetting")]
[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Array]$Setting,
[Parameter(Position = 1, Mandatory = $false)]
[String]$Password
)
begin {
$wmi = Get-WmiObject -Class Lenovo_SetBiosSetting -Namespace root\wmi -ErrorAction Stop
$object = $null
if ($object) {
Remove-Variable -Name $object
}
#$setting
}
process {
foreach ($s in $setting) {
#$s
$result = $null
if ($s.Current -ne $s.Selection) {
$result = ($wmi.SetBiosSetting("$($s.setting),$($s.selection)$password")).return
$object = New-Object –TypeName PSObject
$object | Add-Member –MemberType NoteProperty –Name Setting –Value $($s.setting)
$object | Add-Member –MemberType NoteProperty –Name Current –Value $($s.selection)
$object | Add-Member –MemberType NoteProperty –Name Result –Value $result
$object
}
else {
$object = New-Object –TypeName PSObject
$object | Add-Member –MemberType NoteProperty –Name Setting –Value $($s.setting)
$object | Add-Member –MemberType NoteProperty –Name Current –Value $($s.Current)
$object | Add-Member –MemberType NoteProperty –Name Result –Value 'Not updated'
$object
}
}
}
}