Wednesday, January 18, 2017

Powershell Script to Deploy Dell BIOS/Firmware Updates to Computers in the Wild using SCCM

Update 1/31/2017 - I've joined the world and opened a GitHub account. I've posted the XML and my Script there. Makes it much easier to share.

https://github.com/dptechjournal/Dell-Firmware-Updates

Update - 1/27/2017, Script has been modified to create a scheduled task instead of a registry entry in Windows 7, and I changed the -contains parameter to the more appropriate -match parameter.

Update 1/27/2017 - I am testing modifying the script in the Windows 7 section to add a scheduled task on startup, that does two things, one, re-enables bitlocker on startup, and two,  removes itself from scheduled tasks. As Jay pointed out in the comments, with the current script that adds the reg key to HKLM it only re-enables bitlocker when a user logs in, and it runs with the user's permissions. I rather not have bitlocked machines be unprotected between the firmware update and a user logging in. So moving it to Scheduled Tasks on startup seems the way to go. I will post the updated script when it is finished, along with the XML for the scheduled task.

BIOS (or Firmware) updates are rated “Recommended” or "Urgent" by Dell, which means you should probably be performing them regularly. I find they solve a lot of little issues that we see with hardware, and also solve issues with BitLocker. For instance, on the OptiPlex 9010, BitLocker protection would activate after plugging in an additional monitor to a desktop. Updating the BIOS version fixed the issue. There was a time when a BIOS update that went sideways could basically brick your computer, but those days appear to be past. I have successfully updated a few thousand Dell computer BIOS’s in the wild over the past year using the script below without losing a single one. I will say I stick to flashing newer hardware of the past 4 years. I do not try to update GX 280’s for instance. Here is a list of the Dell Models I have successfully been updating:

We use a two pronged approach to updating our BIOS’s. During task sequence imaging we use a package to update the BIOS’s, the package suppresses the reboot, allowing the Task Sequence to control the reboot at a later stage. There are many blog posts about how to accomplish this type of update during a task sequence so I will just focus on the script we use for deploying BIOS updates to computers in the wild. The SCCM application we use to deploy this script is set to only run when no user is logged in. We time the deployments with our monthly Windows updates, which initiate a 10 hour countdown to reboot after installation. This ensures that a majority of desktops and a portion of laptops get the BIOS updates about 10 hours after deployment. The monthly Windows updates are due after work hours, the computers install the updates, reboot, and now have no user logged in. They then install the BIOS update and reboot again. The rest of the computers get the BIOS updates as the application catches the computer without a logged in user. This is hit and miss, but eventually works it way to most of the other computers. Twice I have heard someone mention seeing the Bios flashing and reboot and asked about it, but most of the time the end user either wont be there or wont see it.

Now you may ask, why not just deploy the bios update and suppress the reboot? Well, we tried that, and found that if a computer installed a bios update and went to sleep before rebooting to finish the update, it would cause an error when Windows resumed from sleep. Therefore I created this script to try to handle any contingencies I could think of.

The script has gone through a number of versions before I felt it was ready to share. It performs the following functions, while logging steps to the computers Application Log, so we can check for certain error codes in SCOM.
  1. Checks if computer is a laptop, if it is, exits script if battery life is less than 30%.
  2. Gets the OS of the Computer.
    1. For Windows 7 – Uses Manage-bde.exe to suspend BitLocker if the C: drive is BitLocked. We found that computers that are not BitLocked but ran the -disable command, informed the user that BitLocker could not be resumed upon logon, even though its not Bitlocked to being with. So we only run the suspend command if its actually BitLocked. Also writes to the RunOnce registry key a command to re-enable BitLocker on next startup. Also adds a Scheduled Task to enable bitlocker on startup, and then remove itself. This was necessary because in rare cases, BitLocker did not resume on its own after using the “Manage-bde.exe –protectors –disable” command.
    2. Windows 8 and 10 – Uses PowerShell commands to check for and disable BitLocker on the volume with the Operating System.
  3. Checks if BitLocker was successfully suspended. If it was not, writes the error to the Application Log and exists. I have not seen this error happen yet.
  4. Updates the BIOS and forces a reboot of the machine, which re-enables Bitlocker
We have Collections for each Hardware Model we have. I use those as  limiting collections for the Updating Collection that I will use to deploy the application script, with a query looking for older bio’s versions that need updating. I do this for each model I am updating. Here is an example of my Optiplex 3020 shrinking collection (models that are up to date get removed from the collection based a query), of which we have 575 of this model.  You can see, only 29 still have a BIOS that needs updating. (Most of them haven't been online, or in rare cases, the user is one who turns off the computer every night before leaving, so its never had time to install when no user is logged in. Eventually I will get them!)



Create an XML file named "sTask_Details.xml" to contain the Scheduled Task that will get imported when the script is ran. This will create 2 actions on the startup trigger. The first action will enable bitlocker, the 2nd action will remove the tasks from the task scheduler. Copy and pasting from the code below wont work, either recreate the task on a machine and export it as an XML, or better yet, grab it from my GitHub repository. https://github.com/dptechjournal/Dell-Firmware-Updates


  
    2017-01-30T08:47:18.2600256
    dave
  
  
    
      true
    
  
  
    
      S-1-5-18
      LeastPrivilege
    
  
  
    IgnoreNew
    false
    true
    true
    false
    false
    
      true
      false
    
    true
    true
    false
    false
    false
    false
    false
    P3D
    7
  
  
    
      Manage-bde.exe
      -protectors -enable c:
    
    
      cmd
      /c schtasks /delete /f /tn "Bitlock"
    
  


Here is the script itself, replace "Latitude_E7x70_1.12.3.exe" on the last line with the BIOS Update executable you want to run. If you have a Firmware Password, put it in the $args, replace /p=password with your firmware password. Yes this means that your firmware password is in your SCCM cache. But what can you do huh? The script is also at my GitHub repository https://github.com/dptechjournal/Dell-Firmware-Updates
<# 
 .NOTES
 ==========================================================================
  Created on:    3/25/2016 12:44 PM
  Created by:    David Pearson http://www.dptechjournal.net
 ===========================================================================
 .DESCRIPTION
  Installs BIOS Update's for Dell Computers.
  1. Determines the OS of the computer
  2. Suspends Bitlocker if needed
  3. Updates BIOS
  4. Writes Log to Application Log
 Function Get-Laptop from https://blogs.technet.microsoft.com/heyscriptingguy/2010/05/15/hey-scripting-guy-weekend-scripter-how-can-i-use-wmi-to-detect-laptops/
#>
Function Get-Laptop
{
 Param (
  [string]$computer = “localhost”
 )
 $isLaptop = $false
 if (Get-WmiObject -Class win32_systemenclosure -ComputerName $computer |
 Where-Object {
  $_.chassistypes -eq 9 -or $_.chassistypes -eq 10 `
  -or $_.chassistypes -eq 14
 })
 { $isLaptop = $true }
 if (Get-WmiObject -Class win32_battery -ComputerName $computer)
 { $isLaptop = $true }
 $isLaptop
} # end function Get-Laptop
$currentDirectory = split-path -parent $MyInvocation.MyCommand.Definition

# Setup Logging
$ErrorActionPreference = "SilentlyContinue"
if (!(Get-Eventlog -LogName "Application" -Source "ConfigMgr Team"))
{
 New-Eventlog -LogName "Application" -Source "ConfigMgr Team" | Out-Null
}
$ErrorActionPreference = "Continue"


Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 1 -Message "ConfigMgr detected No User Logged In, Starting BIOS Upgrade Script."

# Check if Laptop, exit if Battery life is less than 30
If (get-Laptop)
{
 Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 3 -Message "Laptop detected, checking battery life."
 if ((Get-WmiObject win32_battery).estimatedChargeRemaining -le 30)
 {
  Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 4 -Message "Battery below 30%, canceling BIOS Upgrade...this time."
  exit
 }
}

# Get Operating System version and performs the appropriate actions to bitlocker
[int]$computerOS = (Get-WmiObject -namespace Root\cimv2 -Query "SELECT BuildNumber FROM win32_operatingSystem").buildnumber


if ($computerOS -ge 8000)
{
 $drive = Get-BitLockerVolume | where { $_.ProtectionStatus -eq "On" -and $_.VolumeType -eq "OperatingSystem" }
    if ($drive)
 {
     Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 2 -Message "Attempting to Suspend Bitlocker on drive $drive."
     Suspend-BitLocker -Mountpoint $drive -RebootCount 1
     if (Get-BitLockerVolume -MountPoint $drive | where ProtectionStatus -eq "On")
     {
      #Bitlocker Suspend Failed, Exit Script
      Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 13 -Message "Failed to Suspend Bitlocker on drive $drive , Exiting."
      exit
     }
    }
}
else
{
 $drive = manage-bde.exe -status c:
 if ($drive -match "    Protection Status:    Protection On")
 {
  Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 2 -Message "Attempting to Suspend Bitlocker on drive C: ."
  manage-bde.exe -protectors -disable c:
  $verifydrive = manage-bde.exe -status c:
  if ($verifydrive -match "    Protection Status:    Protection On")
  {
   #Bitlocker Suspend Failed, Exit Script
   Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 13 -Message "Failed to Suspend Bitlocker on drive C: , Exiting."
   exit
  }
  # Create a Scheduled Task to resume Bitlocker on startup, then remove
  cmd /c schtasks /create /f /tn "Bitlock" /XML $currentDirectory\sTask_Details.xml
 }
 
}

#Install BIOS Update
Write-EventLog -LogName "Application" -Source "ConfigMgr Team" -EntryType "Information" -EventId 7 -Message "Configmgr starting BIOS Update and rebooting. For more information, examine update.log in $currentDirectory"

$args = "/s /r /f /p=password /l=$currentDirectory\Update.log"
$install = Start-Process Latitude_E7x70_1.12.3.exe -ArgumentList $args -WorkingDirectory $currentDirectory -Wait


Name the script "Install_Dell_Bios_upgrade.ps1" and put it in your application source with the Bios Update executable and your XML file that holds the Scheduled Tasks details.

Here are the screenshots of how I setup my Application in ConfigMgr. The installation program install line is
powershell.exe -ExecutionPolicy Unrestricted -File "Install_Dell_Bios_upgrade.ps1"



My detection method checks the BIOS version in the registry, this briefly makes the application report that it failed to install as this does not update until after the reboot, but I am only concerned with the BIOS not upgrading if its already at the correct version.
I should mention that the parameters in the install command WILL NOT install the Firmware update if a laptop is not plugged into power. If you are feeling adventurous, you can add the switch to force the update, usually its /f . I am debating adding this, as I will rely on the estimated battery life part of the script to ensure there is enough juice left for the update to happen. That would go a long way towards getting the update to laptops in a more timely manner.


21 comments:

  1. This is really nice I like your usage of the SCCM application console and bios version detection rule. You are braver than most deploying bios updates to alrady built PC in production. We still do all our BIOS update at OSD. I have a dynamic method I'm going to open source very soon so you do not need to hard code a model for each application or script.

    ReplyDelete
  2. Hello,

    I have tested your code and there are a few issues for windows 7:
    You need to replace -contains with -match or you end up at the bitlocker screen as contains doesn't work and the checks never run.

    The runOnce to re-enable bitlocker will only work if the user is an Administrator.

    ReplyDelete
    Replies
    1. Hello Jay, The script is meant to be deployed in SCCM 2012 or Current Branch, as an Application targeting computers, therefore it runs as system. It is not meant to be run by a user, administrator or otherwise.
      The -contains parameter has been working with Powershell 5 on Windows 7, suspending bitlocker and writing to the logs at the appropriate steps. I have not tested it with previous versions of Powershell, but you are right, the -match parameter may be more appropriate.

      Delete
  3. I'm testing using app model as system. The run once step won't enable bit locker as that runs with user permissions- I am going to test either mbam or sccm compliance scripts to re enable bit locker. Also I've modified the script to accept a parameter for the firmware exe so you can use the same script for every model. I'll put an updated script once finished on GitHub if you'd like

    ReplyDelete
    Replies
    1. I understand what your saying now. I was under the impression the HKLM key would run once per startup, but it instead runs once the next time any user logs in. Which would be too late. That part of the script is therefore doing nothing helpful. I will modify the script. There was only a few cases where we had seen Windows 7 not re-enable bitlocker after using managed-bde to suspend and rebooting. I could never duplicate it in testing and we haven't seen it since.

      Delete
    2. Cool. I don't see how manage-bde will ever resume without intervention unless you use pin bitlocker (which SCCM supports resuming) or something else managing bitlocker

      Delete
    3. I've modified the script to create a scheduled task that resumes bitlocker on startup, and then removes the scheduled task. This re-eneables bitlocker without needing an administrative user to log in.

      Delete
    4. Nice Solution!

      The XML above for the scheduled task is corrupt, ie commands aren't in the command tags, and a couple of other things.

      I re-created the XML and tested successfully. Uploaded to git hub.

      From initial testing (Optiplex 9020) I've found the Version Reg Data Type to be a string instead of version, and I'm using the operator Equals, Value A17

      I got a bit OCD and renamed the script to Install_Dell_Firmware_update.ps1

      I've modified the script to accept -FirmwareFile Parameter so the command would be:
      powershell.exe -ExecutionPolicy Unrestricted -File Install_Dell_Firmware_update.ps1 -FirmwareFile O9020A17.exe


      https://github.com/happysccm/Files/tree/master/Deploy%20Dell%20Firmware%20-%20Modified%20David%20Pearsons%20Script

      Delete
  4. The principal thing that you should do is find the 3 stick CMOS secret key and reset jumper. It is ordinarily on the motherboard. On the off chance that you have no involvement in taking care of PC equipment, you ought to call a qualified expert to take the necessary steps for you. visit here

    ReplyDelete
  5. You could change the detection method to data type Version and use Greater than or equal to. That would make it more flexible so that it will not install if the computer has a newer BIOS-version.
    Data type Version is great in this case as Dell is naming their BIOS-version as 1.15.4 etc.

    ReplyDelete
  6. You actually make it appear really easy along with your presentation however I to find this matter to be really one thing that I think I'd never understand. It kind of feels too complicated and very wide for me. I'm having a look ahead to your next submit, I will attempt to get the dangle of it! gmail sign in

    ReplyDelete
  7. Positive site, where did u come up with the information on this posting? I'm pleased I discovered it though, ill be checking back soon to find out what additional posts you include.
    Magisk Manager

    ReplyDelete
  8. you can watch all these shows without paying anything from your pocket because all of them are free and you will not have to take out any of the amount https://hotstickybun.com/ipvanish-for-kodi/

    ReplyDelete
  9. How best to hit a lucrative manage your creation is dependent upon you however a couple of supportive tips can help you in your undertakings. script coverage

    ReplyDelete
  10. They bear colossal obligation regarding benefit making, since having the eye to purchase the correct item for deals can have any kind of effect in income.
    adlist24.com

    ReplyDelete
  11. Thanks for sharing the info, keep up the good work going.... I really enjoyed exploring your site. good resource... The Best VPN

    ReplyDelete
  12. Each word composed has charmed its group of onlookers in the most one of a kind way.
    best cell phone number lookup reviews

    ReplyDelete
    Replies
    1. Apart from the spam link, what a nice reply!

      Delete
  13. Nowadays, people are getting busy in their tight schedule to get money. Consequently, they have no spare time to enjoy, whereas everyone does want to have it. However with Manna-tech you can manage your time efficiently.
    best stuff

    ReplyDelete
  14. Thanks so much for this information. I have to let you know I concur on several of the points you make here and others may require some further review, but I can see your viewpoint. gbwhatsapp for iphone 6

    ReplyDelete