MSEndpointMgr

SCCM Dell Client Bios & Driver Autodownload PowerShell Script

PLEASE NOTE: Some of the details and features in this post have been superseded. Please review our Modern Driver Management and Modern BIOS Management solutions for up to date information.

Original Post:

If you are a Dell hardware house then this script might come in useful for you.

As you are probably aware Dell provide excellent support for SCCM deployments via their Dell Command integration software and up to date driver cab files via their Enterprise Client Deployment site at https://en.community.dell.com/techcenter/enterprise-client/w/wiki/2065.dell-command-deploy-driver-packs-for-enterprise-client-os-deployment.

dellsccmscreen1

When I was refreshing my driver and bios update file repository I got thinking wouldn’t it be nice if I could just run a script that would download these update files based on the models of Dell client systems listed in my SCCM device collections?.

I found a script on Dustin Hedges blog (https://deploymentramblings.wordpress.com/2014/04/17/downloading-dell-driver-cab-files-automagically-with-the-driver-pack-catalog/) but I wanted to automate this further.

So here is my resulting effort. The below scripts requires you to specify your driver file share and your SCCM site server name as a variable, it then does the following;

  1. Queries SCCM for a full list of Dell enterprise client products (Optiplex & Latitude)
  2. Downloads BIOS updates for each model
  3. Downloads the driver CAB for each model
  4. Extract the driver CAB
  5. Import the drivers in the extracted CAB folder
  6. Create a Category based on the machine model
  7. Create a Driver Package based on the machine model and filename
  8. Imports the associated drivers into the newly created Driver Package
  9. Creates a BIOS Update Package based on machine model
  10. Creates a BIOS update deployment PowerShell script for each model using the latest BIOS update and silent switches

Progress bars have also been added for both the system model and driver import stage.

The downloads are stored within sub-folders within the share you specified, e.g;

\\MySCCMServer\Drivers\Dell Optiplex 7040\BIOS
\\MySCCMServer\Drivers\Dell Optiplex 7040\Driver Cabs\

Automatically created SCCM Driver Packages:

dellsccmscreen7

SCCM Driver Package Contents;
dellsccmscreen8

Multi-Threaded Script

To run the script use the following syntax;

.\DellDownloads.ps1 -SiteServer YOURSITESERVER -RepositoryPath “\\YOURSERVER\DRIVERREPO\” -PackagePath “\\YOURSERVER\DRIVERPACKPATH”

dellmultithread
Multi-Thread Script In Use (Running Code in PS Console)

Task Sequence BIOS Update Script

In the latest release BIOS packaging is included, it also generates a PowerShell script for use at deployment time which is contained within the BIOS folder of the model and uses the latest BIOS exe with silent switches for a silent upgrade.

Note: If you are using a BIOS setup password (which you should be), you will need to specify this within the script (unless you want me to update the script to look for this run running it from the shell).

Modify the following line – $BIOSSwitches = ” -noreboot -nopause /p=%YOURBIOSPASSWORD “

UPDATE LOG

08/11/2016
The script has been updated with the following functionality;

  1. Creates BIOS packages for each model downloaded
  2. Creates a deployment PowerShell script containing the latest BIOS exe name and switches for a silent / no reboot update of the BIOS
  3. If the script is re-run it will automatically update the BIOS exe to use in the deployment PS script and update the distribution points.

28/10/2016
As a response to feedback, I have added a $MaxConcurrent jobs variable into the multi-threaded script that lets you specify the max number of jobs in order to control CPU utilization.

26/10/2016
I have added in an additional script below which is multi-threaded. This should help reduce the overall time to download, extract and create the driver packages in large environments with a wide range of models.

16/10/2016
Additional functionality has been added to now automate the process of extracting the CAB, creating computer categories, import the drivers into SCCM and create a driver pack for each of the models / driver packs downloaded.

Script Download Link – https://gallery.technet.microsoft.com/scriptcenter/SCCM-Dell-Client-Bios-ee577b04?redir=0

<#
 .NOTES
 ===========================================================================
 Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.128
 Created on: 16/10/2016 13:00
 Created by: Maurice Daly
 Filename: DellDownloads.ps1
 ===========================================================================
 .DESCRIPTION
 This script allows you to automate the process of keeping your Dell
 driver and BIOS update sources up to date. The script reads the Dell
 SCCM driver pack site for models you have specified and then downloads
 the corresponding latest driver packs and BIOS updates.

 Version 1.0
 Retreive Dell models and download BIOS and Driver Packs
 Version 2.0
 Added driver CAB file extract, create new driver pack, category creation
 and import driver functions.
 Version 2.1
 Added multi-threading
 Version 2.2
 Added Max Concurrent jobs setting for limiting CPU utilisation
 Version 2.3
 Replaced Invoke-WebRequest download with BITS enabled downloads for
 improved performance
 Version 2.4
 Updated code and separated functions. Added required variables via commandline
 Version 3.0
 Creates BIOS Packages for each model and writes update powershell file for deployment
 with SCCM. 

 Notes
 You can skip the driver package creation process by changing the
 $DriverPackageCreation variable to $False.
 The system architecture can also be changed by modifying the
 $Architecture variable and using x64 or x86

 To re-enable error messaging for troubleshooting purpose
 comment out the Error and Warning Preference values below

 Use : This script is provided as it and I accept no responsibility for
 any issues arising from its use.

 Twitter : @modaly_it
 Blog : https://modalyitblog.com/
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
	

[parameter(Mandatory = $true, HelpMessage = “Site server where the SMS Provider is installed”, Position = 1)]

[ValidateNotNullOrEmpty()] [ValidateScript({ Test-Connection -ComputerName $_ -Count 1 -Quiet })] [string]$SiteServer,

[parameter(Mandatory = $true, HelpMessage = “UNC path for downloading and extracting drivers”)]

[ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path $_ })] [string]$RepositoryPath,

[parameter(Mandatory = $true, HelpMessage = “UNC path of your driver package repository”)]

[ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path $_ })] [string]$PackagePath ) $ErrorActionPreference = ‘SilentlyContinue’ $WarningPreference = ‘SilentlyContinue’ # Define Maximum Number Of Simultaneously Running Jobs $MaxConcurrentJobs = 5 $ErrorActionPreference = ‘SilentlyContinue’ $WarningPreference = ‘SilentlyContinue’ # Import SCCM PowerShell Module $ModuleName = (get-item $env:SMS_ADMIN_UI_PATH).parent.FullName + “\ConfigurationManager.psd1” Import-Module $ModuleName # Query SCCM Site Code function QuerySiteCode ($SiteServer) { Write-Debug “Determining SiteCode for Site Server: ‘$($SiteServer)'” $SiteCodeObjects = Get-WmiObject -Namespace “root\SMS” -Class SMS_ProviderLocation -ComputerName $SiteServer -ErrorAction Stop foreach ($SiteCodeObject in $SiteCodeObjects) { if ($SiteCodeObject.ProviderForLocalSite -eq $true) { $SiteCode = $SiteCodeObject.SiteCode Write-Debug “SiteCode: $($SiteCode)” } } Return [string]$SiteCode } function QueryModels ($SiteCode) { # ArrayList to store the Dell models in $DellProducts = New-Object -TypeName System.Collections.ArrayList # Enumerate through all models $Models = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_G_System_COMPUTER_SYSTEM | Select-Object -Property Model | Where-Object { ($_.Model -like “*Optiplex*”) -or ($_.Model -like “*Latitude*”) } # Add model to ArrayList if not present if ($Models -ne $null) { foreach ($Model in $Models) { if ($Model.Model -notin $DellProducts) { $DellProducts.Add($Model.Model) | Out-Null } } } Return $DellProducts } function StartDownloadAndPackage ($PackagePath, $RepositoryPath, $SiteCode, $DellProducts) { $RunDownloadJob = { Param ($Model, $SiteCode, $PackagePath, $RepositoryPath) # =================== DEFINE VARIABLES ===================== # Import SCCM PowerShell Module $ModuleName = (get-item $env:SMS_ADMIN_UI_PATH).parent.FullName + “\ConfigurationManager.psd1” Import-Module $ModuleName # Directory used for driver downloads $DriverRepositoryRoot = ($RepositoryPath.Trimend(“\”) + “\Dell\”) Write-Host “Driver package path set to $DriverRepositoryRoot” # Directory used by SCCM for driver package $DriverPackageRoot = $PackagePath Write-Host “Driver package path set to $DriverPackageRoot” # Define Operating System $OperatingSystem = “Windows 10” $Architecture = “x64” # Define Dell Download Sources $DellDownloadList = “https://downloads.dell.com/published/Pages/index.html” $DellDownloadBase = “https://downloads.dell.com” $DellSCCMDriverList = “https://en.community.dell.com/techcenter/enterprise-client/w/wiki/2065.dell-command-deploy-driver-packs-for-enterprise-client-os-deployment” $DellSCCMBase = “https://en.community.dell.com” # Import Driver Packs? $DriverPackCreation = $true # =================== INITIATE DOWNLOADS =================== # ============= BIOS Upgrade Download ================== Write-Host “Getting download URL for Dell client model: $Model” $ModelLink = (Invoke-WebRequest -Uri $DellDownloadList).Links | Where-Object { $_.outerText -eq $Model } $ModelURL = (Split-Path $DellDownloadList -Parent) + “/” + ($ModelLink.href) # Correct slash direction issues $ModelURL = $ModelURL.Replace(“\”, “/”) $BIOSDownload = (Invoke-WebRequest -Uri $ModelURL -UseBasicParsing).Links | Where-Object { ($_.outerHTML -like “*BIOS*”) -and ($_.outerHTML -like “*WINDOWS*”) } | select -First 1 $BIOSFile = $BIOSDownload.href | Split-Path -Leaf # Check for destination directory, create if required and download the BIOS upgrade file if ((Test-Path -Path ($DriverRepositoryRoot + $Model + “\BIOS”)) -eq $true) { if ((Test-Path -Path ($DriverRepositoryRoot + $Model + “\BIOS\” + $BIOSFile)) -eq $false) { Write-Host -ForegroundColor Green “Downloading $($BIOSFile) BIOS update file” # Invoke-WebRequest ($DellDownloadBase + $BIOSDownload.href) -OutFile ($DriverRepositoryRoot + $Model + “\BIOS\” + $BIOSFile) -UseBasicParsing Start-BitsTransfer ($DellDownloadBase + $BIOSDownload.href) -Destination ($DriverRepositoryRoot + $Model + “\BIOS\” + $BIOSFile) } else { Write-Host -ForegroundColor Yellow “Skipping $BIOSFile… File already downloaded…” } } else { Write-Host -ForegroundColor Green “Creating $Model download folder” New-Item -Type dir -Path ($DriverRepositoryRoot + $Model + “\BIOS”) Write-Host -ForegroundColor Green “Downloading $($BIOSFile) BIOS update file” # Invoke-WebRequest ($DellDownloadBase + $BIOSDownload.href) -OutFile ($DriverRepositoryRoot + $Model + “\BIOS\” + $BIOSFile) -UseBasicParsing Start-BitsTransfer ($DellDownloadBase + $BIOSDownload.href) -Destination ($DriverRepositoryRoot + $Model + “\BIOS\” + $BIOSFile) } # ============= SCCM Driver Cab Download ================== Write-Host “Getting SCCM driver pack link for model: $Model” $ModelLink = (Invoke-WebRequest -Uri $DellSCCMDriverList -UseBasicParsing).Links | Where-Object { ($_.outerHTML -like “*$Model*”) -and ($_.outerHTML -like “*$OperatingSystem*”) } | select -First 1 $ModelURL = $DellSCCMBase + ($ModelLink.href) # Correct slash direction issues $ModelURL = $ModelURL.Replace(“\”, “/”) $SCCMDriverDownload = (Invoke-WebRequest -Uri $ModelURL -UseBasicParsing).Links | Where-Object { $_.href -like “*.cab” } $SCCMDriverCab = $SCCMDriverDownload.href | Split-Path -Leaf # Check for destination directory, create if required and download the driver cab if ((Test-Path -Path ($DriverRepositoryRoot + $Model + “\Driver Cab\”)) -eq $true) { if ((Test-Path -Path ($DriverRepositoryRoot + $Model + “\Driver Cab\” + $SCCMDriverCab)) -eq $false) { Write-Host -ForegroundColor Green “Downloading $($SCCMDriverCab) driver cab file” # Invoke-WebRequest ($SCCMDriverDownload.href) -OutFile ($DriverRepositoryRoot + $Model + “\Driver Cab\” + $SCCMDriverCab) -UseBasicParsing Start-BitsTransfer -Source ($SCCMDriverDownload.href) -Destination ($DriverRepositoryRoot + $Model + “\Driver Cab\” + $SCCMDriverCab) $SkipDriver = $false } else { Write-Host -ForegroundColor Yellow “Skipping $SCCMDriverCab… File already downloaded…” $SkipDriver = $true } } else { Write-Host -ForegroundColor Green “Creating $Model download folder” New-Item -Type dir -Path ($DriverRepositoryRoot + $Model + “\Driver Cab”) Write-Host -ForegroundColor Green “Downloading $($SCCMDriverCab) driver cab file” #Invoke-WebRequest ($SCCMDriverDownload.href) -OutFile ($DriverRepositoryRoot + $Model + “\Driver Cab\” + $SCCMDriverCab) Start-BitsTransfer -Source ($SCCMDriverDownload.href) -Destination ($DriverRepositoryRoot + $Model + “\Driver Cab\” + $SCCMDriverCab) } # =================== CREATE BIOS UPDATE PACKAGE =========================== $BIOSUpdatePackage = (“Dell” + ” ” + $Model + ” ” + “BIOS UPDATE”) $BIOSUpdateRoot = ($DriverRepositoryRoot + $Model + “\BIOS\”) Set-Location -Path ($SiteCode + “:”) if ((Get-CMPackage -name $BIOSUpdatePackage) -eq $null) { Write-Host -ForegroundColor Green “Creating BIOS Package” New-CMPackage -Name “$BIOSUpdatePackage” -Path $BIOSUpdateRoot -Description “Dell $Model BIOS Updates” -Manufacturer “Dell” -Language English } Set-Location -Path $env:SystemDrive $BIOSUpdateScript = ($BIOSUpdateRoot + “BIOSUpdate.ps1”) $CurrentBIOSFile = Get-ChildItem -Path $BIOSUpdateRoot -Filter *.exe -Recurse | Sort-Object $_.LastWriteTime | select -First 1 if ((Test-Path -Path $BIOSUpdateScript) -eq $False) { # Create BIOSUpdate.ps1 Deployment Script New-Item -Path ($BIOSUpdateRoot + “BIOSUpdate.ps1″) -ItemType File $BIOSSwitches = ” -noreboot -nopause ” Add-Content -Path $BIOSUpdateScript (‘$CurrentBIOSFile=’ + ‘”‘ + $($CurrentBIOSFile.name) + ‘”‘) Add-Content -Path $BIOSUpdateScript (‘$BIOSSwitches=’ + ‘”‘ + $($BIOSSwitches) + ‘”‘) Add-Content -Path $BIOSUpdateScript (‘Start-Process $CurrentBIOSFile -ArgumentList $BIOSSwitches’) } else { # Check if older BIOS update exists and update BIOSUpdate deployment script $BIOSFileCount = (Get-ChildItem -Path $BIOSUpdateRoot -Filter *.exe -Recurse).count if ($BIOSFileCount -gt 1) { $OldBIOSFiles = Get-ChildItem -Path $BIOSUpdateRoot -Filter *.exe -Recurse | Where-Object { $_.Name -ne $CurrentBIOSFile.name } foreach ($OldBIOS in $OldBIOSFiles) { (Get-Content -Path $BIOSUpdateScript) -replace $OldBIOS.name, $CurrentBIOSFile.name | Set-Content -Path $BIOSUpdateScript } } } # Refresh Distribution Points Get-CMPackage -name $BIOSUpdatePackage | Update-CMDistributionPoint } # =================== CREATE DRIVER PACKAGE AND IMPORT DRIVERS =================== $DriverSourceCab = ($DriverRepositoryRoot + $Model + “\Driver Cab\” + $SCCMDriverCab) $DriverExtractDest = ($DriverRepositoryRoot + $Model + “\Extracted Drivers”) $DriverPackageDir = ($DriverSourceCab | Split-Path -Leaf) $DriverPackageDir = $DriverPackageDir.Substring(0, $DriverPackageDir.length – 4) $DriverCabDest = $DriverPackageRoot + “\Dell\” + $DriverPackageDir if ($DriverPackCreation -eq $true) { if ((Test-Path -Path $DriverExtractDest) -eq $false) { New-Item -Type dir -Path $DriverExtractDest } else { Get-ChildItem -Path $DriverExtractDest -Recurse | Remove-Item -Recurse -Force } New-Item -Type dir -Path $DriverCabDest Set-Location -Path ($SiteCode + “:”) $CMDDriverPackage = “Dell ” + $Model + ” ” + “(” + $DriverPackageDir + “)” + ” ” + $Architecture if (Get-CMDriverPackage -Name $CMDDriverPackage) { Write-Host -ForegroundColor Yellow “Skipping.. Driver package already exists..” } else { Write-Host -ForegroundColor Green “Creating driver package” Set-Location -Path $env:SystemDrive Expand “$DriverSourceCab” -F:* “$DriverExtractDest” $DriverINFFiles = Get-ChildItem -Path $DriverExtractDest -Recurse -Filter “*.inf” | Where-Object { $_.FullName -like “*$Architecture*” } Set-Location -Path ($SiteCode + “:”) # Get-Location | Out-File -FilePath C:\Location2.txt New-CMDriverPackage -Name $CMDDriverPackage -path ($DriverPackageRoot + “\Dell\” + $DriverPackageDir + “\” + $OperatingSystem + “\” + $Architecture) if (Get-CMCategory -CategoryType DriverCategories -name (“Dell ” + $Model)) { Write-Host -ForegroundColor Yellow “Category already exists” $DriverCategory = Get-CMCategory -CategoryType DriverCategories -name (“Dell ” + $Model) } else { Write-Host -ForegroundColor Green “Creating category” $DriverCategory = New-CMCategory -CategoryType DriverCategories -name (“Dell ” + $Model) } $DriverPackage = Get-CMDriverPackage -Name $CMDDriverPackage foreach ($DriverINF in $DriverINFFiles) { $DriverInfo = Import-CMDriver -UncFileLocation ($DriverINF.FullName) -ImportDuplicateDriverOption AppendCategory -EnableAndAllowInstall $True -AdministrativeCategory $DriverCategory | Select-Object * Add-CMDriverToDriverPackage -DriverID $DriverInfo.CI_ID -DriverPackageName $CMDDriverPackage } } Set-Location -Path $env:SystemDrive } $TotalModelCount = $DellProducts.Count $RemainingModels = $TotalModelCount foreach ($Model in $DellProducts) { write-progress -activity “Initiate Driver Download &amp;amp;amp; Driver Package Jobs” -status “Progress:” -percentcomplete (($TotalModelCount – $RemainingModels)/$TotalModelCount * 100) $RemainingModels– $Check = $false while ($Check -eq $false) { if ((Get-Job -State ‘Running’).Count -lt $MaxConcurrentJobs) { Start-Job -ScriptBlock $RunDownloadJob -ArgumentList $Model, $SiteCode, $PackagePath, $RepositoryPath -Name ($Model + ” Download”) $Check = $true } } } Get-Job | Wait-Job | Receive-Job Get-Job | Remove-Job } # Get SCCM Site Code $SiteCode = QuerySiteCode ($SiteServer) Write-Debug $PackagePath Write-Debug $RepositoryPath if ($SiteCode -ne $null) { # Query Dell Products in SCCM using QueryModels function $DellProducts = QueryModels ($SiteCode) # Output the members of the ArrayList if ($DellProducts.Count -ge 1) { foreach ($ModelItem in $DellProducts) { $PSObject = [PSCustomObject]@{ “Dell Models Found” = $ModelItem } Write-Output $PSObject Write-Debug $PSObject } } # Start download, extract, import and package process Write-Host -ForegroundColor Green “Starting download, extract, import and driver package build process..” StartDownloadAndPackage ($PackagePath) ($RepositoryPath) ($SiteCode) ($DellProducts) }

Maurice Daly

Maurice has been working in the IT industry for the past 20 years and currently working in the role of Senior Cloud Architect with CloudWay. With a focus on OS deployment through SCCM/MDT, group policies, active directory, virtualisation and office 365, Maurice has been a Windows Server MCSE since 2008 and was awarded Enterprise Mobility MVP in March 2017. Most recently his focus has been on automation of deployment tasks, creating and sharing PowerShell scripts and other content to help others streamline their deployment processes.

13 comments

  • Just a quick update for everyone following this: Maurice has been a huge help today and helped me via Teamviewer. I started the script in PowerShell ISE instead of the normal PowerShell console. That was the problem, now everything is working. 🙂

    • Hi Kevin,

      Thanks for the feedback by email. I have added in the requested Max Concurrent jobs variable so you can now limit your CPU utilization during the download / package phase.

  • It’s not working, I just copied it again. I also get this in line 49:

    + Begin
    + ~~~~~
    + CategoryInfo : ObjectNotFound: (Begin:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

  • Hi Maurice,

    yes I’m running the script on the site server. I’m using the multi-threaded script.

    Thanks!

    Best regards
    Kevin

    • Hi Kevin,

      OK so stepping through this one step at a time, if you run lines 38-97 do you get any returned values for the $DellProducts or $Models arrays?.

      Maurice

    • Actually Kevin it looks like that there might have been an issue pasting my script into WordPress. I have just pasted in the script again and run it with no issue. Can you either copy it off of the site or I can email you a copy.

  • Hi there, first of all thanks for this script. I can’t get it to work, here is what happens. I edit all the variables (SCCM Server, Path to the ConfigurationManager.psd1 because our SCCM is setup on the D: drive and source share path.)

    When I run the script, it gives me the following output and nothing else happens:

    # Determine SiteCode from WMI
    try
    {
    Write-Verbose “Determining SiteCode for Site Server: ‘$($SiteServer)'”
    $SiteCodeObjects = Get-WmiObject -Namespace “rootSMS” -Class SMS_ProviderLocation -ComputerName $SiteServer -ErrorAction Stop
    foreach ($SiteCodeObject in $SiteCodeObjects)
    {
    if ($SiteCodeObject.ProviderForLocalSite -eq $true)
    {
    $SiteCode = $SiteCodeObject.SiteCode
    Write-Debug “SiteCode: $($SiteCode)”
    }
    }
    }
    catch [Exception] {
    Throw “Unable to determine SiteCode”
    }

    Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
    ——- —— —– —– —– —— — ———–
    439 36 146676 132868 1597 86,28 4328 AFD2DMonitor
    323 14 8896 14932 68 7,80 5644 AgPkiMon
    2345 31 28252 32480 547 1,52 1256 AIUpdateSVC
    ….
    ….

    # ArrayList to store the Dell models in
    $DellProducts = New-Object -TypeName System.Collections.ArrayList
    # Enumerate through all models
    $Models = Get-WmiObject -Namespace “rootSMSsite_$($SiteCode)” -Class SMS_G_System_COMPUTER_SYSTEM | Select-Object -Property Model | Where-Object { ($_.Model -like
    “*Optiplex*”) -or ($_.Model -like “*Latitude*”) }
    # Add model to ArrayList if not present
    if ($Models -ne $null)
    {
    foreach ($Model in $Models)
    {
    if ($Model.Model -notin $DellProducts)
    {
    $DellProducts.Add($Model.Model) | Out-Null
    }
    }
    }
    # Output the members of the ArrayList
    if ($DellProducts.Count -ge 1)
    {
    foreach ($ModelItem in $DellProducts)
    {
    $PSObject = [PSCustomObject]@{
    Model = $ModelItem
    }
    Write-Output $PSObject
    }
    }

    PS C:Windowssystem32>

    I also tried to set the site code manually because apparently the script can’t find it correctly but it doesn’t help either.

    Best regards
    Kevin

    • Hi Kevin,

      Just checking first of all that you are running the script on your Site Server?. Can you also let me know if you are running the single or multi-threaded script.

      Regards

      Maurice

Sponsors