Shared Script Library
GitHubLinkedInSponsor
  • Overview
  • Software
    • Installers
      • Bluebeam Revu
      • Browser Extension
      • Google Chrome
      • Microsoft Defender for Endpoint
      • Microsoft Office
      • Microsoft Teams
      • QuickBooks Desktop
    • Uninstallers
      • Browser Extension
  • Device Management
    • Backups
      • SQL Databases
      • Synology Active Backup
      • Windows Restore Point
    • Updates
      • Dell Command Update
      • Microsoft Office
      • Microsoft Store Applications
      • Windows Management Framework
      • Windows Updates
    • Misc
      • Reset Local Group Policy
Powered by GitBook
On this page
  • Overview
  • Script
  • Examples
  • Parameters

Was this helpful?

  1. Device Management
  2. Updates

Dell Command Update

PowerShell script to silently install and run Dell Command Update (DCU).

PreviousUpdatesNextMicrosoft Office

Last updated 1 month ago

Was this helpful?

Overview

Dev Insight: This script was originally written to make sure devices deployed during our MDT process had the latest drivers/firmware from Dell, but it can also be used to update Dell devices already deployed via your RMM of choice.

This script downloads and installs the latest version of Dell Command Update (DCU) if not already installed. It will then use dcu-cli.exe to download and install all Dell driver/firmware updates, ignoring reboots.

Prerequisites: This script has no prerequisites.

Notes:

  • Script will abort if a PC manufacturer other than Dell is detected.

  • Dell Update, which is incompatible with Dell Command Update, will be removed if detected.

  • .NET Desktop Runtime, a prerequisite to Dell Command Update 5.5+, will be updated / installed to the latest LTS release if not already installed.

  • Dell Command Update is not intended for use with server hardware. Use Dell System Update instead.


Script

Script will abort if a system manufacturer other than Dell is detected.

Examples

.\DellCommandUpdate.ps1

This example downloads and installs Dell Command Update if it isn't already installed. After installation, Dell Command Update will check for and install any Dell updates.


Parameters

This script has no parameters.

Support for Dell Command | Update | Manuals and Documents | Dell US
Logo
https://github.com/wise-io/scripts/blob/main/scripts/DellCommandUpdate.ps1
<#
  .SYNOPSIS
    Installs Dell updates via Dell Command Update
  .DESCRIPTION
    Installs the latest version of Dell Command Update and applies all Dell updates silently.
  .LINK
    https://www.dell.com/support/product-details/en-us/product/command-update/resources/manuals
    https://github.com/wise-io/scripts/blob/main/scripts/DellCommandUpdate.ps1
  .NOTES
    Author: Aaron J. Stevenson
#>

function Get-Architecture {
  # On PS x86, PROCESSOR_ARCHITECTURE reports x86 even on x64 systems.
  # To get the correct architecture, we need to use PROCESSOR_ARCHITEW6432.
  # PS x64 doesn't define this, so we fall back to PROCESSOR_ARCHITECTURE.
  # Possible values: amd64, x64, x86, arm64, arm
  if ($null -ne $ENV:PROCESSOR_ARCHITEW6432) { $Architecture = $ENV:PROCESSOR_ARCHITEW6432 }
  else {     
    if ((Get-CimInstance -ClassName CIM_OperatingSystem -ErrorAction Ignore).OSArchitecture -like 'ARM*') {
      if ( [Environment]::Is64BitOperatingSystem ) { $Architecture = 'arm64' }  
      else { $Architecture = 'arm' }
    }

    if ($null -eq $Architecture) { $Architecture = $ENV:PROCESSOR_ARCHITECTURE }
  }

  switch ($Architecture.ToLowerInvariant()) {
    { ($_ -eq 'amd64') -or ($_ -eq 'x64') } { return 'x64' }
    { $_ -eq 'x86' } { return 'x86' }
    { $_ -eq 'arm' } { return 'arm' }
    { $_ -eq 'arm64' } { return 'arm64' }
    default { throw "Architecture '$Architecture' not supported." }
  }
}

function Get-InstalledApps {
  param(
    [Parameter(Mandatory)][String[]]$DisplayNames,
    [String[]]$Exclude
  )
  
  $RegPaths = @(
    'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
    'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
  )
  
  # Get applications matching criteria
  $BroadMatch = @()
  foreach ($DisplayName in $DisplayNames) {
    $AppsWithBundledVersion = Get-ChildItem -Path $RegPaths | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" -and $null -ne $_.BundleVersion }
    if ($AppsWithBundledVersion) { $BroadMatch += $AppsWithBundledVersion }
    else { $BroadMatch += Get-ChildItem -Path $RegPaths | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" } }
  }
  
  # Remove excluded apps
  $MatchedApps = @()
  foreach ($App in $BroadMatch) {
    if ($Exclude -notcontains $App.DisplayName) { $MatchedApps += $App }
  }

  return $MatchedApps | Sort-Object { [version]$_.BundleVersion } -Descending
}

function Remove-IncompatibleApps {
  # Check for incompatible products
  $IncompatibleApps = Get-InstalledApps -DisplayNames 'Dell Update', 'Dell Command | Update' `
    -Exclude 'Dell SupportAssist OS Recovery Plugin for Dell Update', 'Dell Command | Update for Windows Universal', 'Dell Command | Update for Windows 10'
  
  if ($IncompatibleApps) { Write-Output 'Incompatible applications detected' }
  foreach ($App in $IncompatibleApps) {
    Write-Output "Attempting to remove [$($App.DisplayName)]"
    try {
      if ($App.UninstallString -match 'msiexec') {
        $Guid = [regex]::Match($App.UninstallString, '\{[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}\}').Value
        Start-Process -NoNewWindow -Wait -FilePath 'msiexec.exe' -ArgumentList "/x $Guid /quiet /qn"
      }
      else { Start-Process -NoNewWindow -Wait -FilePath $App.UninstallString -ArgumentList '/quiet' }
      Write-Output "Successfully removed $($App.DisplayName)"
    }
    catch { 
      Write-Warning "Failed to remove $($App.DisplayName)"
      Write-Warning $_
      exit 1
    }
  }
}

function Install-DellCommandUpdate {
  function Get-LatestDellCommandUpdate {
    # Set KB URL
    $DellKBURL = 'https://www.dell.com/support/kbdoc/en-us/000177325/dell-command-update'
  
    # Set fallback URL based on architecture
    $Arch = Get-Architecture
    if ($Arch -like 'arm*') { 
      $FallbackDownloadURL = 'https://dl.dell.com/FOLDER11914141M/1/Dell-Command-Update-Windows-Universal-Application_6MK0D_WINARM64_5.4.0_A00.EXE'
      $FallbackMD5 = 'c6ed3bc35d7d6d726821a2c25fbbb44d'
    }
    else { 
      $FallbackDownloadURL = 'https://dl.dell.com/FOLDER12925773M/1/Dell-Command-Update-Windows-Universal-Application_P4DJW_WIN64_5.5.0_A00.EXE'
      $FallbackMD5 = 'a1eb9c7eadb6d9cbfbbe2be13049b299'
    }
  
    # Set headers for Dell website
    $Headers = @{
      'accept'          = 'text/html'
      'accept-encoding' = 'gzip'
      'accept-language' = '*'
    }
  
    # Attempt to parse Dell website for download page links of latest DCU
    [String]$DellKB = Invoke-WebRequest -UseBasicParsing -Uri $DellKBURL -Headers $Headers -ErrorAction Ignore
    $LinkMatches = @($DellKB | Select-String '(https://www\.dell\.com.+driverid=[a-z0-9]+).+>Dell Command \| Update Windows Universal Application<\/a>' -AllMatches).Matches
    $KBLinks = foreach ($Match in $LinkMatches) { $Match.Groups[1].Value }
  
    # Attempt to parse Dell website for download URLs for latest DCU
    $DownloadObjects = foreach ($Link in $KBLinks) {
      $DownloadPage = Invoke-WebRequest -UseBasicParsing -Uri $Link -Headers $Headers -ErrorAction Ignore
      if ($DownloadPage -match '(https://dl\.dell\.com.+Dell-Command-Update.+\.EXE)') { 
        $Url = $Matches[1]
        if ($DownloadPage -match 'MD5:.*?([a-fA-F0-9]{32})') { $MD5 = $Matches[1] }
        [PSCustomObject]@{
          URL = $Url
          MD5 = $MD5
        }
      }
    }
  
    # Set download URL / MD5 based on architecture
    if ($Arch -like 'arm*') { $DownloadObject = $DownloadObjects | Where-Object { $_.URL -like '*winarm*' } }
    else { $DownloadObject = $DownloadObjects | Where-Object { $_.URL -notlike '*winarm*' } }
    $DownloadURL = $DownloadObject.URL
    $MD5 = $DownloadObject.MD5

    # Revert to fallback URL / MD5 if unable to retrieve from Dell
    if ($null -eq $DownloadObject.URL -or $null -eq $DownloadObject.MD5) { 
      $DownloadURL = $FallbackDownloadURL
      $MD5 = $FallbackMD5
    }

    # Get version from DownloadURL
    $Version = $DownloadObject.URL | Select-String '[0-9]*\.[0-9]*\.[0-9]*' | ForEach-Object { $_.Matches.Value }
  
    return @{
      MD5     = $MD5.ToUpper()
      URL     = $DownloadURL
      Version = $Version
    }
  }
  
  $LatestDellCommandUpdate = Get-LatestDellCommandUpdate
  $Installer = Join-Path -Path $env:TEMP -ChildPath (Split-Path $LatestDellCommandUpdate.URL -Leaf)
  $CurrentVersion = (Get-InstalledApps -DisplayName 'Dell Command | Update for Windows Universal', 'Dell Command | Update for Windows 10').DisplayVersion
  Write-Output "`nInstalled Dell Command Update: $CurrentVersion"
  Write-Output "Latest Dell Command Update: $($LatestDellCommandUpdate.Version)"

  if ($CurrentVersion -lt $LatestDellCommandUpdate.Version) {

    # Download installer
    Write-Output "`nDell Command Update installation needed"
    Write-Output 'Downloading...'
    Invoke-WebRequest -Uri $LatestDellCommandUpdate.URL -OutFile $Installer -UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::Chrome)

    # Verify MD5 checksum
    Write-Output 'Verifying MD5 checksum...'
    $InstallerMD5 = (Get-FileHash -Path $Installer -Algorithm MD5).Hash
    if ($InstallerMD5 -ne $LatestDellCommandUpdate.MD5) {
      Write-Warning 'MD5 verification failed - aborting...'
      Remove-Item $Installer -Force -ErrorAction Ignore
      exit 1
    }

    # Install Dell Command Update
    Write-Output 'Installing...'
    Start-Process -Wait -NoNewWindow -FilePath $Installer -ArgumentList '/s'

    # Confirm installation
    $CurrentVersion = (Get-InstalledApps -DisplayName 'Dell Command | Update for Windows Universal').DisplayVersion
    if ($CurrentVersion -match $LatestDellCommandUpdate.Version) {
      Write-Output "Successfully installed Dell Command Update [$CurrentVersion]`n"
      Remove-Item $Installer -Force -ErrorAction Ignore 
    }
    else {
      Write-Warning "Dell Command Update [$($LatestDellCommandUpdate.Version)] not detected after installation attempt"
      Remove-Item $Installer -Force -ErrorAction Ignore 
      exit 1
    }
  }
  else { Write-Output "`nDell Command Update installation / upgrade not needed`n" }
}

function Install-DotNetDesktopRuntime {
  function Get-LatestDotNetDesktopRuntime {
    $BaseURL = 'https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop'
    $Version = (Invoke-WebRequest -Uri "$BaseURL/LTS/latest.version" -UseBasicParsing).Content
    $Arch = Get-Architecture
    $URL = "$BaseURL/$Version/windowsdesktop-runtime-$Version-win-$Arch.exe"
  
    return @{
      URL     = $URL
      Version = $Version
    }
  }
  
  $LatestDotNet = Get-LatestDotNetDesktopRuntime
  $Installer = Join-Path -Path $env:TEMP -ChildPath (Split-Path $LatestDotNet.URL -Leaf)
  $CurrentVersion = (Get-InstalledApps -DisplayName 'Microsoft Windows Desktop Runtime').BundleVersion
  if ($CurrentVersion -is [system.array]) { $CurrentVersion = $CurrentVersion[0] }
  Write-Output "`nInstalled .NET Desktop Runtime: $CurrentVersion"
  Write-Output "Latest .NET Desktop Runtime: $($LatestDotNet.Version)"

  if ($CurrentVersion -lt $LatestDotNet.Version) {
    
    # Download installer
    Write-Output "`n.NET Desktop Runtime installation needed"
    Write-Output 'Downloading...'
    Invoke-WebRequest -Uri $LatestDotNet.URL -OutFile $Installer

    # Install .NET
    Write-Output 'Installing...'
    Start-Process -Wait -NoNewWindow -FilePath $Installer -ArgumentList '/install /quiet /norestart'

    # Confirm installation
    $CurrentVersion = (Get-InstalledApps -DisplayName 'Microsoft Windows Desktop Runtime').BundleVersion
    if ($CurrentVersion -is [system.array]) { $CurrentVersion = $CurrentVersion[0] }
    if ($CurrentVersion -match $LatestDotNet.Version) {
      Write-Output "Successfully installed .NET Desktop Runtime [$CurrentVersion]"
      Remove-Item $Installer -Force -ErrorAction Ignore 
    }
    else {
      Write-Warning ".NET Desktop Runtime [$($LatestDotNet.Version)] not detected after installation attempt"
      Remove-Item $Installer -Force -ErrorAction Ignore 
      exit 1
    }
  }
  else { Write-Output "`n.NET Desktop Runtime installation / upgrade not needed" }
}

function Invoke-DellCommandUpdate {
  # Check for DCU CLI
  $DCU = (Resolve-Path "$env:SystemDrive\Program Files*\Dell\CommandUpdate\dcu-cli.exe").Path
  if ($null -eq $DCU) {
    Write-Warning 'Dell Command Update CLI was not detected.'
    exit 1
  }
  
  try {
    # Configure DCU automatic updates
    Start-Process -NoNewWindow -Wait -FilePath $DCU -ArgumentList '/configure -scheduleAction=DownloadInstallAndNotify -updatesNotification=disable -forceRestart=disable -scheduleAuto -silent'
    
    # Scan for / apply updates
    Start-Process -NoNewWindow -Wait -FilePath $DCU -ArgumentList '/scan -silent'
    Start-Process -NoNewWindow -Wait -FilePath $DCU -ArgumentList '/applyUpdates -autoSuspendBitLocker=enable -reboot=disable'
  }
  catch {
    Write-Warning 'Unable to apply updates using the dcu-cli.'
    Write-Warning $_
    exit 1
  }
}

# Set PowerShell preferences
Set-Location -Path $env:SystemRoot
$ProgressPreference = 'SilentlyContinue'
$ErrorActionPreference = 'Stop'
if ([Net.ServicePointManager]::SecurityProtocol -notcontains 'Tls12' -and [Net.ServicePointManager]::SecurityProtocol -notcontains 'Tls13') {
  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
}

# Check device manufacturer
if ((Get-CimInstance -ClassName Win32_BIOS).Manufacturer -notlike '*Dell*') {
  Write-Output "`nNot a Dell system. Aborting..."
  exit 0
}

# Handle Prerequisites / Dependencies
Remove-IncompatibleApps
Install-DotNetDesktopRuntime

# Install DCU and available updates
Install-DellCommandUpdate
Invoke-DellCommandUpdate