Deploy Lenovo ThinkCentre, ThinkPad, and ThinkStation BIOS by Custom WinPE
In this blog post, we are updating a previous post that only covered ThinkPad. We have included updated scripting and information to cover ThinkCentre and ThinkStation as well.
Upgrading BIOS versions and getting consistency in an environment can provide unique challenges across an organization. This blog post will lay out a possible process for putting together a custom WinPE boot WIM file to deploy a BIOS upgrades to current ThinkCentre, ThinkPad, and ThinkStation products.
Overview of the solution:
- Setup the server side folder structure
- Folders
- Logs
- BIOS Folders
- BIOS update files
- Powershell script
- Configure the WinPE.wim file
- Add necessary optional components
- Add necessary drivers to the boot image
- Edit the StartNet.cmd file in the WinPE.wim file.
Setup the server side folder structure
Beginning with the server side folder structure, create a new share (or use an existing share with appropriate permissions) on a server. In that share, create a folder called BIOSVersions. Inside BIOSVersions, create a folder called Logs and then create a folder with the first 4 digits of the SMBIOSBIOSVersion of each model computer with a BIOS to upgrade.The folders seen above are for the X1 Carbon 4th (N1FE) and X1 Carbon 3rd (N14E). The contents of each folder are made up of the source files from the Windows installable version of the BIOS from https:\\support.lenovo.com. Once each BIOS has been downloaded and extracted, copy it to the coordinating folder. Be sure to include all files and sub-folders from the extracted location.
Once the folder structure has been created and populated, create a blank Powershell file called BIOSUpdater.ps1 and copy the following lines of code to the file and save it.
#Define Variables $exeName = "" $srcDir = "T:\BIOSVersions\" $localBiosFolder = "X:\bios" $csproduct = Get-WmiObject -Class Win32_ComputerSystemProduct -Namespace root\cimv2 $deviceID = $csproduct.Name + '-' + $csproduct.IdentifyingNumber $type = $csproduct.Version.Split()[0] $model = $csproduct.Version.Split(' ', 2)[1] $osArc = (Get-WmiObject -Class Win32_OperatingSystem -Namespace root\cimv2).OSArchitecture $BiosCode = (Get-WmiObject -Class Win32_BIOS -Namespace root\cimv2).SMBIOSBIOSVersion.Substring(0,4) $log = $srcDir + 'logs\' + $deviceID + '.log' #Locate and Copy BIOS Folder Write-Host "Locating BIOS folder" $folder = Get-ChildItem -Path $srcDir | ? {$_.Name -eq $BiosCode} if($folder -eq $null) { "Could not find folder for $deviceID. Looking for $BiosCode" | out-file $log Write-Host "Could not locate BIOS folder" Write-Host "Examine log file on the share for more information" return } Write-Host "Located BIOS folder" Write-Host "Begin BIOS Files Copy" Copy-Item ($srcDir + $folder + '\*') $localBiosFolder -Recurse Write-Host "Completed BIOS Files Copy" #Execute BIOS Flash and Reboot Write-Host "Executing BIOS Flash" If($type -eq "ThinkPad") { $exeName = if($osArc -eq "32-bit") {"WINUPTP.EXE"} else {"WINUPTP64.EXE"} $arg1 = "-s" & .\$exeName $arg1 | Out-Null If (Test-Path $localBiosFolder\winuptp.log) { Copy-Item $localBiosFolder\winuptp.log $log # Copy the log file for this device to the network share for auditing purposes. } Else { "Could not find log file for $deviceID" | out-file $log Write-Host "Could not locate Winuptp.log file" } Write-Host "Completing BIOS Flash..." Write-Host "..." Write-Host "Rebooting" & "wpeutil" reboot } ElseIf($type -eq "ThinkCentre" -or ($type -eq "ThinkStation" -and $model -like "P3*")) { & ".\Flash.cmd" /ign /sccm /quiet | Out-File $log # Check the readme.txt to ensure that each WFlash2.exe supports the /SCCM (Reboot Suppress) switch. Write-Host "Completing BIOS Flash..." Write-Host "..." Write-Host "Rebooting" & "wpeutil" shutdown # This needs to be a shutdown command, as the ThinkCentre BIOS documentation states # that the computer needs to reach an S5 power state to complete the update of the # BIOS. Computer will actually reboot. } ElseIf($type -eq "ThinkStation") { $exeName = if($osArc -eq "32-bit") {"AFUWIN.EXE"} else {"AFUWINx64.EXE"} $ROMName = Get-ChildItem -Path $localBiosFolder -Include "*.ROM" -Name & .\$exeName $ROMName /P /B /N /R /SP /RTB /FIT | Out-File $log Write-Host "Completing BIOS Flash..." Write-Host "..." Write-Host "Rebooting" & "wpeutil" reboot } Else { "Unable to determine type of machine for $deviceID" | out-file $log Write-Host "Unable to determine type of machine for $deviceID" }
BIOSUpdater.ps1
Configure the WinPE.wim file
The next item is to configure the WinPE.wim file with all of the pertinent optional components, drivers, and script updates.In this example, we have the Windows 10 ADK installed to the default directory (C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment).
Copy the winpe.wim file from amd64\en-us folder to a spot to work with it. I am using D:\TestBIOS\WinPE.wim as the area that is copied to.
Load the the wim file into command window using the DISM Command to open the image.
Dism /Mount-Image /ImageFile:D:\TestBios\WinPE.wim /Index:1 /MountDir:D:\MountIt
Next we need to configure Optional Components. Add the following Optional Components in this specific order, as order does matter for this installation:
1. HTA:
Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-HTA.cab" Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-HTA_en-us.cab"
2. Scripting:
Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Scripting.cab" Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Scripting_en-us.cab"
3. WMI:
Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-WMI.cab" Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-WMI_en-us.cab"
4. .NET Framework (NetFX):
Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-NetFX.cab" Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-NetFX_en-us.cab"
5. Powershell:
Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\WinPE-Powershell.cab"
Dism /Image:D:\MountIt /Add-Package /PackagePath:"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment\amd64\WinPE_OCs\en-us\WinPE-Powershell_en-us.cab"
After loading those optional components, we need to add in a driver for interrogating the battery status on ThinkPads.
Dism /Image:D:\MountIt /Add-Driver /Driver:D:\MountIt\Windows\INF\Battery.inf
Next we load any networking drivers needed. I suggest using the WinPE Boot Image Driver Packs.
Dism /Image:D:\MountIt /Add-Driver /Driver:<Path to extracted WinPE Drivers>
The last item is to edit the StartNet.cmd file located at D:\MountIt\Windows\System32. Add the following lines of code to the .cmd file after the "wpeinit" line. Change <Server>, <Share>, <password>, and <domain>\<user> to your appropriate values. Save and then close the file.
@echo OFF echo Loading 3rd party PNP drivers wpeinit echo Completed 3rd party PNP driver load echo Creating directory: X:\BIOS mkdir X:\BIOS cd X:\BIOS echo Completed directory creation echo Attempting conection to share. @ipconfig > ipconfig.txt @ping <SERVER> > ping.txt @net use T: \\<SERVER>\<SHARE> <PASSWORD> /user:<DOMAIN>\<USER> IF NOT %ErrorLevel% EQU 0 goto EndDrv echo Copying powershell script file locally. xcopy T:\BIOSVersions\BiosUpdater.ps1 /iqvy Powershell -ExecutionPolicy UnRestricted -File X:\BIOS\BiosUpdater.ps1 :EndDrv echo Connection to server\share not established. Please reboot and try again.
Finally we use Dism to close up the WinPE.wim file.
Dism /Unmount-Image /MountDir:D:\MountIt /commit
Once the WinPE.wim file closes up completely, load it into WDS or your favorite PXE Service and update a BIOS.
When your ThinkCentre, ThinkPad, or ThinkStation client PXE boots this boot image, it will automatically launch the BiosUpdate.ps1 script. The script will find the appropriate BIOS update source files on the network share and will execute the BIOS update silently. The script will then reboot the system at which time the BIOS will be updated. Once complete the system will reboot back to Windows.