86°F Knoxville
Mon–Fri 9–5 ETAnswered by a human
Automation · RESOLVED

Mass BIOS & Driver Updates: Dell Fleet Vulnerability Patching

Critical BIOS vulnerability affecting 127 Dell machines. Manual updates would take weeks. Dell Command Update via RMM: 2 weeks, zero site visits.

127
Machines

Incident

Dell security bulletin dropped on a Friday: critical BIOS vulnerability affecting OptiPlex and Latitude models, CVSS 8.2, local privilege escalation. Our client had 127 affected Dell machines split across 4 locations, and the cyber insurance policy obligated them to patch within 30 days of a critical bulletin. Manual updates would mean physically visiting every machine — weeks of work and a lot of windshield time.

The challenge

BIOS updates used to be a hands-on nightmare. You download the .exe from Dell's support site, stage it on a USB stick or a share, visit each machine, run it, reboot, pray nothing flashes wrong. For 127 machines across 4 sites, that's weeks and a nonzero brick risk per machine.

Initial assessment:

  • affected machines: 127 Dell OptiPlex 7080, 7090, Latitude 5520, 5530
  • vulnerability: BIOS privilege escalation, CVSS 8.2
  • compliance deadline: 30 days from bulletin release
  • constraint: zero disruption to business operations — updates during off-hours only

Dell Command Update is Dell's official tool for managing drivers and BIOS. It has a CLI mode that can scan for updates, download them, and apply them silently. The trick is getting it installed and configured across the fleet without touching each machine.

The solution: Dell command update via RMM

We built a PowerShell wrapper that installs Dell Command Update via winget (so it's always the current version), configures it for silent unattended operation, and runs the scan/apply cycle. Three configuration options did the heavy lifting:

  • autoSuspendBitLocker=enable: automatically suspends BitLocker before a BIOS update and resumes after. Without this, post-update reboots hit the BitLocker recovery key prompt and users get locked out.
  • userConsent=disable: no popups asking users to approve updates
  • reboot=disable: updates stage but don't force a reboot — we control reboot timing separately through the RMM
dell_command_update.ps1View on GitHub →
$ErrorActionPreference = 'Stop'

<#
██╗     ██╗███╗   ███╗███████╗██╗  ██╗ █████╗ ██╗    ██╗██╗  ██╗
██║     ██║████╗ ████║██╔════╝██║  ██║██╔══██╗██║    ██║██║ ██╔╝
██║     ██║██╔████╔██║█████╗  ███████║███████║██║ █╗ ██║█████╔╝
██║     ██║██║╚██╔╝██║██╔══╝  ██╔══██║██╔══██║██║███╗██║██╔═██╗
███████╗██║██║ ╚═╝ ██║███████╗██║  ██║██║  ██║╚███╔███╔╝██║  ██╗
╚══════╝╚═╝╚═╝     ╚═╝╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝ ╚══╝╚══╝ ╚═╝  ╚═╝
================================================================================
 SCRIPT   : Dell Command Update                                         v2.1.4
 AUTHOR   : Limehawk.io
 DATE     : January 2026
 USAGE    : .\dell_command_update.ps1
================================================================================
 FILE     : dell_command_update.ps1
 DESCRIPTION : Installs Dell Command Update and applies driver/firmware updates
--------------------------------------------------------------------------------
 README
--------------------------------------------------------------------------------
 PURPOSE

 Installs Dell Command Update via winget and runs it to scan and apply
 driver/firmware updates silently. Designed for automated Dell system maintenance
 in RMM environments.

 DATA SOURCES & PRIORITY

 1) Hardcoded values (defined within the script body)
 2) winget package manager
 3) Dell Command Update CLI

 REQUIRED INPUTS

 - DcuCliPath    : Path to Dell Command Update CLI executable
 - DcuLogPath    : Directory for DCU log files
 - WingetId      : winget package ID for Dell Command Update

 SETTINGS

 - Uses winget for package management
 - Configures DCU to auto-suspend BitLocker and disable user consent prompts
 - Applies updates without rebooting (reboot=disable)
 - Logs scan and apply operations to C:\dell\logs

 BEHAVIOR

 1. Verifies winget is available
 2. Installs Dell Command Update if not present
 3. Verifies DCU CLI exists
 4. Configures DCU for silent operation
 5. Scans for available updates and outputs log to console
 6. Applies updates without rebooting and outputs log to console

 PREREQUISITES

 - Windows 10 1809+ or Windows 11 with winget
 - Dell system (script will fail on non-Dell hardware)
 - Admin privileges for installation and updates

 SECURITY NOTES

 - No secrets in logs
 - Only affects Dell driver/firmware components

 EXIT CODES

 - 0: Success
 - 1: Failure (winget not found, DCU not installed, etc.)

 EXAMPLE RUN

 [INFO] INPUT VALIDATION
 ==============================================================
 DCU CLI Path : C:\Program Files\Dell\CommandUpdate\dcu-cli.exe
 DCU Log Path : C:\dell\logs
 Winget ID    : Dell.CommandUpdate

 [RUN] OPERATION
 ==============================================================
 Checking for winget...
 winget found
 Checking for Dell Command Update...
 Dell Command Update already installed
 Configuring Dell Command Update...
 Configuration complete
 Scanning for updates...
 Scan complete

 [INFO] SCAN LOG
 ==============================================================
 <Dell Command Update scan results displayed here>

 [RUN] APPLY UPDATES
 ==============================================================
 Applying updates...
 Updates applied

 [INFO] APPLY UPDATES LOG
 ==============================================================
 <Dell Command Update apply results displayed here>

 [OK] RESULT
 ==============================================================
 Status : Success

 [OK] SCRIPT COMPLETED
 ==============================================================

--------------------------------------------------------------------------------
 CHANGELOG
--------------------------------------------------------------------------------
 2026-01-19 v2.1.4 Fixed EXAMPLE RUN section formatting
 2026-01-19 v2.1.3 Updated to two-line ASCII console output style
 2026-01-14 v2.1.2 Fixed header formatting for framework compliance
 2025-12-23 v2.1.1 Updated to Limehawk Script Framework
 2025-12-01 v2.1.0 Output scan and apply log contents to console for RMM visibility
 2025-12-01 v2.0.0 Switched from Chocolatey to winget for package management
 2025-11-29 v1.0.0 Initial Style A implementation
================================================================================
#>

Set-StrictMode -Version Latest

# ==== STATE ====
$errorOccurred = $false
$errorText = ""

# ==== HARDCODED INPUTS ====
$DcuCliPath = "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe"
$DcuLogPath = "C:\dell\logs"
$WingetId = "Dell.CommandUpdate"

# ==== VALIDATION ====
if ([string]::IsNullOrWhiteSpace($DcuCliPath)) {
    $errorOccurred = $true
    if ($errorText.Length -gt 0) { $errorText += "`n" }
    $errorText += "- DcuCliPath is required."
}
if ([string]::IsNullOrWhiteSpace($DcuLogPath)) {
    $errorOccurred = $true
    if ($errorText.Length -gt 0) { $errorText += "`n" }
    $errorText += "- DcuLogPath is required."
}
if ([string]::IsNullOrWhiteSpace($WingetId)) {
    $errorOccurred = $true
    if ($errorText.Length -gt 0) { $errorText += "`n" }
    $errorText += "- WingetId is required."
}

if ($errorOccurred) {
    Write-Host ""
    Write-Host "[ERROR] VALIDATION FAILED"
    Write-Host "=============================================================="
    Write-Host $errorText
    exit 1
}

# ==== RUNTIME OUTPUT ====
Write-Host ""
Write-Host "[INFO] INPUT VALIDATION"
Write-Host "=============================================================="
Write-Host "DCU CLI Path : $DcuCliPath"
Write-Host "DCU Log Path : $DcuLogPath"
Write-Host "Winget ID    : $WingetId"

Write-Host ""
Write-Host "[RUN] OPERATION"
Write-Host "=============================================================="

try {
    # Check for winget
    Write-Host "Checking for winget..."
    $wingetPath = Get-Command "winget" -ErrorAction SilentlyContinue
    if (-not $wingetPath) {
        throw "winget is not available. Windows 10 1809+ or Windows 11 required."
    }
    Write-Host "winget found"

    # Check if Dell Command Update is already installed
    Write-Host "Checking for Dell Command Update..."
    $installed = winget list -e --id $WingetId --accept-source-agreements 2>$null | Select-String $WingetId

    if (-not $installed) {
        Write-Host "Installing $WingetId..."
        $installResult = winget install -e --id $WingetId --silent --accept-package-agreements --accept-source-agreements 2>&1
        if ($LASTEXITCODE -ne 0) {
            throw "Failed to install $WingetId. Exit code: $LASTEXITCODE"
        }
        Write-Host "Installation complete"
    } else {
        Write-Host "Dell Command Update already installed"

        # Check for updates
        Write-Host "Checking for DCU updates..."
        $upgradeResult = winget upgrade -e --id $WingetId --silent --accept-package-agreements --accept-source-agreements 2>&1
        if ($LASTEXITCODE -eq 0) {
            Write-Host "DCU upgraded to latest version"
        }
    }

    # Verify DCU CLI exists
    if (-not (Test-Path $DcuCliPath)) {
        throw "DCU CLI not found at $DcuCliPath. Installation may have failed."
    }

    # Ensure log directory exists
    if (-not (Test-Path $DcuLogPath)) {
        New-Item -Path $DcuLogPath -ItemType Directory -Force | Out-Null
        Write-Host "Created log directory: $DcuLogPath"
    }

    # Configure DCU for silent operation
    Write-Host "Configuring Dell Command Update..."
    & $DcuCliPath /configure -silent -autoSuspendBitLocker=enable -userConsent=disable
    Write-Host "Configuration complete"

    # Scan for updates
    Write-Host "Scanning for updates..."
    & $DcuCliPath /scan -outputLog="$DcuLogPath\scan.log"
    Write-Host "Scan complete"

    # Output scan log
    Write-Host ""
    Write-Host "[INFO] SCAN LOG"
    Write-Host "=============================================================="
    if (Test-Path "$DcuLogPath\scan.log") {
        Get-Content "$DcuLogPath\scan.log" | ForEach-Object { Write-Host $_ }
    } else {
        Write-Host "Scan log not found."
    }

    # Apply updates without rebooting
    Write-Host ""
    Write-Host "[RUN] APPLY UPDATES"
    Write-Host "=============================================================="
    Write-Host "Applying updates..."
    & $DcuCliPath /applyUpdates -reboot=disable -outputLog="$DcuLogPath\applyUpdates.log"
    Write-Host "Updates applied"

    # Output apply log
    Write-Host ""
    Write-Host "[INFO] APPLY UPDATES LOG"
    Write-Host "=============================================================="
    if (Test-Path "$DcuLogPath\applyUpdates.log") {
        Get-Content "$DcuLogPath\applyUpdates.log" | ForEach-Object { Write-Host $_ }
    } else {
        Write-Host "Apply updates log not found."
    }

} catch {
    $errorOccurred = $true
    $errorText = $_.Exception.Message
}

if ($errorOccurred) {
    Write-Host ""
    Write-Host "[ERROR] OPERATION FAILED"
    Write-Host "=============================================================="
    Write-Host $errorText

    Write-Host ""
    Write-Host "[ERROR] RESULT"
    Write-Host "=============================================================="
    Write-Host "Status : Failure"
} else {
    Write-Host ""
    Write-Host "[OK] RESULT"
    Write-Host "=============================================================="
    Write-Host "Status : Success"
}

Write-Host ""
if ($errorOccurred) {
    Write-Host "[ERROR] FINAL STATUS"
} else {
    Write-Host "[OK] FINAL STATUS"
}
Write-Host "=============================================================="
if ($errorOccurred) {
    Write-Host "Dell Command Update process failed. See error above."
} else {
    Write-Host "Dell Command Update completed successfully."
    Write-Host "Reboot may be required for some updates to take effect."
}

Write-Host ""
if ($errorOccurred) {
    Write-Host "[ERROR] SCRIPT COMPLETED"
} else {
    Write-Host "[OK] SCRIPT COMPLETED"
}
Write-Host "=============================================================="

if ($errorOccurred) {
    exit 1
} else {
    exit 0
}

Deployment strategy

We didn't blast the script to all 127 machines at once. Firmware is the one area where "deploy to all" is a genuinely bad idea — one bad update or a power loss mid-flash can brick a machine, and bricked machines across four locations is not a Monday anyone wants.

  1. pilot group (5 machines): one of each model type, users notified, ran during lunch
  2. wave 1 (30 machines): IT department and volunteers, overnight deployment
  3. wave 2 (50 machines): non-critical workstations, weekend deployment
  4. wave 3 (42 machines): remaining systems including exec laptops, scheduled maintenance window

After each wave we verified BIOS versions via RMM inventory scan. Any machine that didn't update got flagged for manual review before we moved on.

Outcome

All 127 machines patched within 2 weeks, well ahead of the 30-day deadline. Zero bricked machines. Zero user complaints. Total hands-on time: about 4 hours for script development, pilot testing, and monitoring. Without automation this would have been 200+ hours of on-site work split across four offices.

machines patched: 127 across 4 locations time to complete: 2 weeks (phased rollout) hours saved: 196+ vs manual updates bricked machines: zero

Key takeaways