Updating BIOS & Drivers using a Task Sequence, OSD, IPU, Stand alone.
Why DCU (Dell Command Update)? Why not driver packages? First off, this solution isn’t right for some, and frankly is a horrible solution in many environments, but for others, it can be a life saver. If you don’t have time to keep all of your driver packages updated for your 30,40,50 + Dell models in your environment, if you have good internet bandwidth on the clients, and you just want to make sure your machines are at the latest BIOS / Drivers at time of OSD, then this works pretty well.
Disclaimer… I use this in my Lab, I have a Dell 7470, so that’s the extent of my testing. I used to use this at my previous organization, with 30+ models and worked very well. It’s handy because if a new model comes in the door, you don’t have any prep work, connect it up, and image, drivers and bios come right from Dell.
Other notes, this builds off my original post a few years back (when I used it in production). There is some other tips there that might come in handy if you run into any issue with brand new models requiring storage drivers or network drivers: https://garytown.com/say-goodbye-to-dell-driver-management-use-dell-command-update-in-osd
I’ve currently tested running this Task Sequence as a child TS in OSD & IPU scenarios, as well as just deploying the TS itself to a device and running it stand alone. This works in OSD after the OS is laid down, it does not support WinPE, so if you want to update your BIOS in the WinPE stage… sorry.
Please report any observations or tips in the comments, thanks. Other observations I’ve made about DCU: https://garytown.com/dell-command-update-tips-for-enterprise-automation
Requirements: CCTK Package, (more about that later), Dell Command Update Package 2.4 (or use the step I created that downloads and installs DCU from the internet), Custom Policy.XML files (I’ve included mine in the download), ideally some idea of the Task Sequences I’ve provided for WaaS to understand the reporting steps.
Do not use anything newer then 2.4 (at this time), 3.X currently has no CLI support. I hear they will be updating 3.x to include CLI, but no ETA. I have asked for many CLI enhancements, which would remove the need to even import a policy, as well as provide a BIOS password and have DCU suspend bitlocker with a parameter. I’m still waiting on all that. 🙂
Now to the goods…
Download: Dell Command Update Task Sequence (7442 downloads )
What this Task Sequence Does (High Level):
- Checks for DCU and Installs if DCU is not installed then reboots
- Creates Folders for Logs & DCU Templates, the copies down my templates
- Creates Variables used later in reporting
- Check if BIOS Update Available
- Import BIOS / FIRMWARE DCU Template
- Runs DCU in Report Mode, generating a Report only if update exist
- Check if Report exist, if Yes, Run DCU BIOS Group
- if No Report, skip group to cleanup
- Update Group Runs
- Gets Info (Gather)
- Records Current BIOS info (for Reporting)
- Removes Current BIOS Password (CCTK)
- Suspend Bitlocker (if Enabled)
- Runs DCU to Update BIOS
- Adds BIOS Password back
- Reboots Machine
- Records new BIOS info (for Reporting)
- Enables Bitlocker (if Bitlocker is used)
- Clean Up
- Clears out the DCU Policy
- Check for Driver Updates
- Import Driver DCU Template
- Run DCU in Report Only mode
- if Report is generated run DCU Update group, else skip
- DCU Updates
- Run DCU and install all available Drivers
- Reboots
- DCU Cleanup
- Cleans up folders and files used
- Imports DCU Defaults Policy
- Run Script to record results to registry (For Reporting)
So that is the basics of it. This was the easiest way I could do it to control the reboot, so it would only reboot if updates were installed, it also creates a nice report (if updates are available) of what updates were available, and hence installed. I also separate the BIOS from Drivers for tighter control. I only wanted the BIOS Password removed for the shortest possible time, which was by doing it this way.
Another reason I did it this was to show it can be modular, you don’t have to download and install everything Dell is pushing, you can pick and choose. You can greatly customize your Policy XML file to have it do other things as well, like Dell Command Suite Apps, etc. I’d recommend spending time in the GUI changing settings, run the scan, see what you get, then change it, scan again to see the differences. For my environment, I basically narrowed it down to BIOS/FIRMWARE settings check boxes for one Policy template, and Drivers Category, and all sub categories checked for my Drivers Policy. Check out my previous post for more info on that.
Steps & Conditions
Few notes about some odd things… All Steps that call dcu-cli.exe, I’ve set to continue on error. Importing a Policy always kills the TS and throws error, but I’ve noticed it does import the policy despite the error. I’ve also had it error on running the updates, returning non-0 exit codes, so I’ve set those to continue as well. Also, to get the dcu-cli to run, I had to do cmd.exe /c start /wait in front of the command or I’d get a really messed up error, I’ll try to record that here later once I reproduce the issue. It was odd, everything worked perfectly in Elevated Command Prompt, but not during a TS.. that fixed it.
- Dell Command Update
WMI Query: select * from Win32_ComputerSystem where Manufacturer like “%Dell%” - Install DCU
- Set TSVar DCU Installed
- DCUInstalled = TRUE
Condition: File exist: C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe
- DCUInstalled = TRUE
- Set TS Var “SMSTS_UpdateTime”
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('SMSTS_UpdateTime') = Get-Date -Format yyyyMMddTHHmmss}"
- Install DCU 2.4.0 from Internet
Condition Var DCUInstall not Truepowershell.exe -command "Invoke-WebRequest -Uri "https://downloads.dell.com/FOLDER05055451M/1/Dell-Command-Update_DDVDP_WIN_2.4.0_A00.EXE" -OutFile "$env:temp\DCU.exe" -UseBasicParsing -Verbose ; start-process -FilePath "$env:temp\DCU.exe" -ArgumentList '/s /f'"
- Restart Computer if DCU Installed
Condition Var DCUINstalled not True - Make Folder for DCU Results
cmd.exe /c if Not Exist "%programdata%\DCU\DCUSettings" (md %programdata%\DCU\DCUSettings)
- Make Folder DCULogs for Logs
cmd.exe /c if Not Exist "%programdata%\DCULogs" (md %programdata%\DCULogs)
- Copy XML File Local
Package = your package with the DCU Policy Setting XML filesxcopy *.xml %programdata%\DCU\DCUSettings /Y
- Set Var SetOSDInfoType = DCU
- SetOSDInfoType = True
Condition: SetOSDInfoType not exist
- SetOSDInfoType = True
- Set TS Var “SMSTS_Build” if not set
- Condition: SMSTS_Build not exist
powershell.exe -command "$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('SMSTS_Build') = Get-ItemPropertyValue 'HKLM:SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion' 'Releaseid'"
- Condition: SMSTS_Build not exist
- Set TSVar DCU Installed
- Run DCU BIOS
- Set TS Var “SMSTS_BIOSStartTime”
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('SMSTS_BIOSStartTime') = Get-Date -f 's'}"
- Set TS Var “SMSTS_BIOSStartTime”
- Run DCU Report if BIOS upgrade available
- Modify Dell DCU – Import Settings – BIOS
cmd.exe /c start /wait C:\"Program Files (x86)"\Dell\CommandUpdate\dcu-cli.exe /import /policy %programdata%\DCU\DCUSettings\BIOSFirmware.xml
- Run Dell Command Update Report Only (BIOS)
cmd.exe /c start /wait C:\"Program Files (x86)"\Dell\CommandUpdate\dcu-cli.exe /report c:\ProgramData\DCU\DCUUpdatesBIOS.xml
- Copy Report to Log Folder
cmd.exe /c if Exist "%programdata%\DCU\DCUUpdatesBIOS.xml" (copy %programdata%\DCU\DCUUpdatesBIOS.xml %programdata%\DCULogs\DCUUpdatesBIOS%SMSTS_UpdateTime%.xml)
- Modify Dell DCU – Import Settings – BIOS
- Run DCU if BIOS Updates exist (Condition: File: %programdata%\DCU\DCUUpdatesBIOS.xml Exist)
- Gather Variables – Run PS Script from Gallery
- Set TS Var CurrentBiosVer
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('CurrentBiosVer') = (Get-WmiObject win32_bios).SMBIOSBIOSVersion}"
- Dell BIOS – Enable HAPI – HAPI\HAPIInstall.bat
Requires your CCTK Package - Start OSDDoNotLogCommand: TSVar: OSDDoNotLogCommand = True
- Set Password Variable – BIOSPWD = YOURBIOSPASSWORD
- Dell BIOS – Remove Password: Requires your CCTK Package
cctk --setuppwd="" --valsetuppwd=%BIOSPWD%
- Suspend Bitlocker (Condition TSVar IsBDE = True)
Manage-bde.exe -protectors -disable c:
- Stop OSDDoNotLogCommand: TSVar: OSDDoNotLogCommand = False
- Run Dell Command Update Install BIOS
"C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" /log c:\windows\ccm\logs /silent
- Start OSDDoNotLogCommand: TSVar: OSDDoNotLogCommand = True
- Dell BIOS – Remove Password: Requires your CCTK Package
cctk --setuppwd=%BIOSPWD%
- Stop OSDDoNotLogCommand: TSVar: OSDDoNotLogCommand = False
- Restart Computer
- Set TS Var NewBiosVer
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('NewBiosVer') = (Get-WmiObject win32_bios).SMBIOSBIOSVersion}"
- Enable Bitlocker (Condition TSVar IsBDE = True)
Manage-bde.exe -protectors -enable c:
- DCU BIOS CleanUp (No Conditions on Group)
- Clear DCU Policy
reg delete HKLM\SOFTWARE\Dell\CommandUpdate\Preferences /F
- Set TS Var “SMSTS_BIOSFinishTime”
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('SMSTS_BIOSFinishTime') = Get-Date -f 's'}"
- Clear DCU Policy
- Run DCU Drivers
- Set TS Var “SMSTS_DriversStartTime”
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('SMSTS_DriversStartTime') = Get-Date -f 's'}"
- Set TS Var “SMSTS_DriversStartTime”
- Run DCU Report if Drivers available
- Modify Dell DCU – Import Settings -All drivers
cmd.exe /c start /wait C:\"Program Files (x86)"\Dell\CommandUpdate\dcu-cli.exe /import /policy %programdata%\DCU\DCUSettings\Drivers-All.xml
- Run Dell Command Update Report Only (DRIVERS)
cmd.exe /c start /wait C:\"Program Files (x86)"\Dell\CommandUpdate\dcu-cli.exe /report c:\ProgramData\DCU\DCUUpdatesDrivers.xml
- Copy Report to Log Folder
cmd.exe /c if Exist "%programdata%\DCU\DCUUpdatesDrivers.xml" (copy %programdata%\DCU\DCUUpdatesDrivers.xml %programdata%\DCULogs\DCUUpdatesDrivers%SMSTS_UpdateTime%.xml)
- Modify Dell DCU – Import Settings -All drivers
- Run DCU if Updates Exist (Condition :File: %programdata%\DCU\DCUUpdatesDrivers.xml exist)
- Run Dell Command Update Install Driver Updates
"C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" /log c:\windows\ccm\logs /silent
- Restart
- Run Dell Command Update Install Driver Updates
- DCU CleanUp
- Set TS Var “SMSTS_DriversFinishTime”
powershell.exe -command "&{$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment; $tsenv.Value('SMSTS_DriversFinishTime') = Get-Date -f 's'}"
- Remove Folder ProgramData\DCU
cmd.exe /c if Exist "%programdata%\DCU\" (rd %programdata%\DCU /S /Q)
- Modify Dell DCU – Import Settings -Defaults
cmd.exe /c start /wait C:\"Program Files (x86)"\Dell\CommandUpdate\dcu-cli.exe /import /policy %programdata%\DCU\DCUSettings\Defaults.xml
- Record Results for Reporting
- Run PowerShell Script
- Parameters:-ID “%SMSTS_Build%” -WMI -Registry -Class “%SetOSDInfoType%” -Namespace “GARYTOWN”
<# .SYNOPSIS Sets information during OSD / IPU for Dell BIOS & Drivers .DESCRIPTION This script will add build, task sequence, and other information to the OS so that it can later be examined or inventoried. Information can be added to the registry, WMI, or both. .PARAMETER Registry This switch will add information to the following location: - Registry .PARAMETER WMI This switch will add information to the following location: - WMI Repository .EXAMPLE Set-OSDInfo.ps1 -WMI -Registry Will add all information to the following locations: - Registry - WMI Repository .NOTES Modified from the versions by Stephane van Gulick from www.powershellDistrict.com This was completely borrowed from Jason Sandys SetOSDInfo script... it's my go to for this.. .LINK http://blog.configmgrftw.com https://garytown.com for Modifications to Jason's Original Script .VERSION 2019.01.20 #> [cmdletBinding()] Param( [Parameter(Mandatory=$false)][switch]$WMI, [Parameter(Mandatory=$false)][switch]$Registry, [Parameter(Mandatory=$false)][String]$Namespace, [Parameter(Mandatory=$false)][String]$Class, [Parameter(Mandatory=$true)][String]$ID, [Parameter(Mandatory=$false)][String]$AttributePrefix = "WaaS_" ) # Start-Transcript >> $env:temp\PowerShellTranscript.log Function Get-WMINamespace { <# .SYNOPSIS Gets information about a specified WMI namespace. .DESCRIPTION Returns information about a specified WMI namespace. .PARAMETER Namespace Specify the name of the namespace where the class resides in (default is "root\cimv2"). .EXAMPLE Get-WMINamespace Lists all WMI namespaces. .EXAMPLE Get-WMINamespace -Namespace cimv2 Returns the cimv2 namespace. .NOTES Version: 1.0 .LINK http://blog.configmgrftw.com #> [CmdletBinding()] Param ( [Parameter(Mandatory=$false,valueFromPipeLine=$true)][string]$Namespace ) begin { Write-Verbose "Getting WMI namespace $Namespace" } Process { if ($Namespace) { $filter = "Name = '$Namespace'" $return = Get-WmiObject -Namespace "root" -Class "__namespace" -filter $filter } else { $return = Get-WmiObject -Namespace root -Class __namespace } } end { return $return } } Function New-WMINamespace { <# .SYNOPSIS This function creates a new WMI namespace. .DESCRIPTION The function creates a new WMI namespsace. .PARAMETER Namespace Specify the name of the namespace that you would like to create. .EXAMPLE New-WMINamespace -Namespace "ITLocal" Creates a new namespace called "ITLocal" .NOTES Version: 1.0 .LINK http://blog.configmgrftw.com #> [CmdletBinding()] Param( [Parameter(Mandatory=$true,valueFromPipeLine=$true)][string]$Namespace ) if (!(Get-WMINamespace -Namespace "$Namespace")) { Write-Verbose "Attempting to create namespace $($Namespace)" $newNamespace = "" $rootNamespace = [wmiclass]'root:__namespace' $newNamespace = $rootNamespace.CreateInstance() $newNamespace.Name = $Namespace $newNamespace.Put() | out-null Write-Verbose "Namespace $($Namespace) created." } else { Write-Verbose "Namespace $($Namespace) is already present. Skipping.." } } Function Get-WMIClass { <# .SYNOPSIS Gets information about a specified WMI class. .DESCRIPTION Returns the listing of a WMI class. .PARAMETER ClassName Specify the name of the class that needs to be queried. .PARAMETER Namespace Specify the name of the namespace where the class resides in (default is "root\cimv2"). .EXAMPLE get-wmiclass List all the Classes located in the root\cimv2 namespace (default location). .EXAMPLE get-wmiclass -classname win32_bios Returns the Win32_Bios class. .EXAMPLE get-wmiclass -Class MyCustomClass Returns information from MyCustomClass class located in the default namespace (root\cimv2). .EXAMPLE Get-WMIClass -Namespace ccm -Class * List all the classes located in the root\ccm namespace .EXAMPLE Get-WMIClass -NameSpace ccm -Class ccm_client Returns information from the cm_client class located in the root\ccm namespace. .NOTES Version: 1.0 .LINK http://blog.configmgrftw.com #> [CmdletBinding()] Param ( [Parameter(Mandatory=$false,valueFromPipeLine=$true)][string]$Class, [Parameter(Mandatory=$false)][string]$Namespace = "cimv2" ) begin { Write-Verbose "Getting WMI class $Class" } Process { if (Get-WMINamespace -Namespace $Namespace) { $namespaceFullName = "root\$Namespace" Write-Verbose $namespaceFullName if (!$Class) { $return = Get-WmiObject -Namespace $namespaceFullName -Class * -list } else { $return = Get-WmiObject -Namespace $namespaceFullName -Class $Class -list } } else { Write-Verbose "WMI namespace $Namespace does not exist." $return = $null } } end { return $return } } Function New-WMIClass { <# .SYNOPSIS This function creates a new WMI class. .DESCRIPTION The function create a new WMI class in the specified namespace. It does not create a new namespace however. .PARAMETER Class Specify the name of the class that you would like to create. .PARAMETER Namespace Specify the namespace where class the class should be created. If not specified, the class will automatically be created in "root\cimv2" .PARAMETER Attributes Specify the attributes for the new class. .PARAMETER Key Specify the names of the key attribute (or attributes) for the new class. .EXAMPLE New-WMIClass -ClassName "OSD_Info" Creates a new class called "OSD_Info" .EXAMPLE New-WMIClass -ClassName "OSD_Info1","OSD_Info2" Creates two classes called "OSD_Info1" and "OSD_Info2" in the root\cimv2 namespace .NOTES Version: 1.0 .LINK http://blog.configmgrftw.com #> [CmdletBinding()] Param( [Parameter(Mandatory=$true,valueFromPipeLine=$true)][string]$Class, [Parameter(Mandatory=$false)][string]$Namespace = "cimv2", [Parameter(Mandatory=$false)][System.Management.Automation.PSVariable[]]$Attributes, [Parameter(Mandatory=$false)][string[]]$Key ) $namespaceFullName = "root\$Namespace" if (!(Get-WMINamespace -Namespace $Namespace)) { Write-Verbose "WMI namespace $Namespace does not exist." } elseif (!(Get-WMIClass -Class $Class -NameSpace $Namespace)) { Write-Verbose "Attempting to create class $($Class)" $newClass = "" $newClass = New-Object System.Management.ManagementClass($namespaceFullName, [string]::Empty, $null) $newClass.name = $Class foreach ($attr in $Attributes) { $attr.Name -match "$AttributePrefix(?<attributeName>.*)" | Out-Null $attrName = $matches['attributeName'] $newClass.Properties.Add($attrName, [System.Management.CimType]::String, $false) Write-Verbose " added attribute: $attrName" } foreach ($keyAttr in $Key) { $newClass.Properties[$keyAttr].Qualifiers.Add("Key", $true) Write-Verbose " added key: $keyAttr" } $newClass.Put() | out-null Write-Verbose "Class $($Class) created." } else { Write-Verbose "Class $($Class) is already present. Skipping..." } } Function New-WMIClassInstance { <# .SYNOPSIS Creates a new WMI class instance. .DESCRIPTION The function creates a new instance of the specified WMI class. .PARAMETER Class Specify the name of the class to create a new instance of. .PARAMETER Namespace Specify the name of the namespace where the class is located (default is Root\cimv2). .PARAMETER Attributes Specify the attributes and their values using PSVariables. .EXAMPLE $MyNewInstance = New-WMIClassInstance -Class OSDInfo Creates a new instance of the WMI class "OSDInfo" and sets its attributes. .NOTES Version: 1.0 .LINK http://blog.configmgrftw.com #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [ValidateScript({ $_ -ne "" })][string]$Class, [Parameter(Mandatory=$false)][string]$Namespace="cimv2", [Parameter(Mandatory=$false)][System.Management.Automation.PSVariable[]]$Attributes ) $classPath = "root\$($Namespace):$($Class)" $classObj = [wmiclass]$classPath $classInstance = $classObj.CreateInstance() Write-Verbose "Created instance of $Class class." foreach ($attr in $Attributes) { $attr.Name -match "$AttributePrefix(?<attributeName>.*)" | Out-Null $attrName = $matches['attributeName'] if ($attr.Value) { $attrVal = $attr.Value } else { $attrVal = "" } $classInstance[$attrName] = $attrVal " added attribute value for $($attrName): $($attrVal)" >> $env:temp\newWMIInstance.log } $classInstance.Put() } Function New-RegistryItem { <# .SYNOPSIS Sets a registry value in the specified key under HKLM\Software. .DESCRIPTION Sets a registry value in the specified key under HKLM\Software. .PARAMETER Key Species the registry path under HKLM\SOFTWARE\ to create. Defaults to OperatingSystemDeployment. .PARAMETER ValueName This parameter specifies the name of the Value to set. .PARAMETER Value This parameter specifies the value to set. .Example New-RegistryItem -ValueName Test -Value "abc" .NOTES -Version: 1.0 #> [cmdletBinding()] Param( [Parameter(Mandatory=$false)] [string]$Key = "OperatingSystemDeployment", [Parameter(Mandatory=$true)] [string]$ValueName, [Parameter(Mandatory=$false)] [string]$Value ) begin { $registryPath = "HKLM:SOFTWARE\WaaS\$ID" } Process { if ($registryPath -eq "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\") { write-verbose "The registry path that is tried to be created is the uninstall string.HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\." write-verbose "Creating this here would have as consequence to erase the whole content of the Uninstall registry hive." exit } ##Creating the registry node if (!(test-path $registryPath)) { write-verbose "Creating the registry key at : $($registryPath)." try { New-Item -Path $registryPath -force -ErrorAction stop | Out-Null } catch [System.Security.SecurityException] { write-warning "No access to the registry. Please launch this function with elevated privileges." } catch { write-host "An unknown error occurred : $_ " } } else { write-verbose "The registry key already exists at $($registryPath)" } ##Creating the registry string and setting its value write-verbose "Setting the registry string $($ValueName) with value $($Value) at path : $($registryPath) ." try { New-ItemProperty -Path $registryPath -Name $ValueName -PropertyType STRING -Value $Value -Force -ErrorAction Stop | Out-Null } catch [System.Security.SecurityException] { write-host "No access to the registry. Please launch this function with elevated privileges." } catch { write-host "An unknown error occurred : $_ " } } End { } } try { $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment } catch { Write-Verbose "Not running in a task sequence." } $keyValue = "ID" #New-Variable -Name "$($AttributePrefix)$keyValue" -Value $ID if ($tsenv) { #Get what kind of TS: CompatScan (CS) / InPlace Uprade (IPU) / Operating System Deployment (OSD) - Must have Step in TS that lets script know if ($tsenv.Value("SetOSDInfoType")) {$SetOSDInfoType = $tsenv.Value("SetOSDInfoType")} #Gets the Time in Minutes it takes to run Parts of the Task Sequence - Requires you to set a Start Variable & Finish Variable $DifferenceBIOS = ([datetime]$TSEnv.Value('SMSTS_BIOSFinishTime')) - ([datetime]$TSEnv.Value('SMSTS_BIOSStartTime')) $DifferenceBIOS = [math]::Round($DifferenceBIOS.TotalMinutes) $DifferenceDrivers = ([datetime]$TSEnv.Value('SMSTS_DriversFinishTime')) - ([datetime]$TSEnv.Value('SMSTS_DriversStartTime')) $DifferenceDrivers = [math]::Round($DifferenceDrivers.TotalMinutes) if ($SetOSDInfoType -eq "OSD" -or $SetOSDInfoType -eq "DCU") { $ID = $SetOSDInfoType } if ($SetOSDInfoType -eq "IPU" -or $SetOSDInfoType -eq "OSD") #This will run during an IPU or OSD TS (Assuming you're using the methods I've setup in my WaaS blogs) { New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_BIOSUpgradeTime" -Value $DifferenceBIOS New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_DriverUpgradeTime" -Value $DifferenceDrivers if($tsenv.Value('CurrentBiosVer')){New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_OriginalBiosVer" -Value $tsenv.Value('CurrentBiosVer')} if($tsenv.Value('NewBiosVer')){New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_UpdatedBiosVer" -Value $tsenv.Value('NewBiosVer')} } else { $AttributePrefix = "DCU_" #This will run if this is setup as a standalone TS. New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_BIOSUpgradeTime" -Value $DifferenceBIOS New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_DriverUpgradeTime" -Value $DifferenceDrivers if($tsenv.Value('CurrentBiosVer')){New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_OriginalBiosVer" -Value $tsenv.Value('CurrentBiosVer')} if($tsenv.Value('NewBiosVer')){New-Variable -Name "$($AttributePrefix)$($SetOSDInfoType)_UpdatedBiosVer" -Value $tsenv.Value('NewBiosVer')} } } #$customInfo = @() #$customInfo = $tsenv.getVariables() | where {$_ -match "$($AttributePrefix).*"} #Foreach ($infoItem in $customInfo) #{ # New-Variable -Name $infoItem -Value $tsenv.value($infoItem) #} $customAttributes = Get-Variable -Name "$AttributePrefix*" if ($PSBoundParameters.ContainsKey("WMI")) { New-WMINamespace -Namespace $Namespace New-WMIClass -Namespace $Namespace -Class $Class -Attributes $customAttributes -Key $keyValue New-WMIClassInstance -Namespace $Namespace -Class $Class -Attributes $customAttributes } if ($PSBoundParameters.ContainsKey("Registry")) { foreach ($attr in $customAttributes) { $attr.Name -match "$AttributePrefix(?<attributeName>.*)" | Out-Null $attrName = $matches['attributeName'] if ($attr.Value) { $attrVal = $attr.Value } else { $attrVal = "" } Write-Verbose "Setting registry value named $attrName to $attrVal" New-RegistryItem -Key "$($Class)\$ID" -ValueName $attrName -Value $attrVal } }
- Set TS Var “SMSTS_DriversFinishTime”
OK, So that is the long beast… you can remove many of those steps if you don’t care about recording metrics. The Script should look familiar if you know my work, it’s 99% stolen from Jason Sandys blog. If you use my SetOSDInfo script in your WaaS task sequences, you could easliy just add the few changes into that, but to make this standalone, I created a separate script. Yes, you could acutally do everything in about 20 lines of code instead of several hundred.. but I like to re-use common code sets… to make the script I copy and past pretty much all of it, then added the 15-ish lines I needed.
Notes about the command line steps, you’ll see
Results when run with OSD:
It records before & after BIOS versions, along with the time taken to run the BIOS Update and the Driver Updates.
Note that is also logs the activities log to c:\windows\ccm\logs folder, which is a very detailed log (in XML) of the DCU-CLI step process
If you need help making a CCTK package, please check out this Post by Mike Terrill: https://miketerrill.net/2015/08/24/how-to-create-a-dell-command-configure-package-in-configmgr/
Posted on GARYTOWN.COM
Nice blog, Gary! A year or so ago I was using DCU in task sequences and never had to use the start /wait and C:\”Program Files (x86)”. I thought maybe this was a symptom of version 2.4.0 of DCU but I also just tried version 2.3.1 which I was using a year ago, and had the same issue with it. So it seems like a change in CM may have caused this weird behavior (tested with 1810).
Based on your Dell Command Update post you did a couple of years ago, I manage all my 12 Dell models and Lenovo 7 models using Dell Command Update & Dell Repository Manager for Dell and Lenovo Update retriever and Thininstaller for Lenovo and it works really well. I used to have it setup like you where I would just download the latest drivers and BIOS updates from Dell directly but I had multiple times where Dells ftp site went down so my SCCM builds would fail because it could not connect to the site. I use Dell Repository Manager now to make a local repository so I can manage what drivers get installed and I am now not dependant on Dell’s website every time I build a machine. I would love to send you my documentation for how I manage my Dell and Lenovo machines and our process. you have helped me out many times over the 5 years of SCCM/desktop administration.