Lenovo BIOS Manipulation: Set-LenovoBIOSSetting

Changing a BIOS setting on a Lenovo, is a two step process:

  • Change the setting(s) in question
  • Commit (or Save) the all of your changes

Explanation

The script supports execution against remote systems to silently and easily toggle various BIOS settings.

By default the script relies on Get-LenovoBIOSSetting for validation purposes:

  1. It confirms the Setting you want to change is a valid setting.
  2. It confirms the count of the results returned from the previous command is equal to 1.
    This is done to ensure you specified an explicit setting like ‘Boot Up Num-Lock Status’ and not something generic like ‘Boot%’ which could return ‘Boot Agent’, ‘Boot Up Num-Lock Status’, ‘BootMode’, ‘BootOrder’ and so on.
  3. Finally, it will attempt to validate the Value you specified by evaluating the ‘OptionalValue’s returned by query.

Once it passes validation – or you bypass it – it then proceeds.

However there is a safety net and that is the requirement of the -Commit parameter to actually flip the bits.  If the -Commit parameter isn’t included, which is the default, it doesn’t make any changes.

Function Set-LenovoBIOSSetting


#region Function Set-LenovoBIOSSetting
Function Set-LenovoBIOSSetting
    {
        [cmdletbinding(SupportsShouldProcess=$True)]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string[]]$ComputerName = $env:computername,

                [Parameter(Mandatory=$true)]
                    [string]$Setting,

                [Parameter(Mandatory=$true)]
                [Alias('NewValue')]
                    [string]$Value,

                [Parameter(Mandatory=$false)]
                    [switch]$SkipCheck = $false,

                [Parameter(Mandatory=$false)]
                    [switch]$Commit
            )

        Begin { $NewValue = $Value }

        Process
            {
                foreach($Computer in $ComputerName)
                    {
                        if($SkipCheck -eq $false)
                            {
                                # Validate Setting - Part 1: Ensure this is a legitimate settng in the BIOS
                                $CurrentSettings = Get-LenovoBIOSSetting -ComputerName $Computer -Setting $Setting
                                if([string]::IsNullOrEmpty($CurrentSettings)) { Write-Output "ERROR INVALID SETTING SPECIFIED: [$Setting]"; throw }

                                # Validate Setting - Part 2: Ensure an 'exact' BIOS setting was specified like 'USB Port 1' not 'USB%' which could match several.
                                if(@($CurrentSettings).Count -gt 1) { Write-Output "ERROR SETTING [$Setting] RETURNED TOO MANY MATCHES: [$($CurrentSettings.Setting -join ', ')]"; throw }

                                # Validate New Value - If the OptionalValue isn't 'N/A' then we can verify the user supplied
                                if($CurrentSettings.OptionalValue -ne 'N/A')
                                    {
                                        if($($CurrentSettings.OptionalValue -split ',') -cnotcontains $Value) { Write-Output "ERROR: INVALID VALUE SPECIFIED [$Value] - MUST BE ONE OF: [$($CurrentSettings.OptionalValue)]"; throw }
                                    }

                                if($CurrentSettings.CurrentValue -eq $NewValue) { Write-Output "No change made for [$Setting] on [$Computer] because new and current values are identical:`r`nCurrent Value: [$($CurrentSettings.CurrentValue)]`r`nUpdated Value: [$NewValue]"; return }

                                $Action = "from [$($CurrentSettings.CurrentValue)] to [$NewValue]"
                            }
                        else { $Action = "to [$NewValue]" }

                        if($PSCmdlet.ShouldProcess("Setting [$Setting] on [$Computer] $Action"))
                            {
                                If($Commit -eq $true)
                                    {
                                        $OriginalEAP = $ErrorActionPreference
                                        $ErrorActionPreference = 'Stop'

                                        Try { $SetResult = (gwmi -class Lenovo_SetBiosSetting -namespace root\wmi -ComputerName $Computer -ErrorAction Stop).SetBiosSetting("$Setting,$NewValue") }
                                        Catch { Write-Output "ERROR: FAILED TO SET BIOS SETTING [$Setting] TO [$NewValue] ON [$Computer]: $_"; throw }

                                        $ErrorActionPreference = $OriginalEAP

                                        Switch($SetResult.return)
                                            {
                                                'Success' { Write-Output "$($SetResult.Return)fully set [$Setting] on [$Computer] $Action" }
                                                default
                                                    {
                                                        Write-Output "ERROR: FAILED TO SET [$Setting] TO [$NewValue] ON [$Computer]: [$($SetResult.Return)]"
                                                        if($Firmware -eq 'UEFI') { Write-Output "HINT: Firmware is UEFI [$Firmware] and you //may// first need to disable either 'OS Optimized Defaults' or 'SecureBoot' as these lock certain settings in the BIOS." }
                                                        throw
                                                    }
                                            }
                                    }
                                Else
                                    {
                                        Write-Output "WARNING: Commit is false [$Commit]; NOT Changing BIOS Setting [$Setting] on [$Computer] $Action!"
                                    }
                            }
                    }
            }

        End {  }
    }
#endregion Function Set-LenovoBIOSSetting

One Final Tidbit: Save-LenovoBIOSSettings

This one is important and I intentionally put this down here at the bottom.  Like I mentioned in the beginning, making BIOS change is a two step process, and so far we’ve only done one of the two.

The final step is to save the changes so that they’re committed.  Yes, I realize I’m using the parameter -Commit above which may be a little confusing but that’s just to make sure you don’t accidentally shoot yourself in the foot: write a change you didn’t mean to write and then forget what you had previously, so you struggle to get back to where you were before.

I intentionally didn’t include the ‘save’ code in the function above because I prefer to set all the changes first:

  • Switch to UEFI via ‘Boot Mode’ and ‘Boot Priority’ or ‘SecureBoot’
  • Set boot device order
  • Enable/Disable USB ports
  • Enable/Disable Bluetooth
  • Enable/Disable IPv4NetworkStack and/or IPv6NetworkStack
  • And so on…

Once all the changes are staged and no errors were encountered, I save the settings once via a call to a function which also requires the -Commit parameter to help prevent accidental foot shooting:


#region Function Set-LenovoBIOSSetting
Function Set-LenovoBIOSSetting
    {
        [cmdletbinding(SupportsShouldProcess=$True)]
        Param
            (
                [Parameter(Mandatory=$false)]
                    [string[]]$ComputerName = $env:computername,

                [Parameter(Mandatory=$true)]
                    [string]$Setting,

                [Parameter(Mandatory=$true)]
                [Alias('NewValue')]
                    [string]$Value,

                [Parameter(Mandatory=$false)]
                    [switch]$SkipCheck = $false,

                [Parameter(Mandatory=$false)]
                    [switch]$Commit
            )

        Begin { $NewValue = $Value }

        Process
            {
                foreach($Computer in $ComputerName)
                    {
                        if($SkipCheck -eq $false)
                            {
                                # Validate Setting - Part 1: Ensure this is a legitimate settng in the BIOS
                                $CurrentSettings = Get-LenovoBIOSSetting -ComputerName $Computer -Setting $Setting
                                if([string]::IsNullOrEmpty($CurrentSettings)) { Write-Output "ERROR INVALID SETTING SPECIFIED: [$Setting]"; throw }

                                # Validate Setting - Part 2: Ensure an 'exact' BIOS setting was specified like 'USB Port 1' not 'USB%' which could match several.
                                if(@($CurrentSettings).Count -gt 1) { Write-Output "ERROR SETTING [$Setting] RETURNED TOO MANY MATCHES: [$($CurrentSettings.Setting -join ', ')]"; throw }

                                # Validate New Value - If the OptionalValue isn't 'N/A' then we can verify the user supplied
                                if($CurrentSettings.OptionalValue -ne 'N/A')
                                    {
                                        if($($CurrentSettings.OptionalValue -split ',') -cnotcontains $Value) { Write-Output "ERROR: INVALID VALUE SPECIFIED [$Value] - MUST BE ONE OF: [$($CurrentSettings.OptionalValue)]"; throw }
                                    }

                                if($CurrentSettings.CurrentValue -eq $NewValue) { Write-Output "No change made for [$Setting] on [$Computer] because new and current values are identical:`r`nCurrent Value: [$($CurrentSettings.CurrentValue)]`r`nUpdated Value: [$NewValue]"; return }

                                $Action = "from [$($CurrentSettings.CurrentValue)] to [$NewValue]"
                            }
                        else { $Action = "to [$NewValue]" }

                        if($PSCmdlet.ShouldProcess("Setting [$Setting] on [$Computer] $Action"))
                            {
                                If($Commit -eq $true)
                                    {
                                        $OriginalEAP = $ErrorActionPreference
                                        $ErrorActionPreference = 'Stop'

                                        Try { $SetResult = (gwmi -class Lenovo_SetBiosSetting -namespace root\wmi -ComputerName $Computer -ErrorAction Stop).SetBiosSetting("$Setting,$NewValue") }
                                        Catch { Write-Output "ERROR: FAILED TO SET BIOS SETTING [$Setting] TO [$NewValue] ON [$Computer]: $_"; throw }

                                        $ErrorActionPreference = $OriginalEAP

                                        Switch($SetResult.return)
                                            {
                                                'Success' { Write-Output "$($SetResult.Return)fully set [$Setting] on [$Computer] $Action" }
                                                default
                                                    {
                                                        Write-Output "ERROR: FAILED TO SET [$Setting] TO [$NewValue] ON [$Computer]: [$($SetResult.Return)]"
                                                        if($Firmware -eq 'UEFI') { Write-Output "HINT: Firmware is UEFI [$Firmware] and you //may// first need to disable either 'OS Optimized Defaults' or 'SecureBoot' as these lock certain settings in the BIOS." }
                                                        throw
                                                    }
                                            }
                                    }
                                Else
                                    {
                                        Write-Output "WARNING: Commit is false [$Commit]; NOT Changing BIOS Setting [$Setting] on [$Computer] $Action!"
                                    }
                            }
                    }
            }

        End {  }
    }
#endregion Function Set-LenovoBIOSSetting

Obviously you’re welcome to do as you wish in your environment, even roll the execution of the WMI method to the Set-LenovoBIOSSettings function.

Just be safe and sure of what you’re doing 🙂

Good Providence to you as you reconfigure your BIOS’!

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s