Worklet - Dell Command Update with Output Showing What Updates Installed

Disclaimer - this will apply all Dell driver updates without a reboot. It is up to you to configure this to your environment.

This worklet will Install Dell Command Update. Be sure to include the binaries and update the name to the EXE you use. https://www.dell.com/support/home/en-us/drivers/DriversDetails?driverId=JDXHH

Once installed or if already installed the worklet is setup to install updates then parse through a log to provide some details on what updates, if any were installed.

Activity Log Output

image

Evaluation Code

$system = Get-WmiObject win32_computersystem
IF($system.Manufacturer -match "Dell"){ Exit 1 }else { Exit 0 }

Remediation Code

# Dell Command | Update executable uploaded to worklet
$exe = 'Dell-Command-Update-Application-for-Windows10_JDXHH_WIN_4.2.0_A00.EXE'

# Dell Command Version
[version]$version = '4.2.0'

# Log location
$log = 'C:\Windows\Temp\DellCommand_Install.txt'

$system = Get-WmiObject win32_computersystem
IF($system.Manufacturer -match "Dell"){ 

    # Find versions of Dell Command installed
    $scriptblock = {
      Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where DisplayName -match 'Dell Command'
    }
    # Run the scriptblock and store results in the $64bit variable
    $software = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock

    function Install-DellCommand{
        [void](Start-Process -FilePath $exe -ArgumentList "/s /l='$log'" -wait -PassThru)
        IF($? -eq "True"){
            return $true
        }else{
            return $false
        }
 
        Start-Sleep -Seconds 20
        # Configure Dell Command Update
        Write-Host
        $Arguments = 'scheduleAuto',
                        'lockSettings=enable',
                        'userConsent=disable',
                        'scheduleMonthly=28,00:45',
                        'scheduledReboot=0',
                        'autoSuspendBitLocker=enable'
        foreach ($Arg in $Arguments){
            $results = Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/configure -$Arg" -Wait | Out-Null
            Start-Sleep -Seconds 5
        }
    }

    # IF Dell Command is not installed or older version exists
    IF(!$software){
        Write-Output "Installing Dell Command. "
        IF(Install-DellCommand){Write-Output "Installed Dell Command. "}else{Write-Output "Failed to install Dell Command. "; exit 1}
    }else{
        # Remove older versions of Dell Command
        foreach ($title in $software){
            IF([version]$title.DisplayVersion -lt $version){
                Write-Output "Uninstall Dell Command $($title.DisplayVersion). "
                $UninstallString = $title.UninstallString | Select-String -Pattern '{[-0-9A-F]+?}' -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
                Start-Process MsiExec -ArgumentList "/X $UninstallString /qn" -Wait
                Write-Output "Installing Dell Command. "
                IF(Install-DellCommand){Write-Output "Installed Dell Command. "}else{Write-Output "Failed to install Dell Command. "; exit 1}
            }
        }
    }

    # Apply Updates
    Function Start-DellCommandUpdates {
        # 2 minute timeout if dcu-cli is already running
        $dcu = Get-Process dcu-cli -ErrorAction SilentlyContinue
        IF($dcu){
            Write-Output "dcu-cli currently running. Wait for process to complete. "
            $startDate = Get-Date
            do{}while((Get-Process dcu-cli -ea SilentlyContinue) -and $startDate.AddMinutes(2) -gt (Get-Date))
            $dcu = Get-Process dcu-cli -ErrorAction SilentlyContinue
            IF($dcu){Stop-Process dcu-cli -Force}
        }
        # Run dcu-cli
        Write-Output "Starting Dell Command Update Process. "
        Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/applyUpdates" -Wait
    }
    Start-DellCommandUpdates

    # Get Activity
    Function Get-DellCommandActivity {
        # Get Activity Log and parse intalled updates in last 24 hours
        [xml]$a = Get-Content "C:\ProgramData\dell\UpdateService\Log\Activity.log"
        $range = (Get-Date).AddHours(-24)
        # Write output on Checking Updates
        $events = $a.LogEntries.LogEntry | Select timestamp,message | where {[datetime]$_.timestamp -gt $range -and ($_.message -match 'found' -or $_.message -match 'Checking for updates' -or $_.message -match 'verified')} | % {
            $timestamp = (Get-Date $_.timestamp -Format 'yyyy-MM-dd hh:mm tt')
            $message = $_.message.replace(' verified.','')
            [pscustomobject] @{
                timestamp = $timestamp
                message = $message
            }
        }
        foreach ($event in $events)
        {
            Write-Output "$($event.timestamp) $($event.message). "
        }
    }
    $activity = Get-DellCommandActivity
    foreach ($event in $activity){
        Write-Output "$event"
    }
    IF($activity.count -eq 0){
        Write-Output "Something went wrong. Trying to run Dell Command Update again. "
        Get-Process dcu-cli -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue -Force
        Start-DellCommandUpdates
        $activity = Get-DellCommandActivity
        foreach ($event in $activity){
            Write-Output "$event"
        }
        IF($activity.count -eq 0){
            IF(Test-Path 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe'){
                Write-Output "Something isn't working right. Recommend running this locally and analyzing output: 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe' /applyUpdates "
                Write-Output $activity
            }else{Write-Output "Dell Command failed to install. "}
        }
    }
}else{Write-Host "Not recognized as Dell Manufacturer. Not running." }
3 Likes

highly recommend starting with the latest version 4.2 as to not introduce a vulnerability into the environment https://www.dell.com/support/kbdoc/en-us/000186019/dsa-2021-088-dell-client-platform-security-update-for-dell-driver-insufficient-access-control-vulnerability

Good callout on considering a newer version of Dell Command. Looks like hot off the press too! I will get that link updated above.

What is really cool is using this worklet “should” also update the version of Dell Command when dcu-cli.exe /applyUpdates is used. Here is an example of that happening in the activity log.
image

My understanding is Dell Command plays a role in dropping the dbutil_2_3.sys file. Did you find something where 4.2 prevents that from happening? To buy some time to get the firmware patched I’m using this worklet-- CVE-2021-21551 Band-Aid Dell BIOS Driver Privilege Escalation Flaws - #5 by Tony

1 Like

Hi Jack,

Thank you for the update! If I upload the new 4.2 version it’s fine? it is a silent install?

It does not install Dell command first, can you please let me know how I can create a seperate policy just for installation?

Thanks,
Omid

Thanks for the heads up @OROR

I’ve updated the remediation code above to correct not installing Dell Command.

If you want a separate policy just for installation you could try this –

#Evaluation

$system = Get-WmiObject win32_computersystem
IF($system.Manufacturer -match "Dell"){ 
    # Find versions of Dell Command installed
    $scriptblock = {
        Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where DisplayName -match 'Dell Command'
    }
    # Run the scriptblock and store results in the $64bit variable
    $software = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock
    
    # IF Dell Command is not installed or older version exists
    IF($software -eq ''){ Exit 1 }else{ Exit 0 }
}else{exit 0}

Remediation

 # Dell Command | Update executable uploaded to worklet
$exe = 'Dell-Command-Update-Application-for-Windows10_JDXHH_WIN_4.2.0_A00.EXE'

# Dell Command Version
[version]$version = '4.2.0'

# Log location
$log = 'C:\Windows\Temp\DellCommand_Install.txt'

$system = Get-WmiObject win32_computersystem
IF($system.Manufacturer -match "Dell"){ 

    # Find versions of Dell Command installed
    $scriptblock = {
      Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where DisplayName -match 'Dell Command'
    }
    # Run the scriptblock and store results in the $64bit variable
    $software = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock

    function Install-DellCommand{
        [void](Start-Process -FilePath $exe -ArgumentList "/s /l='$log'" -wait -PassThru)
        IF($? -eq "True"){
            return $true
        }else{
            return $false
        }
 
        Start-Sleep -Seconds 20
        # Configure Dell Command Update
        Write-Host
        $Arguments = 'scheduleAuto',
                        'lockSettings=enable',
                        'userConsent=disable',
                        'scheduleMonthly=28,00:45',
                        'scheduledReboot=0',
                        'autoSuspendBitLocker=enable'
        foreach ($Arg in $Arguments){
            $results = Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/configure -$Arg" -Wait | Out-Null
            Start-Sleep -Seconds 5
        }
    }

    # IF Dell Command is not installed or older version exists
    IF(!$software){
        Write-Output "Installing Dell Command. "
        IF(Install-DellCommand){Write-Output "Installed Dell Command. "}else{Write-Output "Failed to install Dell Command. "; exit 1}
    }else{
        # Remove older versions of Dell Command
        foreach ($title in $software){
            IF([version]$title.DisplayVersion -lt $version){
                Write-Output "Uninstall Dell Command $($title.DisplayVersion). "
                $UninstallString = $title.UninstallString | Select-String -Pattern '{[-0-9A-F]+?}' -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
                Start-Process MsiExec -ArgumentList "/X $UninstallString /qn" -Wait
                Write-Output "Installing Dell Command. "
                IF(Install-DellCommand){Write-Output "Installed Dell Command. "}else{Write-Output "Failed to install Dell Command. "; exit 1}
            }
        }
    }

    # Apply Updates
    Function Start-DellCommandUpdates {
        # 2 minute timeout if dcu-cli is already running
        $dcu = Get-Process dcu-cli -ErrorAction SilentlyContinue
        IF($dcu){
            Write-Output "dcu-cli currently running. Wait for process to complete. "
            $startDate = Get-Date
            do{}while((Get-Process dcu-cli -ea SilentlyContinue) -and $startDate.AddMinutes(2) -gt (Get-Date))
            $dcu = Get-Process dcu-cli -ErrorAction SilentlyContinue
            IF($dcu){Stop-Process dcu-cli -Force}
        }
        # Run dcu-cli
        Write-Output "Starting Dell Command Update Process. "
        Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/applyUpdates" -Wait
    }
    Start-DellCommandUpdates

    # Get Activity
    Function Get-DellCommandActivity {
        # Get Activity Log and parse intalled updates in last 24 hours
        [xml]$a = Get-Content "C:\ProgramData\dell\UpdateService\Log\Activity.log"
        $range = (Get-Date).AddHours(-24)
        # Write output on Checking Updates
        $events = $a.LogEntries.LogEntry | Select timestamp,message | where {[datetime]$_.timestamp -gt $range -and ($_.message -match 'found' -or $_.message -match 'Checking for updates' -or $_.message -match 'verified')} | % {
            $timestamp = (Get-Date $_.timestamp -Format 'yyyy-MM-dd hh:mm tt')
            $message = $_.message.replace(' verified.','')
            [pscustomobject] @{
                timestamp = $timestamp
                message = $message
            }
        }
        foreach ($event in $events)
        {
            Write-Output "$($event.timestamp) $($event.message). "
        }
    }
    $activity = Get-DellCommandActivity
    foreach ($event in $activity){
        Write-Output "$event"
    }
    IF($activity.count -eq 0){
        Write-Output "Something went wrong. Trying to run Dell Command Update again. "
        Get-Process dcu-cli -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue -Force
        Start-DellCommandUpdates
        $activity = Get-DellCommandActivity
        foreach ($event in $activity){
            Write-Output "$event"
        }
        IF($activity.count -eq 0){
            IF(Test-Path 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe'){
                Write-Output "Something isn't working right. Recommend running this locally and analyzing output: 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe' /applyUpdates "
                Write-Output $activity
            }else{Write-Output "Dell Command failed to install. "}
        }
    }
}else{Write-Host "Not recognized as Dell Manufacturer. Not running." }

Hello Jack please test, it does not work for me but I could install the 4.2 version with this simple script:

 $proc = Start-Process -filepath ".\Dell-Command-Update-Application-for-Windows10_JDXHH_WIN_4.2.0_A00.EXE" -ArgumentList "/s /f /l='C:\Windows\Temp\Spreetail_MDT_DellCommand_Install.txt'" -wait -PassThru
Write-Output "Exit Code was $($proc.ExitCode)"
Exit $proc.ExitCode

In your original script when it runs Dell command there is pop up windows and users need to click “OK” when it gets to this line:

$results = Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/configure -$Arg" -Wait | Out-Null

Do you know how I can force it to run automatically without users involved?

Thanks,
Omid

Interesting. I’m not sure how to reproduce that. Do you have other Dell Command products installed perhaps?

@OROR got it. swapped out

($software -eq '')

for
(!$software)

Sorry, my bad I was tested directly from powershell if you push it though automox there is not pop up! :slight_smile:

Can you assist with adding in IF the workstation has “Dell Command | Update for Windows 10” installed, to remove it and install “Dell Command | Update”

“Dell Command | Update for Windows 10” is the universal version and it won’t update or install this version if that version is installed on the computer. Also the universal version doesn’t allow for scripts to be run to initiate silent updates etc.

Thank you in advance. The provided script works great otherwise!

Also, using the remediation script you provided in the original post…the updates aren’t being carried out.

This is what i receive in the Activity log. However when i run Dell Command manually, there are several updates, including critical, recommended and optional.


Installing Dell Command. Installed Dell Command. Starting Dell Command Update Process. 2021-06-06 02:10 PM Checking for updates. 2021-06-06 03:07 PM found [0] updates. 2021-06-07 11:40 AM found [0] updates. 2021-06-07 11:40 AM found [0] updates.

@jcropanese

Thinking you could accomplish that with this snippet of code

# Find versions of Dell Command installed
$scriptblock = {
    Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where DisplayName -match 'Dell Command'
}
# Run the scriptblock and store results in the $64bit variable
$software = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock

IF([version]$software.DisplayName -eq 'Dell Command | Update for Windows 10'){
    Write-Output "Uninstall Dell Command $($software.DisplayVersion). "
    $UninstallString = $software.UninstallString | Select-String -Pattern '{[-0-9A-F]+?}' -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
    Start-Process MsiExec -ArgumentList "/X $UninstallString /qn" -Wait
}

The output is parsing XML from Dell Command after running dcu-clie.exe /applyupdates. Perhaps Dell Command is not configured as expected? You can look with this.

& 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe' /configure -exportSettings=C:\temp

Sorry i am very new to powershell and am working through your script, so i apologize. This is helping me tremendously and will help with my other scripts too, so thank you!

From my understanding, the snippet you provided will look for ANY version of the software named ‘Dell Command | Update for Windows 10’ and uninstall it … correct?

So what i am looking to accomplish in my script is combing both that and what you originally provided.

So i want it to remove the ‘Dell Command | Update for Windows 10’ if it exists and then install the correct one otherwise just install the correct one. Can you show me what the final code would look like?

Thank you for helping!

Can you help me understand the difference between “Dell Command | Update” and “Dell Command | Update for Windows 10”. When I install the binaries linked in the original post, it shows up in Programs and Features as “Dell Command | Update for Windows 10”. By adding that logic to the entire code the solution would uninstall/install Dell Command every time it runs. I’m curious if there is something else that differentiates the difference between the two.

So there are 2 versions, the one you have in your script with " Dell Command | Update for Windows 10 " is the universal app and the other one I am referring to is the normal app and just shows up as “Dell Command | Update” . I have a mixture of the 2 in my environment and would like to just have the normal one installed, so trying to incorporate the uninstall of the other and install of the new into the script .

I had read an article not long ago that there was something wrong with CLI on the universal app, but maybe it is fixed now. In either case, I’d like to just get one version of the app on all machines.

You are correct. The original script is looking for ANY version that is a match of DisplayName “Dell Command” which would include both application titles. Really appreciate you for pointing that out as up until now didn’t even realize two versions existed.

https://www.dell.com/support/kbdoc/en-us/000177325/dell-command-update

To your other ask. This code will follow the same logic as above. Removing any version less than 4.2 as well as versions of “Dell Command | Update for Windows 10”.

# Dell Command | Update executable uploaded to worklet
$exe = 'Dell-Command-Update-Application_MJ3C3_WIN_4.2.0_A00.EXE'

# Dell Command Version
[version]$version = '4.2.0'

# Log location
$log = 'C:\Windows\Temp\DellCommand_Install.txt'

$system = Get-WmiObject win32_computersystem
IF($system.Manufacturer -match "Dell"){ 

    # Find versions of Dell Command installed
    $scriptblock = {
      Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*' | where DisplayName -match 'Dell Command'
    }
    # Run the scriptblock and store results in the $64bit variable
    $software = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock

    function Install-DellCommand{
        [void](Start-Process -FilePath $exe -ArgumentList "/s /l='$log'" -wait -PassThru)
        IF($? -eq "True"){
            return $true
        }else{
            return $false
        }
 
        Start-Sleep -Seconds 20
        # Configure Dell Command Update
        Write-Host
        $Arguments = 'restoreDefaults',
                        'scheduleAuto',
                        'lockSettings=enable',
                        'userConsent=disable',
                        'scheduleMonthly=28,00:45',
                        'scheduledReboot=0',
                        'autoSuspendBitLocker=enable'
        foreach ($Arg in $Arguments){
            $results = Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/configure -$Arg" -Wait | Out-Null
            Start-Sleep -Seconds 5
        }
    }

    # IF Dell Command is not installed or older version exists
    IF(!$software){
        Write-Output "Installing Dell Command. "
        IF(Install-DellCommand){Write-Output "Installed Dell Command. "}else{Write-Output "Failed to install Dell Command. "; exit 1}
    }else{
        # Remove older versions of Dell Command
        foreach ($title in $software){
            IF([version]$title.DisplayVersion -lt $version -or $title.Displayname -eq "Dell Command | Update for Windows 10"){
                Write-Output "Uninstall $($title.Displayname) version $($title.DisplayVersion). "
                $UninstallString = $title.UninstallString | Select-String -Pattern '{[-0-9A-F]+?}' -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value
                Start-Process MsiExec -ArgumentList "/X $UninstallString /qn" -Wait
                
                $software = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock
                IF(!$software){
                  Write-Output "Installing Dell Command. "
                  IF(Install-DellCommand){Write-Output "Installed Dell Command. "}else{Write-Output "Failed to install Dell Command. "; exit 1}
                }else{Write-Output "Detected $($software.DisplayName) version $($software.DisplayVersion). "}
            }
        }
    }

    # Apply Updates
    Function Start-DellCommandUpdates {
        # 2 minute timeout if dcu-cli is already running
        $dcu = Get-Process dcu-cli -ErrorAction SilentlyContinue
        IF($dcu){
            Write-Output "dcu-cli currently running. Wait for process to complete. "
            $startDate = Get-Date
            do{}while((Get-Process dcu-cli -ea SilentlyContinue) -and $startDate.AddMinutes(2) -gt (Get-Date))
            $dcu = Get-Process dcu-cli -ErrorAction SilentlyContinue
            IF($dcu){Stop-Process dcu-cli -Force}
        }
        # Run dcu-cli
        Write-Output "Starting Dell Command Update Process. "
        Start-Process -FilePath "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe" -ArgumentList "/applyUpdates" -Wait
    }
    Start-DellCommandUpdates

    # Get Activity
    Function Get-DellCommandActivity {
        # Get Activity Log and parse intalled updates in last 24 hours
        [xml]$a = Get-Content "C:\ProgramData\dell\UpdateService\Log\Activity.log"
        $range = (Get-Date).AddHours(-24)
        # Write output on Checking Updates
        $events = $a.LogEntries.LogEntry | Select timestamp,message | where {[datetime]$_.timestamp -gt $range -and ($_.message -match 'found' -or $_.message -match 'Checking for updates' -or $_.message -match 'verified')} | % {
            $timestamp = (Get-Date $_.timestamp -Format 'yyyy-MM-dd hh:mm tt')
            $message = $_.message.replace(' verified.','')
            [pscustomobject] @{
                timestamp = $timestamp
                message = $message
            }
        }
        foreach ($event in $events)
        {
            Write-Output "$($event.timestamp) $($event.message). "
        }
    }
    $activity = Get-DellCommandActivity
    foreach ($event in $activity){
        Write-Output "$event"
    }
    IF($activity.count -eq 0){
        Write-Output "Something went wrong. Trying to run Dell Command Update again. "
        Get-Process dcu-cli -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue -Force
        Start-DellCommandUpdates
        $activity = Get-DellCommandActivity
        foreach ($event in $activity){
            Write-Output "$event"
        }
        IF($activity.count -eq 0){
            IF(Test-Path 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe'){
                Write-Output "Something isn't working right. Recommend running this locally and analyzing output: 'C:\Program Files\Dell\CommandUpdate\dcu-cli.exe' /applyUpdates "
                Write-Output $activity
            }else{Write-Output "Dell Command failed to install. "}
        }
    }
}else{Write-Host "Not recognized as Dell Manufacturer. Not running." }

With that script, it’s not removing the Universal one from my test computer.

When looking at your script, how is $title defined? I’ve been starring at this script for a while and starting to lose track as to what each part is doing. HAHA

$software is a variable that could collect multiple objects. For example if you had ‘Dell Command | Update’ and ‘Dell Command | Update for Windows 10’ installed at the same time. In this case $title becomes the ONE object being looked at at the time of the for each loop. You could rename $title to almost anything you want.

ForEach($title in $software)