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 } } } }