BIOS

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’!

Advertisements

Lenovo BIOS Manipulation: Get-LenovoBIOSSetting

First An Apology

I owe you an apology because I forgot to follow up: A year ago I posted about getting ‘pretty’ BIOS data via PowerShell+WMI but never followed up with a function/script; which I’ve been using for sometime now.  I lost sight of this so sorry about that.

Explanation

There really isn’t much to this: Execute Get-BIOSSetting without any parameters to pull ALL the BIOS settings from the current machine.

If you’re on a ThinkPad Laptop (T440 through T470, X240 through X270) it’ll output data like this


ComputerName    Setting                             CurrentValue                          OptionalValue
------------    -------                             ------------                          -------------
Leno-T470s AdaptiveThermalManagementAC         MaximizePerformance                   N/A
Leno-T470s AdaptiveThermalManagementBattery    Balanced                              N/A
Leno-T470s AlwaysOnUSB                         Enable                                N/A
Leno-T470s AMTControl                          Enable                                N/A
Leno-T470s BIOSPasswordAtBootDeviceList        Disable                               N/A
Leno-T470s BIOSPasswordAtReboot                Disable                               N/A
Leno-T470s BIOSPasswordAtUnattendedBoot        Enable                                N/A
Leno-T470s BIOSUpdateByEndUsers                Enable                                N/A
Leno-T470s BluetoothAccess                     Enable                                N/A
Leno-T470s BootDeviceListF12Option             Enable                                N/A
Leno-T470s BootDisplayDevice                   LCD                                   N/A
Leno-T470s BootMode                            Quick                                 N/A
Leno-T470s BootOrder                           USBCD:USBFDD:NVMe0:HDD0:USBHDD:PCILAN N/A
...
Leno-T470s USBPortAccess                       Enable                                N/A
Leno-T470s VirtualizationTechnology            Disable                               N/A
Leno-T470s VTdFeature                          Disable                               N/A
Leno-T470s WakeByThunderbolt                   Enable                                N/A
Leno-T470s WakeOnLAN                           ACOnly                                N/A
Leno-T470s WakeOnLANDock                       Enable                                N/A
Leno-T470s WiGig                               Enable                                N/A
Leno-T470s WiGigWake                           Disable                               N/A
Leno-T470s WindowsUEFIFirmwareUpdate           Enable                                N/A
Leno-T470s WirelessAutoDisconnection           Disable                               N/A
Leno-T470s WirelessLANAccess                   Enable                                N/A
Leno-T470s WirelessWANAccess                   Enable                                N/A

However if you’re on a ThinkCentre, like a M93, M900, M910 it’ll output something like this


ComputerName    Setting                                   CurrentValue                                                                                                              OptionalValue
------------    -------                                   ------------                                                                                                              -------------
Leno-M910 After Power Loss                          Last State                                                                                                                      Power On,Power Off,Last State
Leno-M910 Alarm Date(MM/DD/YYYY)                    [01/01/2016][Status:ShowOnly]                                                                                                   N/A
Leno-M910 Alarm Day of Week                         Sunday                                                                                                                          Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,SaturdayStatus:ShowOnly
Leno-M910 Alarm Time(HH:MM:SS)                      [00:00:00][Status:ShowOnly]                                                                                                     N/A
Leno-M910 Allow Flashing BIOS to a Previous Version Yes                                                                                                                             No,Yes
Leno-M910 Automatic Boot Sequence                   Network 1:M.2 Drive 1:PCIE4X_1 Drive:PCIE16X_1 Drive:SATA 1:SATA 2:SATA 3:SATA 4:Other Device                                   Network 2:Network 3:Network 4:USB FDD:USB HDD:USB CDROM:USB KEY
Leno-M910 Boot Agent                                PXE                                                                                                                             Disabled,PXE
Leno-M910 Boot Up Num-Lock Status                   On                                                                                                                              Off,On
...
Leno-M910 User Defined Alarm Time                   [00:00:00][Status:ShowOnly]                                                                                                     N/A
Leno-M910 VT-d                                      Enabled                                                                                                                         Disabled,Enabled
Leno-M910 Wake from Serial Port Ring                Primary                                                                                                                         Primary,Automatic,Disabled
Leno-M910 Wake on LAN                               Automatic                                                                                                                       Primary,Automatic,Disabled
Leno-M910 Wake Up on Alarm                          Disabled                                                                                                                        Single Event,Daily Event,Weekly Event,Disabled,User Defined
Leno-M910 Wednesday                                 Disabled                                                                                                                        Disabled,EnabledStatus:ShowOnly
Leno-M910 Windows UEFI Firmware Update              Enabled                                                                                                                         Disabled,Enabled

I like that the Desktop’s provide valid configuration settings and I hope this is something that will one day make it over to the laptops.

My two favorite parts about this script:

  • Supports querying remote systems
  • Supports querying for all, single or multiple specific settings

The script isn’t perfect but it works for our needs on our systems but I’m interested in hearing from you.

Function: Get-LenovoBIOSSetting

Usage

Get-LenovoBIOSSetting

Get-LenovoBIOSSetting -Setting Wi%

Get-LenovoBIOSSetting -ComputerName Leno-T470 -Setting 'Fingerprint%'

Get-LenovoBIOSSetting -ComputerName Leno-M900,Leno-M910 -Setting 'USB%','C State Support','Intel(R) Virtualization Technology'

Full code below


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

                [Parameter(Mandatory=$false)]
                    [string[]]$Setting
            )

        Begin { [System.Collections.ArrayList]$BIOSSetting = @() }

        Process
            {
                foreach($Computer in $ComputerName)
                    {
                        if($Setting)
                            {
                                Foreach($Item in $Setting)
                                    {
                                        Try
                                            {
                                                $arrCurrentBIOSSetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi -Filter "CurrentSetting like '$Item,%'" -ComputerName $Computer -ErrorAction Stop | ? { $_.CurrentSetting -ne "" } | Select -ExpandProperty CurrentSetting
                                                Foreach($CurrentBIOSSetting in $arrCurrentBIOSSetting)
                                                    {
                                                        [string]$CurrentItem = $CurrentBIOSSetting.SubString(0,$($CurrentBIOSSetting.IndexOf(',')))
                                                
                                                        if($CurrentBIOSSetting.IndexOf(';') -gt 0) { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1),$CurrentBIOSSetting.IndexOf(';')-$($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                        else { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                
                                                        if($CurrentBIOSSetting.IndexOf(';') -gt 0)
                                                            {
                                                                [string]$OptionalValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(';')+1))
                                                                [string]$OptionalValue = $OptionalValue.Replace('[','').Replace(']','').Replace('Optional:','').Replace('Excluded from boot order:','')
                                                            }
                                                        Else { [string]$OptionalValue = 'N/A' }
                                                                                
                                                        $BIOSSetting += [pscustomobject]@{ComputerName=$Computer;Setting=$CurrentItem;CurrentValue=$CurrentValue;OptionalValue=$OptionalValue;}
                                                
                                                        Remove-Variable CurrentItem,CurrentValue,OptionalValue -ErrorAction SilentlyContinue -WhatIf:$false
                                                    }
                                                Remove-Variable arrCurrentBIOSSetting -ErrorAction SilentlyContinue -WhatIf:$false
                                            }
                                        Catch { Write-Output "ERROR: UNABLE TO QUERY THE BIOS VIA CLASS [Lenovo_BiosSeting] ON [$Computer] - POSSIBLE INVALID SETTING SPECIFIED [$Item][$CurrentItem]: $_"; throw }
                                    }
                            }
                        Else
                            {
                                Try
                                    {
                                        $arrCurrentBIOSSetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi -ComputerName $Computer -ErrorAction Stop | ? { $_.CurrentSetting -ne "" } | Select -ExpandProperty CurrentSetting
                                        Foreach($CurrentBIOSSetting in $arrCurrentBIOSSetting)
                                            {
                                                [string]$CurrentItem = $CurrentBIOSSetting.SubString(0,$($CurrentBIOSSetting.IndexOf(',')))
                                        
                                                if($CurrentBIOSSetting.IndexOf(';') -gt 0) { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1),$CurrentBIOSSetting.IndexOf(';')-$($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                else { [string]$CurrentValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(',')+1)) }
                                                                        
                                                if($CurrentBIOSSetting.IndexOf(';') -gt 0)
                                                    {
                                                        [string]$OptionalValue = $CurrentBIOSSetting.SubString($($CurrentBIOSSetting.IndexOf(';')+1))
                                                        [string]$OptionalValue = $OptionalValue.Replace('[','').Replace(']','').Replace('Optional:','').Replace('Excluded from boot order:','')
                                                    }
                                                Else { [string]$OptionalValue = 'N/A' }
                                                                                                                        
                                                $BIOSSetting += [pscustomobject]@{ComputerName=$Computer;Setting=$CurrentItem;CurrentValue=$CurrentValue;OptionalValue=$OptionalValue;}
                                        
                                                Remove-Variable CurrentItem,CurrentValue,OptionalValue -ErrorAction SilentlyContinue -WhatIf:$false
                                            }
                                        Remove-Variable arrCurrentBIOSSetting -ErrorAction SilentlyContinue -WhatIf:$false
                                    }
                                Catch { Write-Output "ERROR: UNABLE TO QUERY THE BIOS VIA CLASS [Lenovo_BiosSeting] ON [$Computer]: $_"; throw }
                            }
                    }
            }
        
        End { $BIOSSetting }
    }
#endregion Function Get-LenovoBIOSSetting

Wrap It Up!

If you decide to try this and run into problems or have questions, please let me know as I’m generally interested in use cases and constructive feedback.

Thanks and Good Providence to you!

Lenovo BIOS Manipulation: Getting Pretty Data

I touched on this subject previously as I worked through a strategy to reconfigure our Lenovo machines from Legacy BIOS to UEFI.

Lenovo has different engineering teams for the various hardware they have and they’ve all taken different approaches to how they expose and allow you to manipulate the BIOS via WMI.  I really like what the ThinkCentre team did with the M900:  Not only do they allow you to view and set various BIOS options but they also let you see what the valid values are for said options!  It’s a thing of beauty and a stroke of genius.

I hope one day Lenovo gets the various teams together to take the best of the best and normalize the BIOS across the board.

Subtle.

I wrote a small PowerShell script to query the BIOS and display it in a manner I hope anyone will appreciate.  Although it works on ThinkPads, it really shines when executed from a recent ThinkCenter, like an M900, as it will also display the valid values for said BIOS option.

$tmpBIOSSetting = @()
$BIOSSetting = @()
gwmi -class Lenovo_BiosSetting -namespace root\wmi | % { if ($_.CurrentSetting -ne "") { $tmpBIOSSetting += $_.CurrentSetting } } 
Foreach($tmpBIOSSetting in $tmpBIOSSetting)
    {
        [string]$Setting = $tmpBIOSSetting.SubString(0,$($tmpBIOSSetting.IndexOf(',')))
        if($tmpBIOSSetting.IndexOf(';') -gt 0) { [string]$CurrentValue = $tmpBIOSSetting.SubString($($tmpBIOSSetting.IndexOf(',')+1),$tmpBIOSSetting.IndexOf(';')-$($tmpBIOSSetting.IndexOf(',')+1)) }
        else { [string]$CurrentValue = $tmpBIOSSetting.SubString($($tmpBIOSSetting.IndexOf(',')+1)) }

        if($tmpBIOSSetting.IndexOf(';') -gt 0) { [string]$OptionalValue = $tmpBIOSSetting.SubString($($tmpBIOSSetting.IndexOf(';')+1)) } 
        Else { [string]$OptionalValue = 'N/A' } 
        [string]$OptionalValue = $OptionalValue.Replace('[','').Replace(']','').Replace('Optional:','').Replace('Excluded from boot order:','')

        $BIOSSetting += [pscustomobject]@{Setting=$Setting;CurrentValue=$CurrentValue;OptionalValue=$OptionalValue;}
        Remove-Variable Setting,Currentvalue,OptionalValue 
    }

$BIOSSetting = $BIOSSetting | Sort-Object -Property Setting
$BIOSSetting

Reminder: This just displays the data and doesn’t actually set anything.  Stay tuned for a future post on that subject.

 

Good Providence!

Preparing for Windows 10: Switching to UEFI on Lenovo ThinkPad & ThinkCentre

think this has been talked about elsewhere but I don’t have the direct link/s(?) anymore so … sorry if you think I’m stealing thunder.

You know how people say “Oh I hate that” when they really don’t really hate it?  Well I truly abhor the idea of people doing things that could be automated.  I’m not trying to put people out of a job here!  But our time is expensive and better suited for more important tasks like putting out the occasional fire, providing excellent customer service and just contributing to IT being an agile and proactive entity in the organization.

As we prepare to pilot Windows 10, we need to go from Legacy BIOS to UEFI on our fleet of Lenovo workstations and, to help our teams on the ground make this transition as smooth as possible, I started exploring how to go about doing this.

When I initially looked at Lenovo hardware a handful of years ago now I learned that Lenovo provided some sample VBScripts to help configure the BIOS on various hardware.  I leveraged those scripts to enable TPM on our demo ThinkPads and ThinkCentres and set boot order.  Fortunately it was nothing but a bunch of WMI calls making it easy to manipulate in VBScript.  Now that I’m on the PowerShell boat, it’s even easier.  (That isn’t to say there aren’t challenges because there’s always a challenge!)

TL;DR

In its simplest form,  you can query the BIOS on a Lenovo via:

gwmi -class Lenovo_BiosSetting -namespace root\wmi | % { if ($_.CurrentSetting -ne "") { $_.CurrentSetting } }

And you can set a BIOS setting on a Lenovo via:

(gwmi -class Lenovo_SetBiosSetting -namespace root\wmi).SetBiosSetting("$Setting,$Value")

At the moment, we have several models of machines in different families (ThinkPad, ThinkCentre and ThinkStation) spanning anywhere from 1 to 4 generations.  To further complicate things, each of those families, and the generations within, don’t necessarily have the same BIOS options or BIOS values which makes figuring things out a little tricky.

The good news is that once you figure out what needs to change it’s easy.
The bad news is that you have to figure out what needs to change, and that includes order of operations.

Bare Bones Config

I could be mistaken, but I do believe that the X240’s and T440’s and up share similar BIOS options which means if you get one working, you pretty much have them all working.  Still, once you think you have it sorted, I’d do a quick query to verify the settings and values match up across them all.

You’d be forgiven for thinking that you could enable UEFI  on a ThinkPad system via something like:

(gwmi -class Lenovo_SetBiosSetting -namespace root\wmi).SetBiosSetting("Boot Mode","UEFI Only")
(gwmi -class Lenovo_SetBiosSetting -namespace root\wmi).SetBiosSetting("Boot Priority","UEFI First")

Turns out those options are not exposed because, well, that would make sense so of course they’re not there.  Instead you have to enable ‘Secure Boot’ which flips those bits for you:

(gwmi -class Lenovo_SetBiosSetting -namespace root\wmi).SetBiosSetting("SecureBoot","Enable")

Ok semi smart!  So you mosey on over to your ThinkCentre, like an M900, and try to do the same but that doesn’t work either.  Why would it – that would be too easy.

Reminds me of one of my favorite scenes in Groundhog Day.

As it turns out the ThinkCentre is the complete opposite of the ThinkPad:
You can set the ‘Boot Priority’ and ‘Boot Mode’ but you cannot set ‘Secure Boot’.

(gwmi -class Lenovo_SetBiosSetting -namespace root\wmi).SetBiosSetting("Boot Mode","UEFI Only")
(gwmi -class Lenovo_SetBiosSetting -namespace root\wmi).SetBiosSetting("Boot Priority","UEFI First")

*Le sigh*

It’s completely nonsensical but that’s what happens when you have siloed engineering teams working on different, but similar, products.

At the moment, I don’t have an answer for enabling Secure Boot on ThinkCentre’s but it will likely require using SRWIN or SRDOS, and I believe it may require human intervention whereas the WMI calls do not.  If I find a solution, you’ll be the second to know. 🙂

 

Good Providence!

UEFI Windows 10 Installation via USB

Most organizations are running Windows 7 on either legacy hardware or UEFI capable hardware but have disabled UEFI in favor of the legacy BIOS emulation and using an MBR partitioning style versus GPT.  Prior to Windows 7, most deployment tools didn’t work with UEFI and there were almost no UEFI benefits for Windows 7, which is why the legacy configuration was preferred.  But in Windows 10, there are some benefits like faster startup time, better support for resume/hibernate, security etc. that you’ll want to take advantage of.

Although not ideal for Windows 10, you could keep using legacy BIOS emulation (which will work just fine, and “be supported for years to come”) and deal with UEFI for new devices or as devices are returned to IT and prepared for redistribution.  But if you want to take advantage of the new capabilities Windows 10 on UEFI enabled devices offers, you’ll essentially have to do a hardware swap because there’s no good way to ‘convert’ as it requires:

  • changing the firmware settings on the devices
  • changing the disk from an MBR disk to a GPT disk
  • changing the partition structure

All coordinated as part of an OS refresh or an upgrade.

Now, I wouldn’t go as far as to say it’s not possible to automate the above (I love me a good challenge), but the recommended procedure is to capture the state from the old device, do a bare metal deployment on a properly configured UEFI device then restore the data onto said device.

If you’re imaging machines with MDT or SCCM and are PXE booting, all you need to do is:

  • add the x64 boot media to your task sequence
  • deploy your task sequence to a device collection that contains the machine you wish to image in question
  • reconfigure the BIOS for UEFI

If however you’re imaging machines by hand with physical boot media, you’ll want a properly configured USB drive to execute the installation successfully.

There are loads of blogs that talk about creating bootable USB media but the majority of them don’t speak to UEFI.  And those that do touch on UEFI, almost all of them miss that one crucial step which is what allows for a proper UEFI based installation.

What you need:

  • 4GB+ USB drive
  • a UEFI compatible system
  • some patience

Terminology

  • USB drive = a USB stick or USB thumb drve – whatever you want to call it
  • USB hard drive = an external hard drive connected via USB; not the same as above
  • Commands I’m referencing are in italics
  • Commands you have to type are in bold italics

Step 1 – Locate your USB Drive

Open an elevated command prompt & run diskpart
At the diskpart console, type: lis dis
At the diskpart console, type: lis vol

You should have a screen that looks similar to this:
Diskpart_lisdis_lisvol

I frequently have two USB hard drives and one USB drive plugged into my machine, so when I have to re-partition the USB drive, I have to be super extra careful.  So to make sure I’m not screwing up, I rely on a few things to make sure I’m picking the proper device.

First: The dead giveaway lies in the ‘lis vol‘ command which shows you the ‘Type’ of device.  We know USB drives can be removed and they’re listed as ‘Removable’.  There’s only one right now, Volume 8 which is assigned drive letter E.

Second: I know that my USB drive is 8GB in size, so I’m looking at the ‘Size’ column in both the ‘lis vol‘ and ‘lis dis‘ commands to confirm I’m looking at the right device.  And from ‘lis dis‘ I see my USB drive is Disk 6.

Step 2 – Prepare USB Drive

From the diskpart console, we’re going to issue a bunch of commands to accomplish our goal.

Select the proper device: sel dis 6

Issue these seven diskpart commands to prepare the USB drive:

  1. cle
  2. con gpt
  3. cre par pri
  4. sel par 1
  5. for fs=fat32 quick
  6. assign
  7. exit

That’s it!  The second diskpart command above is the *most critical step* for properly preparing your USB drive for installing Windows on UEFI enabled hardware, and nearly all the popular sites omit that step.  Bonkers!
Feel free to close the command window now.

Step 3 – Prepare the Media

With your USB drive properly setup now, all you need to do is mount the Windows 10 ISO and copy the contents to the USB drive.

If you’re on Windows 8 or Windows 10 already, right right-click the ISO and ‘Mount’.
If you’re on Windows 7, use something like WinCDEmu to mount the ISO.

Once mounted, you can copy the contents from the ‘CD’ top the USB drive.

Step 4 – Image

A this point all that’s left to do is

  • boot your machine(s)
  • make sure your BIOS is setup for UEFI versus Legacy BIOS; or simply enable ‘Secure Boot’ which on many machines sets UEFI as the default automatically
  • boot from your USB drive
  • install Windows

 

Hopefully this has helped point you in the right direction for taking advantage of all Windows 10 on UEFI enabled hardware has to offer.

 

Good Providence!