SCCM 2012: Deploying Dell BIOS Updates using the Application Model (Updated)

UPDATE 2016-07-20: I’ve seen the comments about the download link not working.  I’ve instead made an updated script available on GitHub at https://github.com/dhedges01/Blog.  The new script uses new command line syntax as well so I’ve updated the post below to reflect those changes.

During a recent migration from SCCM 2007 to SCCM 2012 SP1, I wanted to really start taking advantage of the new Application Model for software and driver deployments. My goal was simple, to create an Application that would deploy any Dell BIOS Update, to any applicable system, and handle daisy-chaining and even reboots. All this without (much) scripting.

The process outlined below should give you a good understanding of the steps needed to create an Application and various Deployment Types with all of the necessary Detection, Requirement and Dependency Rules needed to successfully deploy Dell BIOS updates using Configuration Manager 2012.

Note: If you are reading this then you are likely familiar with my other post about Updating Dell BIOS with PowerShell.  Much of the concept and setup here is the same, however using the Application Model we get some added benefits.

  1. We only download content for our specific version.  If you have a lot of models to deal with (like I do), this really has an impact in overall download and execution times (My BIOS Update package was well over 300MB when I implemented this method).
  2. Configuration Manager 2012 now gracefully handles reboots from the Application Model meaning we only need to reference the step once in the Task Sequence.  We also don’t need to stage any subsequent updates to the local drive because we aren’t downloading ALL of the BIOS Updates.
  3. We get applicability reporting within Configuration Manager’s AppEnforce.log file which aids troubleshooting
  4. Using this model we can now more easily handle BIOS Updates from other vendors and can even deal with those pesky “Consumer” BIOS updates as well (I’m looking at you Dell XPS…).

First things first, gather up all the latest (tested) BIOS updates for the models you support and organize them (I organize them by model). Each “model” folder will end up being the Content Source location for each Deployment Type. (You could go a step further and create a Version# folder for each version of BIOS update.  But since each model will likely only have 1 or 2 applicable BIOS Updates, it’s not really necessary.)

image

Next up we create our custom SCCM Application. All fields are filled out manually. Make sure you use an appropriate naming convention according to your companies standards.

image

The next step is one that will be repeated over and over again (with a little assistance from the Copy function). We will start by creating the first of many Deployment Types. I’m going to use the BIOS updates for the Dell Latitude E6420 as my example in this post so you can see how to use dependencies in order to install “down level” updates in order.

I like to start with the lowest version number first so we can add the dependencies as we go. We’ll start off with version A05.  Provide the general items such as Name, Content Source, Installation Program, etc.

Installation Program: powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command ".\Invoke-BIOSUpdate.ps1 -UpdateFile .\E6420A05.exe -Arguments /s,/l="C:\WINDOWS\eBay_Deployments\Dell_BIOS_Update_E6420A05.log""

For the Detection Method, I chose to leverage a custom script to detect the BIOS version. This allows me to deal with those pesky “patch” updates that sometimes come out.

image

Here is the detection script I use:

   1: 'Change strBIOSUpdateVersion to the version you are deploying to

   2: strBIOSUpdateVersion = "A05"

   3:

   4: 'Get BIOS Version from Win32_BIOS

   5:

   6: Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")

   7: Set colBIOS = objWMI.ExecQuery("Select * from Win32_BIOS")

   8: For Each objBIOS In colBIOS

   9:     If objBIOS.SMBIOSBIOSVersion >= strBIOSUpdateVersion Then

  10:         WScript.Echo "Detected"

  11:     End If

  12: Next

Add Requirements.

I created a custom Global Condition to echo out the Model from WMI. I use this requirement rule to ensure the Deployment Type only applies to the appropriate system type.

Here is the configured Global Condition:

image

And here is how it’s referenced within the Deployment Type:

image

Configure the User Experience.

Depending on how you configure the install command, you can have ConfigMgr handle the restart or allow the BIOS update to restart the computer. If you choose the latter, make sure you configure the appropriate return code so ConfigMgr is aware of a potential restart.

image

After setting up the first Deployment Type, hit the Apply button so the Deployment Type is saved within the application. Failing to do this will make it unavailable when you try to add it as a dependency.

Copy the Deployment Type and make the necessary updates to the Name, Content Source, Install Command and Detection Method. Click on the Dependency Tab and create a new Dependency Group. Then add a Dependency selecting the first BIOS update you created.

image

After all of the Deployment Types are created, distribute the content and deploy your new application to a test system (or you can add it into an OSD Task Sequence for testing, ConfigMgr should automatically handle the reboots and ensuring that all “down level” updates are applied in order for each model you add.

Update: 

One thing I forgot to mention in this post is the ordering of Deployment Type Priority.  When an application is evaluated, the client evaluates all of the rules for each Deployment Type in order of Priority.  Once it finds an applicable Deployment Type, it executes that one, and it no longer evaluates anymore Deployment Types within that application.  So, as an addendum to this posting, here is how I have the BIOS Updates in my Application sorted by priority.

 

ConfigMgr 2012 Dell BIOS Application Deployment Type Priority

 

As you can see from the screenshot above, I am ordering the Priority of evaluation looking at the Latest BIOS Version first.  Each “down level” BIOS Update is a dependency of the next highest version.  (i.e. A17 has a dependency of A08 and A08 has a dependency of A05).  What happens here is that if the requirements for A17 are not met (version is less than A17), then A17’s dependency (A08) will then be evaluated.  If A08’s requirements aren’t met, it will evaluate A05 and so on until all dependencies are evaluated and executed (in order).  Once the dependencies are met, then A17 can execute.

The important takeaway here is make sure your highest version of BIOS Update is evaluated first because once a Deployment Type is deemed applicable, ConfigMgr stops searching for others unless they are a dependency of the one it finds first.

Dell BIOS Updates with PowerShell

The other day I was discussing with my colleagues about how we were going to deliver Dell BIOS updates to clients during our SCCM OSD deployments.  Several solutions were brought up including using SCUP 2011, a standard software package or just saying forget it and let the desktop support technicians handle it.

We don’t yet have SCUP 2011 implemented in our environment and really don’t have the time to learn it and setup the proper processes (development, testing, release) so I set out to develop a script that would allow us to install it on all of our Dell hardware with ease.

There were 2 main criterion required of this script.

  1. It must “stage” the BIOS update but not force a reboot (OSD must handle that piece)
  2. It must be easy to update the package (even for non-scripters) to support new models or update BIOS versions

That being said, the script described below does just that.  But before we get to the script, lets take a look at the folder structure.  The main folder of the package contains the Invoke-DellBIOSUpdate.ps1 PowerShell script and a bunch of folders all corresponding to the model of a Dell system.  Inside each folder is a *SINGLE* BIOS Update File (You’ll see why I bolded the word single in a bit).

imageimage

Ok, now that we got the directory structure piece out of the way, lets get to the script.  In this first section, we simply gather the current directory, current system model and current installed BIOS version.

*NOTE: I’m referencing the new namespace for Dell OMCI 8.x.  Change this if you are using an older version of Dell OMCI*

$ScriptFolder = Split-Path -Parent $MyInvocation.MyCommand.Definition
$Model = $((Get-WmiObject -Class Win32_ComputerSystem).Model).Trim()
$BIOSVersion = (Get-WmiObject -Namespace root\DCIM\SYSMAN -Class DCIM_BIOSElement).Version

Well that was pretty easy wasn’t it?  But the work isn’t done yet.  The next few steps include:

  • Verifying there is an available BIOS Update for the current system model
  • Identify the version number of the BIOS Update File
  • Verify that a BIOS Update is needed by the system
  • Execute the BIOS Update itself
Doesn’t sound so hard now does it?  Here is the code to do it:
$ScriptFolder = Split-Path -Parent $MyInvocation.MyCommand.Definition
$Model = $((Get-WmiObject -Class Win32_ComputerSystem).Model).Trim()
$BIOSVersion = (Get-WmiObject -Namespace root\DCIM\SYSMAN -Class DCIM_BIOSElement).Version            

if(Test-Path -Path $ScriptFolder\$model)
{
 $BIOSUpdateFile = Get-ChildItem -Path $ScriptFolder\$Model
 $BIOSUpdateFileVersion = $BIOSUpdateFile.ToString() -replace ($BIOSUpdateFile.Extension,"")
 $BIOSUpdateFileVersion = $BIOSUpdateFileVersion.Substring($BIOSUpdateFileVersion.Length -3)                   

 if($BIOSVersion.CompareTo($BIOSUpdateFileVersion) -eq 0)
 {
  Write-Output "BIOS Version is up to date"
 }
 else{
  Try{
   Write-Output "BIOS Update Needed. Attempting BIOS Flash Operation..."
   #Invoke-Expression $ScriptFolder\$Model\$BIOSUpdateFile " /quiet"
   $objStartInfo = New-Object System.Diagnostics.ProcessStartInfo
   $objStartInfo.FileName = "$ScriptFolder\$Model\$BIOSUpdateFile"
   $objStartInfo.Arguments = "-noreboot -nopause -forceit"
   $objStartInfo.CreateNoWindow = $true
   [System.Diagnostics.Process]::Start($objStartInfo) | Out-Null
  }
  Catch{[Exception]
   Write-Output "Failed: $_"
  }
 }            

 Write-Output "End Dell BIOS Update Operation"
}
else
{
 Write-Output "Model Not Supported"
}

OK, now for the explanation of all that nonsense above.  The first thing we are doing in the “if” statement is checking to see if there is a folder with the current system model available.  That will tell us if the package repository is setup to support the current model.  Once we are actually inside the if-loop we create a couple more variables for use.  $BIOSUpdateFile simply gets the BIOS Update File (the .exe) and stores all it’s properties/methods into this variable.  This is now an Object!

The next variable is $BIOSUpdateFileVersion.  This variable stores the actual file version of the BIOS Update File by first stripping off the extension of the file name, then by using the Substring() method to capture only the last three characters.  As you can see from the picture above, the Dell BIOS Update file uses the A## indicator for their BIOS version.

Once we’ve gathered this information, we can then use the .CompareTo() method to compare the installed BIOS Version ($BIOSUpdateFileVersion) and the BIOS Update File Version ($BIOSUpdateFileVersion).  If they are a match, we need not proceed further and can quit.  Otherwise, we need to execute the BIOS Update File silently and without reboot.  We do this by invoking the [System.Diagnostics.Process].  Here we can set the necessary arguments, window style (CreateNoWindow for OSD) and actually Start the process.

The magic of this script is that you don’t actually  have to update the script when adding new models or updating the BIOS version.  Simply add a new folder with the appropriate name, or replace the existing BIOS update file with a new one and the script will handle the rest.