forked from MSEndpointMgr/ModernBIOSManagement
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInvoke-DellBIOSUpdate.ps1
339 lines (295 loc) · 19.8 KB
/
Invoke-DellBIOSUpdate.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
<#
.SYNOPSIS
Invoke Dell BIOS Update process.
.DESCRIPTION
This script will invoke the Dell BIOS update process for the executable residing in the path specified for the Path parameter.
.PARAMETER Path
Specify the path containing the Flash64W.exe and BIOS executable.
.PARAMETER Password
Specify the BIOS password if necessary.
.PARAMETER LogFileName
Set the name of the log file produced by the flash utility.
.EXAMPLE
.\Invoke-DellBIOSUpdate.ps1 -Password "BIOSPassword" -LogFileName "LogFileName.log"
.NOTES
FileName: Invoke-DellBIOSUpdate.ps1
Authors: Maurice Daly & Nickolaj Andersen
Contact: @modaly_it
Created: 2017-05-30
Updated: 2022-07-22
Version history:
1.0.0 - (2017-05-30) Script created (Maurice Daly)
1.0.1 - (2017-06-01) Additional checks for both in OSD and normal OS environments (Maurice Daly)
1.0.2 - (2017-06-07) Fixed bug in legacy update method (Maurice Daly)
1.0.3 - (2017-06-26) Added checks for Flash64W.exe utility and BIOS file presence including some additional logging (Nickolaj Andersen)
1.0.4 - (2017-06-30) Fixed an issue where the password was not passed to Flash64W.exe utility. Added logging for this script to a separate file (Nickolaj Andersen)
1.0.5 - (2017-07-04) Configured Flash64W.exe as the native update tool for 64-bit Full OS deployments
1.0.6 - (2018-12-04) Variable name correction in example. No functional changes
1.0.7 - (2019-02-05) Removed requirement for OSDBIOSPackage01 variable. Script will now default to this value.
Added registry stamping function
1.0.8 - (2019-03-02) Updated path and task sequence handling
1.0.9 - (2019-05-01) Removed the /f switch that bypasses the model check and could possibly incorrectly flash the system with a wrong BIOS package if Dell somehow messes up with the downloaded bits
1.1.0 - (2019-05-14) Handle log output correctly if $Password is not specified
1.2.1 - (2022-07-22) -Added support for systems without a Flash64 option
-Added detection of battery issues for non-flash64 systems and retry with /forceit
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[parameter(Mandatory = $false, HelpMessage = "Specify the path containing the Flash64W.exe and BIOS executable.")]
[ValidateNotNullOrEmpty()]
[string]$Path,
[parameter(Mandatory = $false, HelpMessage = "Specify the BIOS password if necessary.")]
[ValidateNotNullOrEmpty()]
[string]$Password,
[parameter(Mandatory = $false, HelpMessage = "Set the name of the log file produced by the flash utility.")]
[ValidateNotNullOrEmpty()]
[string]$LogFileName = "DellFlashBIOSUpdate.log"
)
Begin {
# Load Microsoft.SMS.TSEnvironment COM object
try {
$TSEnvironment = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction Stop
}
catch [System.Exception] {
Write-Warning -Message "Unable to construct Microsoft.SMS.TSEnvironment object"
}
}
Process {
# Functions
function Write-CMLogEntry {
param (
[parameter(Mandatory = $true, HelpMessage = "Value added to the log file.")]
[ValidateNotNullOrEmpty()]
[string]$Value,
[parameter(Mandatory = $true, HelpMessage = "Severity for the log entry. 1 for Informational, 2 for Warning and 3 for Error.")]
[ValidateNotNullOrEmpty()]
[ValidateSet("1", "2", "3")]
[string]$Severity,
[parameter(Mandatory = $false, HelpMessage = "Name of the log file that the entry will written to.")]
[ValidateNotNullOrEmpty()]
[string]$FileName = "Invoke-DellBIOSUpdate.log"
)
# Determine log file location
$LogFilePath = Join-Path -Path $TSEnvironment.Value("_SMSTSLogPath") -ChildPath $FileName
# Construct time stamp for log entry
$Time = -join @((Get-Date -Format "HH:mm:ss.fff"), "+", (Get-WmiObject -Class Win32_TimeZone | Select-Object -ExpandProperty Bias))
# Construct date for log entry
$Date = (Get-Date -Format "MM-dd-yyyy")
# Construct context for log entry
$Context = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)
# Construct final log entry
$LogText = "<![LOG[$($Value)]LOG]!><time=""$($Time)"" date=""$($Date)"" component=""DellBIOSUpdate.log"" context=""$($Context)"" type=""$($Severity)"" thread=""$($PID)"" file="""">"
# Add value to log file
try {
Out-File -InputObject $LogText -Append -NoClobber -Encoding Default -FilePath $LogFilePath -ErrorAction Stop
}
catch [System.Exception] {
Write-Warning -Message "Unable to append log entry to Invoke-DellBIOSUpdate.log file. Error message: $($_.Exception.Message)"
}
}
# Default to task sequence variable set in detection script
if (-not ([string]::IsNullOrEmpty($TSEnvironment.Value("OSDBIOSPackage01")))) {
Write-CMLogEntry -Value "Using BIOS package location set in OSDBIOSPackage01 TS variable" -Severity 1
$Path = $TSEnvironment.Value("OSDBIOSPackage01")
}
# Run BIOS update process if BIOS package exists
if (-not ([string]::IsNullOrEmpty($Path))) {
# Write log file for script execution
Write-CMLogEntry -Value "Initiating script to determine flashing capabilities for Dell BIOS updates" -Severity 1
# Flash BIOS upgrade utility file name
$FlashUtility = Get-ChildItem -Path $Path -Filter "*.exe" -Recurse | Where-Object { $_.Name -like "Flash64W.exe" } | Select-Object -ExpandProperty FullName
Write-CMLogEntry -Value "Attempting to use flash utility: $($FlashUtility)" -Severity 1
if ($FlashUtility -ne $null) {
# Detect BIOS update executable
$CurrentBIOSFile = Get-ChildItem -Path $Path -Filter "*.exe" -Recurse | Where-Object { $_.Name -notlike ($FlashUtility | Split-Path -Leaf) } | Select-Object -ExpandProperty FullName
Write-CMLogEntry -Value "Attempting to use BIOS update file: $($CurrentBIOSFile)" -Severity 1
if ($CurrentBIOSFile -ne $null) {
# Set log file location
$BIOSLogFile = Join-Path -Path $TSEnvironment.Value("_SMSTSLogPath") -ChildPath $LogFileName
# Set required switches for silent upgrade of the bios and logging
$FlashSwitches = "/b=$($CurrentBIOSFile) /s /l=$($BIOSLogFile)" # /forceit
# Add password to the Flash64W.exe switches
if ($PSBoundParameters["Password"]) {
if (-not ([System.String]::IsNullOrEmpty($Password))) {
$FlashSwitches = $FlashSwitches + " /p=$($Password)"
}
}
if (($TSEnvironment -ne $null) -and ($TSEnvironment.Value("_SMSTSinWinPE") -eq $true)) {
Write-CMLogEntry -Value "Current environment is determined as WinPE" -Severity 1
try {
# Start flash update process
if (-not ([System.String]::IsNullOrEmpty($Password))) {
Write-CMLogEntry -Value "Using the following switches for $FlashUtility $($FlashSwitches -replace $Password, "<password removed>")" -Severity 1
}
else {
Write-CMLogEntry -Value "Using the following switches for $FlashUtility $($FlashSwitches)" -Severity 1
}
$FlashProcess = Start-Process -FilePath $FlashUtility -ArgumentList $FlashSwitches -PassThru -Wait -ErrorAction Stop
# Set reboot flag if restart required determined (exit code 2)
switch ($FlashProcess.ExitCode) {
"0" {
# Set reboot required flag
$TSEnvironment.Value("SMSTSBIOSUpdateRebootRequired") = "True"
$TSEnvironment.Value("SMSTSBIOSInOSUpdateRequired") = "False"
}
"2" {
# Set reboot required flag
$TSEnvironment.Value("SMSTSBIOSUpdateRebootRequired") = "True"
$TSEnvironment.Value("SMSTSBIOSInOSUpdateRequired") = "False"
}
"10" {
Write-CMLogEntry -Value "Laptop is on battery power. The AC power must be connected to successfully flash the BIOS." -Severity 3 #; exit 1
$ResultsArray = (Get-Content $BIOSLogFile).Split(':')
if ($ResultsArray -like $BatteryError) {
Write-CMLogEntry -Value "Battery error detected (below 10%), adding /forceit switch and retrying" -Severity 2
$FlashSwitches = $FlashSwitches + " /forceit"
$FlashUpdate = Start-Process -FilePath $FlashUtility -ArgumentList $FlashSwitches -PassThru -Wait -ErrorAction Stop
}
elseif ($ResultsArray -like $BatteryandPowerError) {
Write-CMLogEntry -Value "Battery error detected (no battery or power connection detected), adding /forceit switch and retrying" -Severity 2
$FlashSwitches = $FlashSwitches + " /forceit"
$FlashUpdate = Start-Process -FilePath $FlashUtility -ArgumentList $FlashSwitches -PassThru -Wait -ErrorAction Stop
}
Write-CMLogEntry -Value "Laptop is on battery power. Retried the update with /forceit switch." -Severity 2; exit 1
}
default {
Write-CMLogEntry -Value "An error occured while updating the system BIOS during OS offline phase. Please review the log file located at $($BIOSLogFile)" -Severity 3; exit 1
}
}
}
catch [System.Exception] {
Write-CMLogEntry -Value "An error occured while updating the system BIOS during OS offline phase. Error message: $($_.Exception.Message)" -Severity 3; exit 1
}
}
else {
# Used as a fall back for systems that do not support the Flash64w update tool
# Used in a later section of the task sequence (after Setup Windows and ConfigMgr step)
Write-CMLogEntry -Value "Current environment is determined as FullOS" -Severity 1
# Detect Bitlocker Status
$OSVolumeEncypted = if ((Manage-Bde -Status C:) -match "Protection On") { Write-Output $true }
else { Write-Output $false }
# Supend Bitlocker if $OSVolumeEncypted is $true, remember to re-enable BitLocker after the flashing has occurred
if ($OSVolumeEncypted -eq $true) {
Write-CMLogEntry -Value "Suspending BitLocker protected volume: C:" -Severity 1
Manage-Bde -Protectors -Disable C:
}
# Start BIOS update process
try {
if (([Environment]::Is64BitOperatingSystem) -eq $true) {
$BatteryError = 'Error: The battery must be charged above 10% before the system BIOS can be flashed.'
$BatteryandPowerError = 'Error: The AC adapter and battery must be plugged in before the system BIOS can be flashed.'
Write-CMLogEntry -Value "Starting 64-bit flash BIOS update process" -Severity 1
if (-not ([System.String]::IsNullOrEmpty($Password))) {
Write-CMLogEntry -Value "Using the following switches for $FlashUtility $($FlashSwitches -replace $Password, "<password removed>")" -Severity 1
}
else {
Write-CMLogEntry -Value "Using the following switches for $FlashUtility $($FlashSwitches)" -Severity 1
}
# Update BIOS using Flash64W.exe
$FlashUpdate = Start-Process -FilePath $FlashUtility -ArgumentList $FlashSwitches -PassThru -Wait -ErrorAction Stop
switch ($FlashUpdate.ExitCode) {
"0" {
# Set reboot required flag
$TSEnvironment.Value("SMSTSBIOSUpdateRebootRequired") = "True"
$TSEnvironment.Value("SMSTSBIOSInOSUpdateRequired") = "False"
}
"2" {
# Set reboot required flag
$TSEnvironment.Value("SMSTSBIOSUpdateRebootRequired") = "True"
$TSEnvironment.Value("SMSTSBIOSInOSUpdateRequired") = "False"
}
"10" {
Write-CMLogEntry -Value "Laptop is on battery power. The AC power must be connected to successfully flash the BIOS." -Severity 3#; exit 1
$ResultsArray = (Get-Content $BIOSLogFile).Split(':')
if ($ResultsArray -like $BatteryError) {
Write-CMLogEntry -Value "Battery error detected (below 10%), adding /forceit switch and retrying" -Severity 2
$FlashSwitches = $FlashSwitches + " /forceit"
$FlashUpdate = Start-Process -FilePath $FlashUtility -ArgumentList $FlashSwitches -PassThru -Wait -ErrorAction Stop
}
elseif ($ResultsArray -like $BatteryandPowerError) {
Write-CMLogEntry -Value "Battery error detected (no battery or power connection detected), adding /forceit switch and retrying" -Severity 2
$FlashSwitches = $FlashSwitches + " /forceit"
$FlashUpdate = Start-Process -FilePath $FlashUtility -ArgumentList $FlashSwitches -PassThru -Wait -ErrorAction Stop
}
Write-CMLogEntry -Value "Laptop is on battery power. Retried the update with /forceit switch." -Severity 2; exit 1
}
default {
Write-CMLogEntry -Value "An error occured while updating the system BIOS during OS offline phase. Please review the log file located at $($BIOSLogFile)" -Severity 3; exit 1
}
}
}
else {
# Set required switches for silent upgrade of the BIOS
$FileSwitches = " /l=$($BIOSLogFile) /s"
# Add password to switches
if ($PSBoundParameters["Password"]) {
if (-not ([System.String]::IsNullOrEmpty($Password))) {
$FileSwitches = $FileSwitches + " /p=$($Password)"
}
}
Write-CMLogEntry -Value "Starting 32-bit flash BIOS update process" -Severity 1
if (-not ([System.String]::IsNullOrEmpty($Password))) {
Write-CMLogEntry -Value "Using the following switches for BIOS file $CurrentBIOSFile : $($FlashSwitches -replace $Password, "<password removed>")" -Severity 1
}
else {
Write-CMLogEntry -Value "Using the following switches for BIOS file $CurrentBIOSFile : $($FlashSwitches)" -Severity 1
}
# Update BIOS using update file
$FileUpdate = Start-Process -FilePath $CurrentBIOSFile -ArgumentList $FileSwitches -PassThru -Wait -ErrorAction Stop
}
}
catch [System.Exception] {
Write-CMLogEntry -Value "An error occured while updating the system BIOS in OS online phase. Error message: $($_.Exception.Message)" -Severity 3; exit 1
}
}
}
else {
Write-CMLogEntry -Value "Unable to locate the current BIOS update file" -Severity 2; exit 1
}
}
else {
$CurrentBIOSFile = Get-ChildItem -Path $Path -Filter "*.exe" -Recurse | Where-Object { $_.Name -match ".exe" } | Select-Object -ExpandProperty FullName
Write-CMLogEntry -Value "Unable to locate the Flash64W.exe utility trying Manual Install" -Severity 1;
# Set log file location
$BIOSLogFile = Join-Path -Path $TSEnvironment.Value("_SMSTSLogPath") -ChildPath $LogFileName
# Expected battery error for manual process
$BatteryError = ' The battery must be charged above 10% before the system BIOS can be flashed.'
$BatteryandPowerError = 'Error: The AC adapter and battery must be plugged in before the system BIOS can be flashed.'
# Set required switches for silent upgrade of the BIOS
#$FileSwitches = "/s /r"
$FileSwitches = "/s /l=$($BIOSLogFile)"
# Add password to switches
if ($PSBoundParameters["Password"]) {
if (-not ([System.String]::IsNullOrEmpty($Password))) {
$FileSwitches = $FileSwitches + " /p=$($Password)"
}
}
Write-CMLogEntry -Value "Starting 32-bit flash BIOS update process" -Severity 1
if (-not ([System.String]::IsNullOrEmpty($Password))) {
Write-CMLogEntry -Value "Using the following switches for BIOS file $CurrentBIOSFile : $($FileSwitches -replace $Password, "<password removed>")" -Severity 1
}
else {
Write-CMLogEntry -Value "Using the following switches for BIOS file $CurrentBIOSFile : $($FileSwitches)" -Severity 1
Write-Host $FileSwitches
}
# Update BIOS using update file
$FileUpdate = Start-Process -FilePath $CurrentBIOSFile -ArgumentList $FileSwitches -PassThru -Wait -ErrorAction Stop
if ($FileUpdate.ExitCode -eq 1) {
$ResultsArray = (Get-Content $BIOSLogFile).Split(':')
if ($ResultsArray -like $BatteryError) {
Write-CMLogEntry -Value "Battery error detected (below 10%), adding /forceit switch and retrying" -Severity 2
$FileSwitches = $FileSwitches + " /forceit"
$FileUpdate = Start-Process -FilePath $CurrentBIOSFile -ArgumentList $FileSwitches -PassThru -Wait -ErrorAction Stop
} elseif ($ResultsArray -like $BatteryandPowerError) {
Write-CMLogEntry -Value "Battery error detected (no battery or power connection detected), adding /forceit switch and retrying" -Severity 2
$FileSwitches = $FileSwitches + " /forceit"
$FileUpdate = Start-Process -FilePath $CurrentBIOSFile -ArgumentList $FileSwitches -PassThru -Wait -ErrorAction Stop
}
}
Write-CMLogEntry "Flash of $CurrentBIOSFile completed with exit code $($FileUpdate.ExitCode)"
}
}
else {
Write-CMLogEntry -Value "Unable to determine BIOS package path." -Severity 2; exit 1
}
}