Dell BIOS update–WinPE–Model Independent–From Internet

Update 12/18/18 – Dell Changed their Cab XML data, and the part of the script that downloads directly isn’t working.  Checking with @Geodesicz (Mark Godfrey) to see if he has updated his script to accommodate this.

Update 5/26 – Updated Script to use Dell’s Enterprise Cab XML data, instead of the ever changing Support Site.  This now truly does work for all Dell Models that I know of. 🙂  Thanks Mark – POST HERE Mark gives some back story as to how we came up with this idea, and why we wanted to switch from our old method.

Updated Script: (Mark Updated to fix some model name changes 2/26/2018) – Please grab the updated Script from Below and replace the one in the download.  I have not updated the Download.
For any questions regarding the download portion, check Mark’s GitHub

<# 
.SYNOPSIS 
Dynamically Download and Install Latest Dell BIOS Update for Local Model
.DESCRIPTION 
Dynamically Download and Install Latest Dell BIOS Update for Local Model
Code to Download and Parse Dell Downloads from SCUP CAB borrowed from Mark Godfrey @Geodesicz
.LINK
https://github.com/markgodfreyii/PowerShell/blob/master/ParseDellCab.ps1
#> 

Param(
    [Parameter(Mandatory=$true,Position=1,HelpMessage="BIOS Password")]
    [ValidateNotNullOrEmpty()]
    [string]$BIOSpassword
)

# Format Cab DL Path
$CabPath = "$PSScriptRoot\dellsdpcatalogpc.cab"

# Download Dell Cab File
Invoke-WebRequest -Uri "http://ftp.dell.com/catalog/dellsdpcatalogpc.cab" -OutFile $CabPath -UseBasicParsing -Verbose
[int32]$n=1
While(!(Test-Path $CabPath) -and $n -lt '3'){
    Invoke-WebRequest -Uri "http://ftp.dell.com/catalog/dellsdpcatalogpc.cab" -OutFile $CabPath -UseBasicParsing -Verbose
    $n++
}

# Extract XML from Cab File
If(Test-Path "$PSScriptRoot\DellSDPCatalogPC.xml"){Remove-Item -Path "$PSScriptRoot\DellSDPCatalogPC.xml" -Force -Verbose}

Expand $CabPath "$PSScriptRoot\DellSDPCatalogPC.xml"

# Import and Create XML Object
If(Test-Path "$PSScriptRoot\DellSDPCatalogPC.xml"){[xml]$XML = Get-Content $PSScriptRoot\DellSDPCatalogPC.xml -Verbose}
else{exit 15}

# Create Array of Downloads
$Downloads = $xml.SystemsManagementCatalog.SoftwareDistributionPackage

# Display List of Available Downloads
# $Names = $Downloads | ForEach {$PSItem.LocalizedProperties.Title}

# Find Target Download for Specific Desired Function (Example)
$Model = ((Get-WmiObject win32_computersystem).Model).TrimEnd()
If(!($Model.EndsWith("AIO")) -or !($Model.EndsWith("M"))){
    $Target = $Downloads | Where-Object -FilterScript {
        $PSitem.LocalizedProperties.Title -match $model -and $PSitem.LocalizedProperties.Title -notmatch $model + " AIO" -and $PSitem.LocalizedProperties.Title -notmatch $model + "M"
    }
}
Else{$Target = $Downloads | Where-Object -FilterScript {$PSitem.LocalizedProperties.Title -match $model}}
$TargetLink = $Target.InstallableItem.OriginFile.OriginUri
$TargetFileName = $Target.InstallableItem.OriginFile.FileName
Invoke-WebRequest -Uri $TargetLink -OutFile $PSScriptRoot\$TargetFileName -UseBasicParsing -Verbose
$File = "$PSScriptRoot\$TargetFileName"


<# 
.SYNOPSIS 
Install the Dell Bios File downloaded by Mark's Script Above during WinPE, then Creates Variables for ConfigMgr's TS Process to do additonal tasks (Reboot, etc)
Requires Paramter for Bios Password.  If you don't have a Bios Password, SHAME ON YOU, add one!
.DESCRIPTION 
Take the downloaded BIOS File and Install in WinPE64 - Gary Blok
.LINK
https://garytown.com
#> 


<#Added TS Variables for if using during OSD.  Creates Variable SMSTS_BiosUpdate, and sets to TRUE. (For Future Use)
	http://powershelldistrict.com/how-to-read-and-write-sccm-task-sequence-variables-with-powershell/

#>

$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment -ErrorAction SilentlyContinue
$tsenv.Value("SMSTS_BiosUpdate") = "True"

#Create Log Path
$LogPath = $tsenv.Value("_SMSTSLogPath")

#Get Bios File Name (No Extension, used to create Log File)
$BiosLogFileName = $TargetFileName.Replace(".exe",".log")

# Validate BIOS Update File Successful Download
If(!(Test-Path $file)){
    Add-Content -Path "$LogPath\BIOSUpdate_$ComputerModel.log" -Value "BIOS update file did not download correctly"
    $tsenv.Value("SMSTS_BiosUpdateFailedDownload") = "True"
    Exit
}

#Set Command Arguments for Bios Update
$cmds = "/b=$File /s /p=$BiosPassword /l=$LogPath\$BiosLogFileName"

#Update Bios
$Process = start-process $PSScriptRoot\Flash64W.exe -ArgumentList $cmds -PassThru -wait


#Creates and Set TS Variable to be used to run additional steps if reboot requried.
if ($process.ExitCode -eq 2)
    {$tsenv.Value("SMSTS_BiosUpdateRebootRequired") = "True"}
    else
    {$tsenv.Value("SMSTS_BiosUpdateRebootRequired") = "False"}
if ($process.ExitCode -eq 10)
    {$tsenv.Value("SMSTS_BiosUpdateBatteryCharge") = "True"}
    else
    {$tsenv.Value("SMSTS_BiosUpdateBatteryCharge") = "False"}

The new Package contents:

 

 

 

The Task Sequence:

Just an FYI… you might notice that it’s not updating the BIOS to the latest BIOS update for that model.  Example, yesterday several bios updates were released for several models to their WebSite. Those will not install using this method.  The Enterprise CAB data has extra layers of Change Management / Testing, so you can feel even better about applying the BIOS updates automatically. Once those extra layers have completed, then they become available.

 

Until Then, I’ve updated scripts and added a script for the TPM update.

2 scripts now, based on Dell Driver Cab, instead of HTML scraping.

  1. DellTPMDownloadUpdatePE – Downloads and install the TPM 2.0 x64 Update for that model (if available)
  2. DellCabBiosUpdate.ps1

—————-

Original Post:

Ok, so you’re thinking, Gary, you just posted about this, and you’d be right, I did, see.. https://garytown.com/dell-bios-upgrade-in-osd-winpe-x64, but in the past week or so, I’ve come up with an idea, after looking at Maurice Daly’s download utilities, thinking, why can’t I just do something like that, and not have to have any content (beside the script and utility) to update the bios, and have it work on any dell model?  So that’s what I did, with the help of @modaly_IT & @geodesicz (my personal powershell guy), we came up with this solution.

DOWNLOAD HERE

Goal of Script:  Update Dell Bios on Any Model without having to maintain and update packages.

What it does:

  1. Gets Model info from WMI
  2. Downloads latest Bios directly from Dell
    1. No testing with Proxy server done, you can probably add this into the script, just don’t ask me how. (I don’t know, ask Maurice, he has it figured out in his cool GUI version)
    2. Mark (@Geodesicz) was able to make the changes to have this work in PE.
  3. Applies Bios to system during WinPE
  4. Create variables to do extra steps based on exit codes

Pros:

  1. Never manually download a BIOS update and build a BIOS package again
  2. Always install the latest Dell BIOS on the system you’re imaging
  3. Works on all dell models, no tracking down a bios per model
  4. See Number 1

Cons:

  1. Giving up control of the Bios Version you’re installing
    1. This doesn’t bother me personally, I haven’t ever had a BIOS update brick a machine, and if the BIOS is coming directly from Dell, it’s supported by them, and they will assist if anything did happen.
  2. Uses the Internet to pull content, while only 8-12MB per Computer, if you’re imaging large numbers, and you don’t plan ahead, this could be potential issue.
  3. Uses HTML scraping, so if Dell ever changes their website, we’d have to update the script.

 

The Script… while very similar to my last one, it has some key differences.

  1. The Bios Password is now parametrized, no longer requiring the text file to pull password (Thanks Mark)
  2. Has large download section in which it has the logic to get the right Bios file (from Maurice) & the Actual download step, (from Mark).
  3. Validating the Bios downloaded.

This script is quite simple still, feel free to add additional logic to it for error handling.

image

image

 

in the TS:

image

Package Content:
image

As before, it will create logs in the SMSTSLog folder in %temp%.
The only difference now, I added a group that will only run if the Download Fails based on lines 86-90 of the script.

For more details on how to setup the rest, check out the old Post:
https://garytown.com/dell-bios-upgrade-in-osd-winpe-x64

Maurice’s new GUI version: http://www.scconfigmgr.com/2017/03/01/driver-automation-tool/

Maurice’s older version, where I stole the code from: https://gallery.technet.microsoft.com/scriptcenter/SCCM-Dell-Client-Bios-ee577b04

32 thoughts on “Dell BIOS update–WinPE–Model Independent–From Internet”

  1. This is a great write up, can’t wait to try this out in my environment.

    Is there any chance that you have plans to try and implement this independent BIOS download for HP machines as well?

    Reply
    • Not at this time, really busy right now, and we only have a couple HP Models that I have to support. But if I ever do, I’ll post it here. For now, you’ll probably have to use the HP’s SSM method to do it if you don’t want to make a package.

      Reply
    • The reason it wasn’t included as this script was intended to be used during WinPE, before Bitlocker was enabled, so no needed to suspend bitlocker. If you’re going to adapt to be used for other things, once Windows is installed and encrypted with bitlocker, you could add something like this:
      #Create Variable of Bitlocker Status
      $BitlockerStatus = Get-BitLockerVolume -MountPoint c: -Verbose | select -ExpandProperty ProtectionStatus -Verbose
      #Check if Bilocker enabled, then suspened.
      If ($BitlockerStatus -eq “On”)
      {Suspend-BitLocker -MountPoint “C:” -Verbose
      }
      #Suspend Bitlocker Again for good messure, Win7 Machines don’t listen to the PS Command.
      #Fix Mof associated with Bitlocker
      mofcomp.exe c:\windows\system32\wbem\win32_encryptablevolume.mof
      Manage-bde.exe -protectors -disable c:

      Reply
  2. Awesome script, working well for the most part; I’ve has success with the Optiplex 7040, 3010 & 790, but the script failed on the 3020. Any idea what the problem is?

    Executing command line: Run Powershell script
    Invoke-WebRequest :
    404 – File or directory not found.
    Server Error

    404 – File or directory not found.
    The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.

    At D:\_SMSTaskSequence\Packages\SCM0011E\DellBiosDownloadUpdatePE.ps1:64 char:20
    + … Download = (Invoke-WebRequest -Uri $ModelURL -UseBasicParsing).Links …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
    eption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
    Split-Path : Cannot bind argument to parameter ‘Path’ because it is null.
    At D:\_SMSTaskSequence\Packages\SCM0011E\DellBiosDownloadUpdatePE.ps1:65 char:36
    + $BIOSFile = $BIOSDownload.href | Split-Path -Leaf
    + ~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCo
    mmand

    Exception calling “DownloadFile” with “2” argument(s): “An exception occurred during a WebClient request.”
    At D:\_SMSTaskSequence\Packages\SCM0011E\DellBiosDownloadUpdatePE.ps1:70 char:9
    + $WebClient.DownloadFile($url,$file)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException

    You cannot call a method on a null-valued expression.
    At D:\_SMSTaskSequence\Packages\SCM0011E\DellBiosDownloadUpdatePE.ps1:83 char:1
    + $BiosLogFileName = $BiosFile.Replace(“.exe”,”.log”)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Process completed with exit code 0

    Reply
    • Hey, I haven’t tested that model, but you should be able to test by putting a pause in your TS before this would run, then running it line by line to see where it hangs. Probably not reading the Dell Website correctly for that model. Their website isn’t always consistent.

      Reply
      • I’ve run into an issue that sometimes it doesn’t download the CAB file from Dell. Is that what you’re seeing to? To get around that, I had planned to have it try to download 3 times, if it still didn’t download, then use a Pre-Downloaded CAB file. I don’t have any environment to test / update these scripts anymore at my new job, so development will have to go to someone else in the community for awhile.

        Reply
  3. Gary, great stuff here. Having some issues and maybe just some questions on the TPM update. I have some devices that do fail on the TPM update. They are E7240 devices.

    When I ran though the script the TPM variable comes up blank. I assume that means there is no updates to the TPM? Thought I could still get to 2.0 or is this just a Dell web site being inconsistent. What I think that variable should be is: http://downloads.dell.com/published/pages/latitude-e7240-ultrabook.html

    When I tried to run the update manually I do get an error which leads me to believe I cannot update this laptop.
    The errors are:
    Error: The System TPM Query Failed, Aborting
    Error: Unable to prepare the TPM update payload.

    Reply
  4. Trying to set this up in our test sequence, and currently have no luck. Seems it is not finding the model in the XML, although if I look through the XML the model is there and there is a download link.

    The error is about null URI, so I went through the PS script and added some Write-Hosts in after each of the Variables $Model, $Target, $TargetLink, $File. And ran the script manually and only $Model had data, the rest were all blank.

    Is there anything that needs to be added to the Boot image or anywhere else for it to read the XML, its reading it somewhat as I also uncommented the link

    # Display List of Available Downloads
    $Names = $Downloads | ForEach {$PSItem.LocalizedProperties.Title}

    And added a Write-Host $Names

    And it does through a large jumble of Models and such into output.

    Thanks

    Reply
    • – Info from Mark Godfrey – I found the issue. It has to do with how Dell inputted the model number into the system for the 780. There are numerous trailing spaces after the model name.
      Inline image 1
      Assuming Gary has not modified my original script portion to parse the Dell CAB too much, you should be able to fix this by finding a line that looks like this:

      $Model = (Get-WmiObject win32_computersystem).Model
      and changing it to this:

      $Model = ((Get-WmiObject win32_computersystem).Model).TrimEnd()
      Inline image 2

      BiosUpdateInfoO780

      Reply
    • I’ve updated the script in the Post, it resolves the issue on the 780, and also fixed a bug we found on some other models where it didn’t want to download.

      Reply
  5. Do you have an export of your task sequence that you can share. With your changes for Old and New Models it would be great to see how you do that in one step. Also from the HowToUse.docx there are undocumented steps for “Notify Download Failed” and those items that follow.

    Reply
    • I’ve updated the download to include the new scripts and a cleaned up “How to”. I’ve removed the notify info, as I as having issues with it in our environment. However, you could create a group based on other exit codes from the BIOS upgrade by having those codes create TS Variables in the script, and have it create a popup or notification if you like.

      Reply
  6. I’ve tried your script, but had to fix it to work on some of our PCs.
    The Latitude E5570 did not work – script just failed – found out Dell has several models that use the same bios – so the title in the downloaded XML is “Dell Latitude E5270/E5470/E5570”. This means the -match $model fails, because $model is Latitude E5570.

    My simple fix: add this after setting $model:
    if($model -eq “Latitude E5570”) {$model = “Latitude E5270”}

    Reply
  7. Don’t have a SCCM environment in place, but we are using MDT 2013 (8450). Would this work in there as a task sequence post install phase since I can’t boot back into WinPE with MDT automatically? I see a bunch of references to system center. Would love to implement and not have to manually maintain BIOS updates.

    Thanks

    Reply
    • You can move it later into the TS and update the BIOS after windows is installed. If you’re using Dell, you can also leverage Command Update to install the latest BIOS & Drivers after the OS is laid down.

      Reply
  8. We have no BIOS passwords set on our Dell machines and the script keeps failing as it is expecting a BIOS password.

    Any ideas please Gary?

    Reply
  9. I’m getting an error:
    C:\dellbiosdownloadupdate\dellsdpcatalogpc.cab: Destination directory required for a multi-file CAB.

    This happened in a task sequence (Feature Upgrade not OSD) and also trying to run the script in a powershell console manually.

    Reply
  10. Hi Gary, I’m having some issues deploying this in an MDT task sequence. I’m attempting to do this in the same place as you have indicated in the post in WinPE, though I am not using SCCM. The download portion works fine in that the cab file is downloaded and the xml file extracted. In the logs I’m seeing the following (server and share names removed):

    Cannot validate argument on parameter ‘Uri’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

    At \\Server\Share$\Scripts\DellCabBIOSUpdate.ps1:52 char:24
    + Invoke-WebRequest -Uri $TargetLink -OutFile $PSScriptRoot\$TargetFile …
    InvalidData: (:) [Invoke-WebRequest], ParameterBindingValidationException

    You cannot call a method on a null-valued expression.

    At \\Server\Share$\Scripts\DellCabBIOSUpdate.ps1:79 char:1
    + $BiosLogFileName = $TargetFileName.Replace(“.exe”,”.log”)
    InvalidOperation: (:) [], RuntimeException

    I’m a bit of a noob with MDT and powershell and I understand the ‘Find Target Download for Specific Desired Function’ is an example. Please could you explain briefly what the example is doing since this is where the first error occurs. I have an idea but would be good to get some clarity. Any advice much appreciated. Cheers.

    Reply
    • Have you tried just running the script manually on a machine? Looks like a variable isn’t getting populated so you’re unable to download the file, since it didn’t download, rest of the script isn’t going to work well. also, you will want to re-write this for MDT. The script is written in a way that will download the files to the same folder the script is running in, which you really don’t want everything downloading back to the MDT Share.

      You’ll want to replace $PSScriptroot with something local, then step through the script line by line to see where the issue is.

      Reply
      • Hi Gary, so after much trial and error, I discovered the issue was not with the script/ permissions or anything else but that the xml file title for the model I was looking for (Dell Latitude 7490) appears in the Title XML node as ‘Dell Latitude 7290/7390/7490’ and therefore doesn’t match the model and so the file cannot be found. It was confusing as it worked on my own machine (XPS 15 9560) as this matches the Title node. It was only when I tested locally on the 7490 and saw the same error that I figured out what might be going on. I’m struggling to write logic to cope with this but I would have thought it’s a fairly common issue since these are popular models. Would be super appreciative of any advice on how you would deal with this.

        Reply
        • Hi Gary – I figured out a solution which may be simplistic but seems to work fine. I’m sure there are more elegant ways of doing this for someone more knowledgeable but after a LOT of trial and error, this works for me! To be fair I have only tested with a 7490 for now, but we have some 7290 and 7390’s which I will test at some point.

          # Find Target Download for Specific Desired Function
          # i.e. Exclude models ending in ‘M’ or ‘AIO’ and match for Latitude models where the ‘Title’ XML node does NOT exactly match the $Model e.g. ‘Dell Latitude 7290/7390/7490’
          $Model = ((Get-WmiObject win32_computersystem).Model).TrimEnd()
          If (($Model.Contains(“7290”)) -and ((!($Model.EndsWith(“AIO”)) -or !($Model.EndsWith(“M”))))){
          $Target = $Downloads | Where-Object -FilterScript {
          $PSitem.LocalizedProperties.Title -match “7290” -and $PSitem.LocalizedProperties.Title -notmatch $model + ” AIO” -and $PSitem.LocalizedProperties.Title -notmatch $model + “M”
          }
          }

          ElseIf (($Model.Contains(“7390”)) -and ((!($Model.EndsWith(“AIO”)) -or !($Model.EndsWith(“M”))))){
          $Target = $Downloads | Where-Object -FilterScript {
          $PSitem.LocalizedProperties.Title -match “7390” -and $PSitem.LocalizedProperties.Title -notmatch $model + ” AIO” -and $PSitem.LocalizedProperties.Title -notmatch $model + “M”
          }
          }

          ElseIf (($Model.Contains(“7490”)) -and ((!($Model.EndsWith(“AIO”)) -or !($Model.EndsWith(“M”))))){
          $Target = $Downloads | Where-Object -FilterScript {
          $PSitem.LocalizedProperties.Title -match “7490” -and $PSitem.LocalizedProperties.Title -notmatch $model + ” AIO” -and $PSitem.LocalizedProperties.Title -notmatch $model + “M”
          }
          }

          Else{$Target = $Downloads | Where-Object -FilterScript {$PSitem.LocalizedProperties.Title -match $model -and $PSitem.Properties.PublicationState -match “Published”}}

          Reply
          • Hi Gary, Just a quick update that a bit of refinement was needed to my earlier solution (above) for Latitude models:

            $Model = ((Get-WmiObject win32_computersystem).Model).TrimEnd()
            If (((($Model -match ‘7290’) -or ($Model -match ‘7390’) -or ($Model -match ‘7490’)) -and (!($Model.EndsWith(“AIO”)) -or !($Model.EndsWith(“M”))))){
            $Target = $Downloads | Where-Object -FilterScript {
            $PSitem.LocalizedProperties.Title -match ‘7290/7390/7490’ -and $PSitem.LocalizedProperties.Title -notmatch $model + ” AIO” -and $PSitem.LocalizedProperties.Title -notmatch $model + “M”
            }
            }
            ElseIf (((($Model -match ‘7280’) -or ($Model -match ‘7380’) -or ($Model -match ‘7480’)) -and (!($Model.EndsWith(“AIO”)) -or !($Model.EndsWith(“M”))))){
            $Target = $Downloads | Where-Object -FilterScript {
            $PSitem.LocalizedProperties.Title -match ‘7280/7380/7480’ -and $PSitem.LocalizedProperties.Title -notmatch $model + ” AIO” -and $PSitem.LocalizedProperties.Title -notmatch $model + “M”
            }
            }

  11. Thanks Gary. Appreciate the response. I have managed to get it working locally. Like you say I’ll need to adjust the location of where the files are being downloaded on the MDT server so hopefully, I’ll be on my way. Many thanks!

    Reply
  12. Thanks Gary, I ran into a problem with OptiPlex, it looks like in the .XML file there’re 4 types of entries and different systemtypeID:

    sdp:TitleDell OptiPlex 7080 System BIOS,1.16.0,1.16.0
    sdp:TitleDell OptiPlex 7080 System BIOS,1.15.0,1.15.0
    sdp:TitleDell OptiPlex 7080 System BIOS,1.14.0,1.14.0
    sdp:TitleDell OptiPlex 7080 System BIOS,1.13.0,1.13.0

    Results with a URI error when running the script, how can the script be modified so it finds my Optiplex computer?

    Thaanks!

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.