├── .gitignore ├── 0_pre_install ├── 0.1_check_for_dcu.ps1 ├── 0.2_DCU_initial_status.ps1 ├── 0.3_uninstall_invalid_versions.ps1 ├── 0.4_create_logs_storage.ps1 └── 0.5_generate_status_report.ps1 ├── 1_install_dcu └── 1.0_generic_install.ps1 ├── 2_config ├── 2.0_dcu_configure.ps1 ├── 2.2_generate_settings_xml.ps1 └── 2.4_dcu_lockSettings.ps1 ├── 3_scans └── 3_dcu_scan_all.ps1 ├── 4_apply_updates ├── 4.1_dcu_update_no_reboot.ps1 └── 4.2_dcu_update_reboot.ps1 └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs and exports 2 | /logs_and_exports/ 3 | 4 | # Mystery DCU_Extra 5 | /DCU_Extra/ 6 | 7 | # Git files 8 | .git 9 | ./git/* 10 | 11 | # OS files 12 | .DS_Store 13 | Thumbs.db 14 | desktop.ini 15 | 16 | # Editor files 17 | .vscode/ 18 | *.sublime-project 19 | *.sublime-workspace 20 | *.swp 21 | *.swo 22 | 23 | # Dependency directories 24 | node_modules/ 25 | __pycache__/ 26 | *.pyc 27 | *.pyo 28 | .venv/ 29 | venv/ 30 | 31 | # Build artifacts 32 | /dist/ 33 | /build/ 34 | 35 | # Environment files 36 | .env 37 | .env.* 38 | 39 | # Backup files 40 | *~ 41 | *.bak 42 | *.old 43 | 44 | # Archives and compressed files 45 | *.zip 46 | *.tar.gz 47 | *.rar 48 | 49 | # Database files 50 | *.sqlite 51 | *.sqlite3 52 | 53 | # Project-specific 54 | /cache/ 55 | /tmp/ -------------------------------------------------------------------------------- /0_pre_install/0.1_check_for_dcu.ps1: -------------------------------------------------------------------------------- 1 | # Define the CLI executable name 2 | $cliExecutable = 'dcu-cli.exe' 3 | 4 | # Define potential locations where DCU can be found 5 | $potentialDcuInstallPaths = @( 6 | 'C:\Program Files\WindowsApps\', 7 | 'C:\Program Files (x86)\Dell\', 8 | 'C:\Program Files\Dell\' 9 | ) 10 | 11 | # Patterns to detect various naming conventions for Dell Command Update 12 | $patterns = @( 13 | "*dell*command*update*", "*command*update*", "*dcu*", "*DellCommandUpdate*", 14 | "*Dell*Update*", "*DellInc.*CommandUpdate*", "*Dell.*CommandUpdate*", 15 | "DellCommandUpdate_*", "*CommandUpdate*_*_*" 16 | ) 17 | 18 | # Function to recursively search for the CLI executable 19 | function Find-CliExecutable { 20 | [CmdletBinding()] 21 | param ( 22 | [Parameter(Mandatory=$true)] 23 | [string]$Path, 24 | [string]$Executable 25 | ) 26 | 27 | $cliPath = $null 28 | try { 29 | $cliPath = Get-ChildItem -Path $Path -Recurse -Filter $Executable -ErrorAction Stop | 30 | Select-Object -First 1 -ExpandProperty FullName 31 | } 32 | catch { 33 | Write-Warning "Error searching in path: $Path. Error: $_" 34 | } 35 | return $cliPath 36 | } 37 | 38 | # Function to locate DCU installation folders and validate installation 39 | function Test-DcuInstallation { 40 | [CmdletBinding()] 41 | param( 42 | [Parameter(Mandatory=$true)] 43 | [ValidateNotNullOrEmpty()] 44 | [array]$Paths, 45 | [Parameter(Mandatory=$true)] 46 | [ValidateNotNullOrEmpty()] 47 | [array]$Patterns, 48 | [Parameter(Mandatory=$true)] 49 | [ValidateNotNullOrEmpty()] 50 | [string]$CliExecutable 51 | ) 52 | 53 | $dcuFound = $false 54 | $cliFound = $false 55 | $cliPath = $null 56 | 57 | foreach ($path in $Paths) { 58 | if (Test-Path $path) { 59 | try { 60 | $folders = Get-ChildItem -Path $path -Directory -Recurse -ErrorAction Stop | 61 | Where-Object { 62 | $folder = $_.FullName 63 | ($Patterns | Where-Object { $folder -like $_ }) -ne $null 64 | } 65 | 66 | if ($folders) { 67 | $dcuFound = $true 68 | foreach ($folder in $folders) { 69 | $cliPath = Find-CliExecutable -Path $folder.FullName -Executable $CliExecutable 70 | if ($cliPath) { 71 | $cliFound = $true 72 | break 73 | } 74 | } 75 | } 76 | if ($cliFound) { break } 77 | } 78 | catch { 79 | Write-Warning "Error searching in path: $path. Error: $_" 80 | } 81 | } 82 | else { 83 | Write-Verbose "Path not found: $path" 84 | } 85 | } 86 | 87 | if (-not $dcuFound) { 88 | Write-Output "DCU Installation Status: Not Installed`nREASON: No Dell Command Update folders found" 89 | } 90 | elseif ($cliFound) { 91 | Write-Output "DCU Installation Status: Valid`nREASON: CLI found at: $cliPath" 92 | } 93 | else { 94 | Write-Output "DCU Installation Status: Invalid`nREASON: Dell Command Update folder found, but CLI not present" 95 | } 96 | } 97 | 98 | # Main execution 99 | Test-DcuInstallation -Paths $potentialDcuInstallPaths -Patterns $patterns -CliExecutable $cliExecutable 100 | -------------------------------------------------------------------------------- /0_pre_install/0.2_DCU_initial_status.ps1: -------------------------------------------------------------------------------- 1 | # Define the CLI executable name 2 | $cliExecutable = 'dcu-cli.exe' 3 | 4 | # Define potential locations where DCU can be found 5 | $potentialDcuInstallPaths = @( 6 | 'C:\Program Files\WindowsApps\', 7 | 'C:\Program Files (x86)\Dell\', 8 | 'C:\Program Files\Dell\' 9 | ) 10 | 11 | # Patterns to detect Dell Command Update 12 | $dcuPatterns = @( 13 | "*Dell*Command*Update*", 14 | "*DellCommandUpdate*", 15 | "DCU*", 16 | "*Command*Update*" 17 | ) 18 | 19 | # Patterns to detect conflicting Dell applications 20 | $conflictingAppPatterns = @( 21 | "*Dell Update*", 22 | "*DellUpdate*", 23 | "*Dell SupportAssist*", 24 | "*DellSupportAssist*" 25 | ) 26 | 27 | # Function to write verbose output 28 | function Write-VerboseOutput { 29 | param([string]$Message) 30 | Write-Verbose $Message 31 | # Add logging to a file if needed 32 | # Add-Content -Path "log.txt" -Value $Message 33 | } 34 | 35 | # Function to recursively search for the CLI executable with limited depth 36 | function Find-CliExecutable { 37 | [CmdletBinding()] 38 | param ( 39 | [Parameter(Mandatory=$true)] 40 | [string]$Path, 41 | [string]$Executable, 42 | [int]$MaxDepth = 3 43 | ) 44 | 45 | $cliPath = $null 46 | try { 47 | $cliPath = Get-ChildItem -Path $Path -Recurse -Filter $Executable -Depth $MaxDepth -ErrorAction Stop | 48 | Select-Object -First 1 -ExpandProperty FullName 49 | } 50 | catch { 51 | Write-VerboseOutput "Error searching in path: $Path. Error: $_" 52 | } 53 | return $cliPath 54 | } 55 | 56 | # Function to check if a folder matches any of the given patterns 57 | function Test-FolderMatchesPattern { 58 | param ( 59 | [Parameter(Mandatory=$true)] 60 | [string]$FolderPath, 61 | [Parameter(Mandatory=$true)] 62 | [array]$Patterns 63 | ) 64 | 65 | foreach ($pattern in $Patterns) { 66 | if ($FolderPath -like $pattern) { 67 | return $true 68 | } 69 | } 70 | return $false 71 | } 72 | 73 | # Function to check for conflicting applications 74 | function Test-ConflictingApps { 75 | param ( 76 | [Parameter(Mandatory=$true)] 77 | [array]$Paths, 78 | [Parameter(Mandatory=$true)] 79 | [array]$Patterns 80 | ) 81 | 82 | foreach ($path in $Paths) { 83 | if (Test-Path $path) { 84 | try { 85 | $conflictingFolders = Get-ChildItem -Path $path -Directory -Recurse -Depth 2 -ErrorAction Stop | 86 | Where-Object { Test-FolderMatchesPattern -FolderPath $_.FullName -Patterns $Patterns } 87 | 88 | if ($conflictingFolders) { 89 | return $true 90 | } 91 | } 92 | catch { 93 | Write-VerboseOutput "Error searching for conflicting apps in path: $path. Error: $_" 94 | } 95 | } 96 | } 97 | return $false 98 | } 99 | 100 | # Function to locate DCU installation folders and validate installation 101 | function Test-DcuInstallation { 102 | [CmdletBinding()] 103 | param( 104 | [Parameter(Mandatory=$true)] 105 | [ValidateNotNullOrEmpty()] 106 | [array]$Paths, 107 | [Parameter(Mandatory=$true)] 108 | [ValidateNotNullOrEmpty()] 109 | [array]$DcuPatterns, 110 | [Parameter(Mandatory=$true)] 111 | [ValidateNotNullOrEmpty()] 112 | [string]$CliExecutable, 113 | [Parameter(Mandatory=$true)] 114 | [ValidateNotNullOrEmpty()] 115 | [array]$ConflictingPatterns 116 | ) 117 | 118 | $result = [PSCustomObject]@{ 119 | DCUFound = $false 120 | CLIFound = $false 121 | ConflictingAppFound = $false 122 | CLIPath = $null 123 | Status = "" 124 | Reason = "" 125 | } 126 | 127 | # Check for conflicting applications first 128 | $result.ConflictingAppFound = Test-ConflictingApps -Paths $Paths -Patterns $ConflictingPatterns 129 | if ($result.ConflictingAppFound) { 130 | $result.Status = "Invalid" 131 | $result.Reason = "Conflicting Dell application found" 132 | Ninja-Property-Set dellCommandUpdateInstalled "INVALID: Dell Update Detected" 133 | return $result 134 | } 135 | 136 | # Check for DCU installation 137 | foreach ($path in $Paths) { 138 | if (Test-Path $path) { 139 | try { 140 | $folders = Get-ChildItem -Path $path -Directory -Recurse -Depth 2 -ErrorAction Stop | 141 | Where-Object { Test-FolderMatchesPattern -FolderPath $_.FullName -Patterns $DcuPatterns } 142 | 143 | if ($folders) { 144 | $result.DCUFound = $true 145 | foreach ($folder in $folders) { 146 | $cliPath = Find-CliExecutable -Path $folder.FullName -Executable $CliExecutable 147 | if ($cliPath) { 148 | $result.CLIFound = $true 149 | $result.CLIPath = $cliPath 150 | break 151 | } 152 | } 153 | } 154 | 155 | if ($result.CLIFound) { break } 156 | } 157 | catch { 158 | Write-VerboseOutput "Error searching in path: $path. Error: $_" 159 | } 160 | } 161 | else { 162 | Write-VerboseOutput "Path not found: $path" 163 | } 164 | } 165 | 166 | # Determine installation status 167 | if (-not $result.DCUFound) { 168 | $result.Status = "Not Installed" 169 | $result.Reason = "No Dell Command Update folders found" 170 | Ninja-Property-Set dellCommandUpdateInstalled "NO: DCU not present" 171 | } 172 | elseif ($result.DCUFound -and -not $result.CLIFound) { 173 | $result.Status = "Invalid" 174 | $result.Reason = "Dell Command Update folder found, but CLI not present" 175 | Ninja-Property-Set dellCommandUpdateInstalled "INVALID: no cli" 176 | } 177 | elseif ($result.CLIFound) { 178 | $result.Status = "Valid" 179 | $result.Reason = "CLI found at: $($result.CLIPath)" 180 | Ninja-Property-Set dellCommandUpdateInstalled "YES: no config" 181 | } 182 | 183 | return $result 184 | } 185 | 186 | # Function to get the current value of the custom field 187 | function Get-CustomFieldValue { 188 | try { 189 | $currentValue = Ninja-Property-Get dellCommandUpdateInstalled 190 | return $currentValue 191 | } 192 | catch { 193 | Write-VerboseOutput "Error getting custom field value: $_" 194 | return $null 195 | } 196 | } 197 | 198 | # Main execution 199 | $currentValue = Get-CustomFieldValue 200 | 201 | if ($currentValue -eq "YES: configured") { 202 | Write-Output "DCU is already configured. Skipping installation check." 203 | } 204 | else { 205 | $result = Test-DcuInstallation -Paths $potentialDcuInstallPaths -DcuPatterns $dcuPatterns -CliExecutable $cliExecutable -ConflictingPatterns $conflictingAppPatterns 206 | Write-Output "DCU Installation Status: $($result.Status)" 207 | Write-Output "REASON: $($result.Reason)" 208 | } 209 | -------------------------------------------------------------------------------- /0_pre_install/0.3_uninstall_invalid_versions.ps1: -------------------------------------------------------------------------------- 1 | # Function to log messages and update status 2 | function Write-Log { 3 | param( 4 | [string]$Message, 5 | [switch]$UpdateStatus 6 | ) 7 | Write-Output "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message" 8 | if ($UpdateStatus) { 9 | # Ninja-Property-Set dcuRemovalStatus $Message 10 | Write-Output "Status Updated: $Message" 11 | } 12 | } 13 | 14 | # Function to remove registry keys 15 | function Remove-RegistryKeys { 16 | $regPaths = @( 17 | "HKLM:\SOFTWARE\Dell\UpdateService", 18 | "HKLM:\SOFTWARE\Dell\CommandUpdate", 19 | "HKLM:\SOFTWARE\WOW6432Node\Dell\UpdateService", 20 | "HKLM:\SOFTWARE\WOW6432Node\Dell\CommandUpdate", 21 | "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DellCommandUpdate", 22 | "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\DellCommandUpdate" 23 | ) 24 | 25 | foreach ($path in $regPaths) { 26 | if (Test-Path $path) { 27 | Write-Log "Removing registry key: $path" 28 | Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue 29 | } 30 | } 31 | 32 | # Search for any remaining Dell Command Update related keys 33 | $dellKeys = Get-ChildItem "HKLM:\SOFTWARE", "HKLM:\SOFTWARE\WOW6432Node" -Recurse -ErrorAction SilentlyContinue | 34 | Where-Object { $_.Name -like "*Dell*Command*Update*" } 35 | foreach ($key in $dellKeys) { 36 | Write-Log "Removing additional registry key: $($key.Name)" 37 | Remove-Item -Path $key.PSPath -Recurse -Force -ErrorAction SilentlyContinue 38 | } 39 | } 40 | 41 | # Function to remove services 42 | function Remove-Services { 43 | $services = Get-WmiObject -Class Win32_Service | Where-Object { $_.Name -like "*DellCommandUpdate*" -or $_.DisplayName -like "*Dell Command Update*" } 44 | foreach ($service in $services) { 45 | Write-Log "Stopping and removing service: $($service.Name)" 46 | Stop-Service -Name $service.Name -Force -ErrorAction SilentlyContinue 47 | $service.Delete() 48 | } 49 | } 50 | 51 | # Function to remove scheduled tasks 52 | function Remove-ScheduledTasks { 53 | $tasks = Get-ScheduledTask | Where-Object { $_.TaskName -like "*Dell*Command*Update*" } 54 | foreach ($task in $tasks) { 55 | Write-Log "Removing scheduled task: $($task.TaskName)" 56 | Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false -ErrorAction SilentlyContinue 57 | } 58 | } 59 | 60 | # Function to remove files and folders 61 | function Remove-FilesAndFolders { 62 | $paths = @( 63 | "C:\Program Files\Dell\CommandUpdate", 64 | "C:\Program Files (x86)\Dell\CommandUpdate", 65 | "C:\ProgramData\Dell\CommandUpdate" 66 | ) 67 | 68 | foreach ($path in $paths) { 69 | if (Test-Path $path) { 70 | Write-Log "Removing folder: $path" 71 | Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue 72 | } 73 | } 74 | 75 | # Search for any remaining Dell Command Update related folders 76 | $dellFolders = Get-ChildItem -Path "C:\Program Files*", "C:\ProgramData" -Recurse -Directory -ErrorAction SilentlyContinue | 77 | Where-Object { $_.Name -like "*Dell*Command*Update*" } 78 | foreach ($folder in $dellFolders) { 79 | Write-Log "Removing additional folder: $($folder.FullName)" 80 | Remove-Item -Path $folder.FullName -Recurse -Force -ErrorAction SilentlyContinue 81 | } 82 | } 83 | 84 | # Function to remove Windows Apps packages 85 | function Remove-WindowsAppsPackages { 86 | $packages = Get-AppxPackage -AllUsers | Where-Object { $_.Name -like "*DellCommandUpdate*" } 87 | foreach ($package in $packages) { 88 | Write-Log "Removing Windows Apps package: $($package.PackageFullName)" 89 | try { 90 | Remove-AppxPackage -Package $package.PackageFullName -AllUsers -ErrorAction Stop 91 | } 92 | catch { 93 | Write-Log "Failed to remove package using Remove-AppxPackage. Attempting alternative method..." 94 | try { 95 | $null = & powershell.exe -NonInteractive -Command "Get-AppxPackage -AllUsers '$($package.Name)' | Remove-AppxPackage -AllUsers" 96 | Write-Log "Alternative removal method completed." 97 | } 98 | catch { 99 | Write-Log "Failed to remove package using alternative method: $_" 100 | } 101 | } 102 | } 103 | 104 | # Remove provisioned packages 105 | $provisionedPackages = Get-AppxProvisionedPackage -Online | Where-Object { $_.DisplayName -like "*DellCommandUpdate*" } 106 | foreach ($package in $provisionedPackages) { 107 | Write-Log "Removing provisioned package: $($package.PackageName)" 108 | try { 109 | Remove-AppxProvisionedPackage -PackageName $package.PackageName -Online -ErrorAction Stop 110 | } 111 | catch { 112 | Write-Log "Failed to remove provisioned package: $_" 113 | } 114 | } 115 | } 116 | 117 | # Function to remove additional files and folders 118 | function Remove-AdditionalFiles { 119 | $additionalPaths = @( 120 | "C:\ProgramData\Dell\UpdatePackage\Log", 121 | "C:\ProgramData\Microsoft\Windows\AppRepository" 122 | ) 123 | 124 | foreach ($path in $additionalPaths) { 125 | Get-ChildItem -Path $path -Recurse -ErrorAction SilentlyContinue | 126 | Where-Object { $_.Name -like "*Dell*Command*Update*" } | 127 | ForEach-Object { 128 | Write-Log "Removing additional file: $($_.FullName)" 129 | Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue 130 | } 131 | } 132 | } 133 | 134 | # Main removal function 135 | function Remove-DellCommandUpdateThoroughly { 136 | Write-Log "Starting thorough removal of Dell Command Update" -UpdateStatus 137 | 138 | Write-Log "Removing registry keys..." -UpdateStatus 139 | Remove-RegistryKeys 140 | 141 | Write-Log "Removing services..." -UpdateStatus 142 | Remove-Services 143 | 144 | Write-Log "Removing scheduled tasks..." -UpdateStatus 145 | Remove-ScheduledTasks 146 | 147 | Write-Log "Removing files and folders..." -UpdateStatus 148 | Remove-FilesAndFolders 149 | 150 | Write-Log "Removing Windows Apps packages..." -UpdateStatus 151 | Remove-WindowsAppsPackages 152 | 153 | Write-Log "Removing additional files..." -UpdateStatus 154 | Remove-AdditionalFiles 155 | 156 | # Run MSI uninstall commands 157 | Write-Log "Running MSI uninstall commands..." -UpdateStatus 158 | $msiCommands = @( 159 | @{Version = "4.1"; GUID = "{D8D5991F-7A8A-4B1B-9F2C-A91F861F1BD3}" }, 160 | @{Version = "4.2"; GUID = "{2F5D3D9E-9A3B-4AEE-8475-E3D5E7B42900}" }, 161 | @{Version = "4.3"; GUID = "{E9C7D65F-DBAE-4EE7-8F40-3A0AF4D4CFB0}" }, 162 | @{Version = "4.4"; GUID = "{D7366302-C0B3-4834-A473-D0F06BD8AEDF}" }, 163 | @{Version = "4.5"; GUID = "{FFD8CF3D-3063-4D97-B007-26258E71D02F}" }, 164 | @{Version = "4.6"; GUID = "{FFD8CF3D-3063-4D97-B007-26258E71D02F}" }, 165 | @{Version = "4.7"; GUID = "{1309CCD0-A923-4203-8A92-377F37EE2C29}" }, 166 | @{Version = "4.8"; GUID = "{D2E875B4-E71A-4AD2-9E0C-3E097A3D54FC}" }, 167 | @{Version = "4.9"; GUID = "{84F3B225-B7D3-45AC-ACA3-DC21DA802140}" } 168 | ) 169 | 170 | foreach ($cmd in $msiCommands) { 171 | Write-Log "Attempting to remove Dell Command Update version $($cmd.Version)" 172 | $process = Start-Process -FilePath "MsiExec.exe" -ArgumentList "/qn", "/norestart", "/X$($cmd.GUID)" -Wait -NoNewWindow -PassThru 173 | if ($process.ExitCode -eq 0) { 174 | Write-Log "Successfully removed Dell Command Update version $($cmd.Version)" 175 | } 176 | else { 177 | Write-Log "Failed to remove Dell Command Update version $($cmd.Version). Exit code: $($process.ExitCode)" 178 | } 179 | } 180 | 181 | Write-Log "Thorough removal process completed. Performing final check..." -UpdateStatus 182 | } 183 | 184 | # Run the thorough removal 185 | Remove-DellCommandUpdateThoroughly 186 | 187 | # Final check 188 | $remainingFiles = Get-ChildItem -Path "C:\Program Files*", "C:\ProgramData" -Recurse -ErrorAction SilentlyContinue | 189 | Where-Object { $_.Name -like "*Dell*Command*Update*" } 190 | $remainingRegKeys = Get-ChildItem "HKLM:\SOFTWARE", "HKLM:\SOFTWARE\WOW6432Node" -Recurse -ErrorAction SilentlyContinue | 191 | Where-Object { $_.Name -like "*Dell*Command*Update*" } 192 | $remainingServices = Get-WmiObject -Class Win32_Service | Where-Object { $_.Name -like "*DellCommandUpdate*" -or $_.DisplayName -like "*Dell Command Update*" } 193 | $remainingTasks = Get-ScheduledTask | Where-Object { $_.TaskName -like "*Dell*Command*Update*" } 194 | $remainingPackages = Get-AppxPackage -AllUsers | Where-Object { $_.Name -like "*DellCommandUpdate*" } 195 | 196 | if ($remainingFiles -or $remainingRegKeys -or $remainingServices -or $remainingTasks -or $remainingPackages) { 197 | Write-Log "Warning: Some Dell Command Update related items may still be present on the system." -UpdateStatus 198 | if ($remainingFiles) { 199 | Write-Log "Remaining files or folders:" 200 | $remainingFiles | ForEach-Object { Write-Log $_.FullName } 201 | } 202 | if ($remainingRegKeys) { 203 | Write-Log "Remaining registry keys:" 204 | $remainingRegKeys | ForEach-Object { Write-Log $_.Name } 205 | } 206 | if ($remainingServices) { 207 | Write-Log "Remaining services:" 208 | $remainingServices | ForEach-Object { Write-Log $_.Name } 209 | } 210 | if ($remainingTasks) { 211 | Write-Log "Remaining scheduled tasks:" 212 | $remainingTasks | ForEach-Object { Write-Log $_.TaskName } 213 | } 214 | if ($remainingPackages) { 215 | Write-Log "Remaining Windows Apps packages:" 216 | $remainingPackages | ForEach-Object { Write-Log $_.PackageFullName } 217 | } 218 | Write-Log "Dell Command Update removal incomplete. Manual intervention may be required." -UpdateStatus 219 | } 220 | else { 221 | Write-Log "Success: Dell Command Update has been fully and completely removed from the system." -UpdateStatus 222 | } 223 | -------------------------------------------------------------------------------- /0_pre_install/0.4_create_logs_storage.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | # take a parameter taht is the path you wish to use to create the folder 3 | # [Parameter(Mandatory=$true)] 4 | [string]$env:folderPath 5 | ) 6 | 7 | try { 8 | # Construct the full folder path on the C drive 9 | $location = "C:\$env:folderPath" 10 | 11 | # Check if the specified folder exists 12 | if (Test-Path -Path $location -PathType Container) { 13 | Write-Output "Folder '$location' exists." 14 | 15 | # Define the subfolder name 16 | $subfolderName = "logs" 17 | $subfolderPath = Join-Path -Path $location -ChildPath $subfolderName 18 | 19 | # Check if the subfolder already exists 20 | if (-not (Test-Path -Path $subfolderPath -PathType Container)) { 21 | # Create the subfolder if it doesn't exist 22 | New-Item -Path $subfolderPath -ItemType Directory 23 | Write-Output "Subfolder '$subfolderName' created inside '$location'." 24 | } else { 25 | Write-Output "Subfolder '$subfolderName' already exists inside '$location'." 26 | } 27 | 28 | # Set NinjaRMM property 29 | # Note: Ensure Ninja-Property-Set command is correctly configured 30 | Ninja-Property-Set dcuLogLocation $subfolderPath 31 | } else { 32 | Write-Output "Folder '$location' does not exist." 33 | } 34 | } catch { 35 | Write-Output "An error occurred: $_" 36 | } 37 | -------------------------------------------------------------------------------- /0_pre_install/0.5_generate_status_report.ps1: -------------------------------------------------------------------------------- 1 | # Script to collect Dell Command Update Status Information 2 | 3 | function Collect-DCUStatus { 4 | # $logPath = Ninja-Property-Get dcuLogLocation 5 | # $logFile = $logPath 6 | $logFile = "C:\dcuReport.txt" 7 | Write-Output "Collecting Dell Command Update Status Information..." | Out-File $logFile 8 | 9 | # Check running processes 10 | $processes = Get-Process | Where-Object { $_.Name -like "*DellCommand*" -or $_.Name -like "*DCU*" } 11 | if ($processes) { 12 | Write-Output "`nRunning Processes:" | Out-File $logFile -Append 13 | $processes | ForEach-Object { Write-Output $_.Name | Out-File $logFile -Append } 14 | } 15 | 16 | # Check AppX packages 17 | $packages = Get-AppxPackage -AllUsers | Where-Object { $_.Name -like "*DellCommandUpdate*" -or $_.PackageFullName -like "*DellInc.DellCommandUpdate*" } 18 | if ($packages) { 19 | Write-Output "`nAppX Packages:" | Out-File $logFile -Append 20 | $packages | ForEach-Object { Write-Output $_.PackageFullName | Out-File $logFile -Append } 21 | } 22 | 23 | # Check provisioned packages 24 | $provPackages = Get-AppxProvisionedPackage -Online | Where-Object { $_.DisplayName -like "*DellCommandUpdate*" } 25 | if ($provPackages) { 26 | Write-Output "`nProvisioned Packages:" | Out-File $logFile -Append 27 | $provPackages | ForEach-Object { Write-Output $_.PackageName | Out-File $logFile -Append } 28 | } 29 | 30 | # Check registry keys 31 | $dellKeys = Get-ChildItem "HKLM:\SOFTWARE", "HKLM:\SOFTWARE\WOW6432Node", "HKCU:\SOFTWARE" -Recurse -ErrorAction SilentlyContinue | 32 | Where-Object { $_.Name -like "*Dell*Command*Update*" -or $_.Name -like "*DellInc.DellCommandUpdate*" } 33 | if ($dellKeys) { 34 | Write-Output "`nRegistry Keys:" | Out-File $logFile -Append 35 | $dellKeys | ForEach-Object { Write-Output $_.Name | Out-File $logFile -Append } 36 | } 37 | 38 | # Check for scheduled tasks 39 | $dellTasks = Get-ScheduledTask | Where-Object { $_.TaskName -like "*Dell*Command*Update*" -or $_.Name -like "*DellInc.DellCommandUpdate*" } 40 | if ($dellTasks) { 41 | Write-Output "`nScheduled Tasks:" | Out-File $logFile -Append 42 | $dellTasks | ForEach-Object { Write-Output $_.TaskName | Out-File $logFile -Append } 43 | } 44 | 45 | # Check for services 46 | $dellServices = Get-Service | Where-Object { $_.DisplayName -like "*Dell*Command*Update*" -or $_.Name -like "*DellInc.DellCommandUpdate*" } 47 | if ($dellServices) { 48 | Write-Output "`nServices:" | Out-File $logFile -Append 49 | $dellServices | ForEach-Object { Write-Output $_.DisplayName | Out-File $logFile -Append } 50 | } 51 | 52 | Write-Output "DCU status report saved to: $logFile" | Out-File $logFile -Append 53 | } 54 | 55 | # Collect the status 56 | Collect-DCUStatus 57 | -------------------------------------------------------------------------------- /1_install_dcu/1.0_generic_install.ps1: -------------------------------------------------------------------------------- 1 | # Function to download and install Dell Command Update using the installer from Dell's website 2 | function Install-DellCommandUpdateUsingInstaller { 3 | # URL of the Dell Command Update installer 4 | $installerUrl = "https://downloads.dell.com/FOLDER11563484M/1/Dell-Command-Update-Windows-Universal-Application_P83K5_WIN_5.3.0_A00.EXE" 5 | # Path where the installer will be downloaded 6 | $installerPath = "$env:TEMP\DCU_Setup.exe" 7 | try { 8 | # Download the installer 9 | Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath -ErrorAction Stop 10 | # Install the application silently 11 | Start-Process -FilePath $installerPath -ArgumentList '/s' -Wait -NoNewWindow -ErrorAction Stop 12 | # Clean up by removing the installer file 13 | Remove-Item $installerPath -Force -ErrorAction Stop 14 | return $true 15 | } catch { 16 | Write-Output "Failed to install Dell Command Update: $_" 17 | return $false 18 | } 19 | } 20 | # Main script logic 21 | try { 22 | # call the function to install Dell Command Update using the installer from Dell's website 23 | $installSuccess = Install-DellCommandUpdateUsingInstaller 24 | # Set 'dcuInstallStatus' property based on installation success 25 | if ($installSuccess) { 26 | 27 | Ninja-Property-Set dcuInstallStatus 'YES: not configured' 28 | Ninja-Property-Set dellCommandUpdateInstalled 'YES: not configured' 29 | Write-Output 'Dell Command Update successfully installed' 30 | } else { 31 | Ninja-Property-Set dcuInstallStatus 'NO: Install Failed' 32 | Ninja-Property-Set dellCommandUpdateInstalled 'NO: Install Failed' 33 | } 34 | } catch { 35 | # Handle any errors during the installation process 36 | Write-Output "Error during installation process: $_" 37 | # Set custom fields on error 38 | Ninja-Property-Set dcuInstallStatus "NO: $_" 39 | Ninja-Property-Set dellCommandUpdateInstalled "NO: $_" 40 | } 41 | -------------------------------------------------------------------------------- /2_config/2.0_dcu_configure.ps1: -------------------------------------------------------------------------------- 1 | # Define registry keys to be modified 2 | $registryKeys = @{ 3 | 'DCUconfigured' = 1 4 | } 5 | # The DCUconfigured registry key is used by Dell Command Update (DCU) to track whether the system has been configured by the tool. 6 | # Specifically, setting this key to 1 indicates that DCU has completed its configuration on the system. 7 | 8 | # Specify possible paths where dcu-cli.exe might be located 9 | $PossibleDcuCliPaths = @( 10 | "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe", 11 | "C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" 12 | ) 13 | # Find dcu-cli.exe 14 | $DcuCliPath = $PossibleDcuCliPaths | Where-Object { Test-Path $_ -PathType Leaf } | Select-Object -First 1 15 | 16 | if ($DcuCliPath) { 17 | try { 18 | # Backup current registry values 19 | $backupPath = "HKLM:\SOFTWARE\Dell\UpdateService\Clients\CommandUpdate\Preferences\CFG_Backup" 20 | if (-not (Test-Path $backupPath)) { 21 | New-Item -Path $backupPath -Force | Out-Null 22 | } 23 | foreach ($key in $registryKeys.Keys) { 24 | $currentValue = Get-ItemProperty -Path "HKLM:\SOFTWARE\Dell\UpdateService\Clients\CommandUpdate\Preferences\CFG" -Name $key -ErrorAction SilentlyContinue 25 | if ($currentValue) { 26 | Set-ItemProperty -Path $backupPath -Name $key -Value $currentValue.$key -Type DWord -Force 27 | } 28 | } 29 | # Update registry value(s) set up to handle more than 1 30 | foreach ($key in $registryKeys.Keys) { 31 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Dell\UpdateService\Clients\CommandUpdate\Preferences\CFG" -Name $key -Value $registryKeys[$key] -Type DWord -Force 32 | } 33 | # Configure settings and output to terminal 34 | $process = Start-Process $DcuCliPath -ArgumentList '/configure -lockSettings=enable -updatesNotification=disable -scheduleManual -userConsent=disable -silent -autoSuspendBitlocker=enable' -NoNewWindow -Wait -PassThru 35 | if ($process.ExitCode -eq 0) { 36 | Write-Output "Dell Command Update has been configured, the following settings have been applied:`n 37 | - settings lock = enabled`n 38 | - update notifications = disabled`n 39 | - automatic updates = disabled`n 40 | - user consent = disabled`n 41 | - automatically suspend bitlocker = enabled 42 | " 43 | #set custom fields 44 | Ninja-Property-Set dcuInstallStatus 'YES: configured' 45 | Ninja-Property-Set dellCommandUpdateInstalled 'YES: configured' 46 | } else { 47 | Write-Output "Error configuring Dell Command Update. Exit code: $($process.ExitCode)" 48 | } 49 | } 50 | catch { 51 | Write-Output "An error occurred: $_" 52 | } 53 | } else { 54 | Write-Output "DCU CLI not found. Please check if Dell Command Update is installed." 55 | } -------------------------------------------------------------------------------- /2_config/2.2_generate_settings_xml.ps1: -------------------------------------------------------------------------------- 1 | # Specify possible paths where dcu-cli.exe might be located 2 | $PossibleDcuCliPaths = @( 3 | "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe", 4 | "C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" 5 | ) 6 | 7 | # Find dcu-cli.exe 8 | $DcuCliPath = $PossibleDcuCliPaths | Where-Object { Test-Path $_ -PathType Leaf } | Select-Object -First 1 9 | 10 | if ($DcuCliPath) { 11 | try { 12 | #create timestamp 13 | $now = Get-Date -Format 'yyyy-MM-dd_HH-mm' 14 | # Check for the log storage location from Ninja 15 | $NinjaExportPath = Ninja-Property-Get dcuLogLocation 16 | if (-not (Test-Path -Path $NinjaExportPath)) { 17 | # If Ninja log location doesn't exist, use default directory 18 | $ExportDir = "C:\Users\Nolan\Documents\Code\Dell Command Update\logs_and_exports" 19 | if (-not (Test-Path -Path $ExportDir)) { 20 | New-Item -Path $ExportDir -ItemType Directory | Out-Null 21 | } 22 | #use personal location + timestamp 23 | $ExportPath = Join-Path $ExportDir "settings_export__1.$now" 24 | } else { 25 | # Use the Ninja log location + timestamp 26 | $ExportPath = Join-Path $NinjaExportPath "settings_export__1.$now" 27 | } 28 | 29 | # Construct the arguments string 30 | $arguments = "/configure -exportSettings=`"$ExportPath`"" 31 | 32 | # Use Start-Process to run the command 33 | Write-Output "Running DCU CLI..." 34 | Start-Process -FilePath $DcuCliPath -ArgumentList $arguments -NoNewWindow -Wait 35 | 36 | Write-Output "Settings exported to: $ExportPath" 37 | } 38 | catch { 39 | Write-Output "Error: $_" -ForegroundColor Red 40 | } 41 | } else { 42 | Write-Output "DCU CLI not found. Ensure Dell Command Update is installed." -ForegroundColor Yellow 43 | } 44 | -------------------------------------------------------------------------------- /2_config/2.4_dcu_lockSettings.ps1: -------------------------------------------------------------------------------- 1 | # Specify possible paths where dcu-cli.exe might be located 2 | $PossibleDcuCliPaths = @( 3 | "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe", 4 | "C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" 5 | ) 6 | 7 | # Find dcu-cli.exe 8 | $DcuCliPath = $PossibleDcuCliPaths | Where-Object { Test-Path $_ -PathType Leaf } | Select-Object -First 1 9 | 10 | if ($DcuCliPath) { 11 | try { 12 | #setting should be either enable or disable 13 | 14 | # Change the setting 15 | $arguments = "/configure -lockSettings=enable" 16 | Write-Output "Executing: $DcuCliPath $arguments" 17 | Start-Process $DcuCliPath -ArgumentList $arguments -NoNewWindow -Wait 18 | 19 | Write-Output "Command executed. Please check Dell Command Update to verify the changes." 20 | } 21 | catch { 22 | Write-Output "An error occurred: $_" -ForegroundColor Red 23 | } 24 | } else { 25 | Write-Output "DCU CLI not found. Please check if Dell Command Update is installed." -ForegroundColor Yellow 26 | } -------------------------------------------------------------------------------- /3_scans/3_dcu_scan_all.ps1: -------------------------------------------------------------------------------- 1 | # Specify possible paths where dcu-cli.exe might be located 2 | $PossibleDcuCliPaths = @( 3 | "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe", 4 | "C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" 5 | ) 6 | 7 | # Initialize a variable to store the actual path of dcu-cli.exe 8 | $DcuCliPath = $null 9 | 10 | # Iterate through each possible path to check if dcu-cli.exe exists 11 | foreach ($path in $PossibleDcuCliPaths) { 12 | if (Test-Path $path -PathType Leaf) { 13 | # If dcu-cli.exe is found, set the path and break the loop 14 | $DcuCliPath = $path 15 | break 16 | } 17 | } 18 | 19 | # Check if dcu-cli.exe was found 20 | if ($DcuCliPath) { 21 | Write-Host "Dell Command Update CLI found at $DcuCliPath. Proceeding with operations..." 22 | 23 | try { 24 | # Start dcu-cli.exe to check if it runs properly 25 | $DcuCliVersionResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/version" -Wait -NoNewWindow -PassThru 26 | if ($DcuCliVersionResult.ExitCode -eq 0) { 27 | Write-Host "`nDell Command Update CLI is running properly." 28 | 29 | $now = Get-Date 30 | $formattedDateTime = $now.ToString("MM/dd/yyyy [HH:mm]") 31 | 32 | # Define a flag to track whether a reboot is needed 33 | $rebootNeeded = $false 34 | $rebootTypes = @() 35 | 36 | # Perform update scans 37 | $updateTypes = @("bios", "firmware", "driver", "application") 38 | foreach ($updateType in $updateTypes) { 39 | Write-Host "Checking for $updateType updates..." 40 | $scanResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan -updateType=$updateType" -NoNewWindow -PassThru -Wait -ErrorAction Stop 41 | # $updateResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan -updateType=$updateType" -NoNewWindow -PassThru -Wait -ErrorAction SilentlyContinue 42 | #confirm scan vs apply update behavior 43 | 44 | # Check for specific exit codes 45 | switch ($updateResult.ExitCode) { 46 | 0 { 47 | Write-Host "$updateType updates applied successfully." 48 | } 49 | 1 { 50 | Write-Host "Reboot required for $updateType updates." 51 | $rebootNeeded = $true 52 | $rebootTypes += $updateType 53 | } 54 | 2 { 55 | Write-Host "Unknown error occurred during $updateType updates." 56 | } 57 | 4 { 58 | Write-Host "Administrative privileges are required to apply $updateType updates." 59 | } 60 | 500..503 { 61 | Write-Host "Error during scanning for $updateType updates. Exit Code: $($updateResult.ExitCode)" 62 | } 63 | 1000..1002 { 64 | Write-Host "Error while applying $updateType updates. Exit Code: $($updateResult.ExitCode)" 65 | } 66 | default { 67 | Write-Host "Unhandled exit code $($updateResult.ExitCode) during $updateType updates." 68 | } 69 | } 70 | } 71 | 72 | # Set Ninja custom field based on reboot status 73 | if ($rebootNeeded) { 74 | $rebootTypeList = $rebootTypes -join ", " 75 | Ninja-Property-Set dcuRebootNeeded "Yes reboot needed, for - $rebootTypeList" 76 | Ninja-Property-Set mostRecentDcuScan "Updates available as of - $formattedDateTime" --stdin 77 | } else { 78 | Ninja-Property-Set dcuRebootNeeded "No reboot needed, updates applied successfully" 79 | Ninja-Property-Set dcuScanLog "No updates as of - $formattedDateTime" 80 | Ninja-Property-Set mostRecentDcuScan "Updates Applied - $formattedDateTime" --stdin 81 | } 82 | 83 | Write-Host "Scans and updates completed successfully." 84 | } else { 85 | Write-Host "Error: Dell Command Update CLI failed to run properly." 86 | } 87 | } catch { 88 | Write-Host "Error: $_" 89 | } 90 | } else { 91 | Write-Host "Error: Dell Command Update CLI (dcu-cli.exe) not found in the expected paths." 92 | } 93 | -------------------------------------------------------------------------------- /4_apply_updates/4.1_dcu_update_no_reboot.ps1: -------------------------------------------------------------------------------- 1 | # Specify possible paths where dcu-cli.exe might be located 2 | $PossibleDcuCliPaths = @( 3 | "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe", 4 | "C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" 5 | ) 6 | 7 | # Initialize a variable to store the actual path of dcu-cli.exe 8 | $DcuCliPath = $null 9 | 10 | # Iterate through each possible path to check if dcu-cli.exe exists 11 | foreach ($path in $PossibleDcuCliPaths) { 12 | if (Test-Path $path -PathType Leaf) { 13 | # If dcu-cli.exe is found, set the path and break the loop 14 | $DcuCliPath = $path 15 | break 16 | } 17 | } 18 | 19 | # Check if dcu-cli.exe was found 20 | if ($DcuCliPath) { 21 | # Display a message indicating the detection of Dell Command Update CLI 22 | Write-Output "Dell Command Update CLI found at $DcuCliPath. Proceeding with operations..." 23 | try { 24 | # Start dcu-cli.exe to check if it runs properly 25 | Start-Process -FilePath $DcuCliPath -ArgumentList "/version" -Wait -NoNewWindow -ErrorAction Stop 26 | # Display a message indicating that Dell Command Update CLI is running properly 27 | Write-Output "`nDell Command Update CLI is running properly." -ForegroundColor Green 28 | 29 | $now = Get-Date 30 | $formattedDateTime = $now.ToString("MM/dd/yyyy [HH:mm:ss]") 31 | 32 | # Define a flag to track whether a reboot is needed 33 | $rebootNeeded = $false 34 | $rebootTypes = @() 35 | 36 | # Function to check if exit code indicates a reboot is needed 37 | function CheckForReboot($exitCode) { 38 | return ($null -ne $exitCode -and ($exitCode -eq 1 -or $exitCode -eq 5)) 39 | } 40 | 41 | # Check for BIOS updates and apply 42 | $BiosUpdateResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan -updateType=bios" -NoNewWindow -PassThru -Wait -ErrorAction Stop 43 | # Attempt to apply updates with reboot disabled 44 | $BiosApplyResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/applyUpdates -updateType=bios -reboot=disable" -NoNewWindow -PassThru -Wait -ErrorAction SilentlyContinue 45 | # Check exit code for reboot indication 46 | if (CheckForReboot $BiosApplyResult.ExitCode) { 47 | $rebootNeeded = $true 48 | $rebootTypes += "bios" 49 | } 50 | 51 | # Check for firmware updates and apply 52 | $FirmwareUpdateResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan -updateType=firmware" -NoNewWindow -PassThru -Wait -ErrorAction Stop 53 | # Attempt to apply updates with reboot disabled 54 | $FirmwareApplyResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/applyUpdates -updateType=firmware -reboot=disable" -NoNewWindow -PassThru -Wait -ErrorAction SilentlyContinue 55 | # Check exit code for reboot indication 56 | if (CheckForReboot $FirmwareApplyResult.ExitCode) { 57 | $rebootNeeded = $true 58 | $rebootTypes += "firmware" 59 | } 60 | 61 | # Check for driver updates and apply 62 | $DriverUpdateResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan -updateType=driver" -NoNewWindow -PassThru -Wait -ErrorAction Stop 63 | # Attempt to apply updates with reboot disabled 64 | $DriverApplyResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/applyUpdates -updateType=driver -reboot=disable" -NoNewWindow -PassThru -Wait -ErrorAction SilentlyContinue 65 | # Check exit code for reboot indication 66 | if (CheckForReboot $DriverApplyResult.ExitCode) { 67 | $rebootNeeded = $true 68 | $rebootTypes += "drivers" 69 | } 70 | 71 | # Check for application updates and apply 72 | $AppUpdateResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan -updateType=application" -NoNewWindow -PassThru -Wait -ErrorAction Stop 73 | # Attempt to apply updates with reboot disabled 74 | $AppApplyResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/applyUpdates -updateType=application -reboot=disable" -NoNewWindow -PassThru -Wait -ErrorAction SilentlyContinue 75 | # Check exit code for reboot indication 76 | if (CheckForReboot $AppApplyResult.ExitCode) { 77 | $rebootNeeded = $true 78 | $rebootTypes += "application" 79 | } 80 | 81 | # Set Ninja custom field if a reboot is needed 82 | if ($rebootNeeded) { 83 | # Join the reboot types into a comma-separated list 84 | $rebootTypeList = $rebootTypes -join ", " 85 | Ninja-Property-Set dcuRebootNeeded "Yes reboot needed, for - $rebootTypeList" 86 | Ninja-Property-Set mostRecentDcuScan "Updates available as of - $formattedDateTime" --stdin 87 | } else { 88 | Ninja-Property-Set dcuRebootNeeded "No reboot needed, updates applied successfully" 89 | Ninja-Property-Set dcuScanLog "No updates as of - $formattedDateTime" 90 | Ninja-Property-Set mostRecentDcuScan "Updates Applied - $formattedDateTime" --stdin 91 | } 92 | 93 | Write-Output "Scans and updates completed successfully." -ForegroundColor Cyan 94 | } catch { 95 | # Display an error message if an exception occurs during the process 96 | Write-Output "Error: $_" -ForegroundColor Red 97 | } 98 | } else { 99 | # Display an error message if Dell Command Update CLI is not found 100 | Write-Output "Error: Dell Command Update CLI (dcu-cli.exe) not found in the expected paths." 101 | } 102 | -------------------------------------------------------------------------------- /4_apply_updates/4.2_dcu_update_reboot.ps1: -------------------------------------------------------------------------------- 1 | # Specify possible paths where dcu-cli.exe might be located 2 | $PossibleDcuCliPaths = @( 3 | "C:\Program Files\Dell\CommandUpdate\dcu-cli.exe", 4 | "C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe" 5 | ) 6 | 7 | # Initialize a variable to store the actual path of dcu-cli.exe 8 | $DcuCliPath = $null 9 | 10 | # Iterate through each possible path to check if dcu-cli.exe exists 11 | foreach ($path in $PossibleDcuCliPaths) { 12 | if (Test-Path $path -PathType Leaf) { 13 | # If dcu-cli.exe is found, set the path and break the loop 14 | $DcuCliPath = $path 15 | break 16 | } 17 | } 18 | 19 | # Check if dcu-cli.exe was found 20 | if ($DcuCliPath) { 21 | # Display a message indicating the detection of Dell Command Update CLI 22 | Write-Host "Dell Command Update CLI found at $DcuCliPath. Proceeding with operations..." 23 | 24 | try { 25 | # Start dcu-cli.exe to check if it runs properly 26 | $DcuCliVersionResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/version" -Wait -NoNewWindow -PassThru 27 | if ($DcuCliVersionResult.ExitCode -eq 0) { 28 | Write-Host "`nDell Command Update CLI is running properly." 29 | 30 | $now = Get-Date 31 | $formattedDateTime = $now.ToString("MM/dd/yyyy [HH:mm:ss]") 32 | 33 | # Define a flag to track whether a reboot is needed 34 | $rebootNeeded = $false 35 | $rebootTypes = @() 36 | 37 | # Check for all updates 38 | $ScanResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/scan" -NoNewWindow -PassThru -Wait -ErrorAction Stop 39 | 40 | # Check exit code for successful scan operation 41 | switch ($ScanResult.ExitCode) { 42 | 0 { 43 | Write-Host "Scan operation completed successfully." 44 | } 45 | 500 { 46 | Write-Host "No updates were found for the system. The system is up to date." 47 | Ninja-Property-Set dcuScanLog "No updates found for the system as of - $formattedDateTime" 48 | Ninja-Property-Set mostRecentDcuScan "No updates found - $formattedDateTime" --stdin 49 | Ninja-Property-Set dcuRebootStatus "No Reboot needed. All applicable updates applied successfully" 50 | Exit 51 | } 52 | default { 53 | Write-Host "Error: Failed to perform scan operation. Exit code: $($ScanResult.ExitCode)" 54 | Exit 55 | } 56 | } 57 | 58 | # Apply updates 59 | $ApplyUpdatesResult = Start-Process -FilePath $DcuCliPath -ArgumentList "/applyUpdates -reboot=enable -autoSuspendBitlocker=enable" -NoNewWindow -PassThru -Wait -ErrorAction Stop 60 | 61 | # Check exit code for successful update application 62 | switch ($ApplyUpdatesResult.ExitCode) { 63 | 0 { 64 | # Set Ninja custom fields 65 | Ninja-Property-Set dcuScanLog "Updates applied successfully as of - $formattedDateTime" 66 | Ninja-Property-Set mostRecentDcuScan $formattedDateTime --stdin 67 | Ninja-Property-Set dcuRebootStatus "No reboot needed" 68 | 69 | Write-Host "Updates applied successfully. No reboot needed." 70 | } 71 | 1 { 72 | # Reboot required from the execution of an operation or pending from a previous operation 73 | Write-Host "A reboot is needed... Rebooting now." 74 | Ninja-Property-Set dcuRebootStatus "Reboot needed" 75 | Ninja-Property-Set dcuScanLog "Updated and rebooted successfully as of - $formattedDateTime" 76 | Write-Host "Updates applied successfully. Reboot is needed. Rebooting system..." 77 | Write-Host "Rebooting..." 78 | Restart-Computer -Force 79 | } 80 | 5 { 81 | # Reboot required from the execution of an operation or pending from a previous operation 82 | Write-Host "A reboot is needed... Rebooting now." 83 | Ninja-Property-Set dcuRebootStatus "Reboot needed" 84 | Ninja-Property-Set dcuScanLog "Updated and rebooted successfully as of - $formattedDateTime" 85 | Write-Host "Updates applied successfully. Reboot is needed. Rebooting system..." 86 | Write-Host "Rebooting..." 87 | Restart-Computer -Force 88 | } 89 | 1001 { 90 | Write-Host "Error: The apply updates operation was canceled." 91 | } 92 | 1002 { 93 | Write-Host "Error: An error occurred while downloading a file during the apply updates operation. Check your network connection and retry the command." 94 | } 95 | default { 96 | Write-Host "Error: Failed to apply updates. Exit code: $($ApplyUpdatesResult.ExitCode)" 97 | } 98 | } 99 | } else { 100 | Write-Host "Error: Dell Command Update CLI failed to run properly." 101 | } 102 | } catch { 103 | # Display an error message if an exception occurs during the process 104 | Write-Host "Error: $_" 105 | } 106 | } else { 107 | # Display an error message if Dell Command Update CLI is not found 108 | Write-Host "Error: Dell Command Update CLI (dcu-cli.exe) not found in the expected paths." 109 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Dell Command Update Automation Project 2 | # This readme is out of date and needs to be updated 3 | ## Overview 4 | 5 | This repository contains scripts and configurations for automating the deployment, configuration, and scanning processes using Dell Command | Update (DCU). The project aims to streamline the management of Dell devices within an MSP (Managed Service Provider) environment. For best results scripts should be run as administrator. The scripts also assume you are using the NinjaOneRMM platform, but can be easily modified to ignore that. 6 | 7 | ## Project Structure 8 | 9 | ### Part 1: Downloading, Installing, and Configuring Dell Command | Update 10 | 11 | #### Folder 1: `1_install_dcu` 12 | 13 | - `1_dcu_install_url.ps1`: This script has been lightly tested. It first checks to see if DCU is already installed and then uninstalls it if it is detected. The script uses the download URL of the Command Update installer for the most recent version of Dell Command Update as of 5/24/2024 14 | - `1.1_dcu_install_s3.ps1`: This script has not been tested, however it is nearly identical in functionality to the first and tested functional installation script. The script is configured to take a link to an installer stored in an AWS S3 bucket instead of pulling from the Dell website 15 | - `1.2_dcu_install_winget.ps1`: This script has been tested and is currently non-operational. There is an existing winget command to install command update but for an unknown reason it is very tricky to get working 16 | 17 | ### Part 2 Configuring Dell Command | Update 18 | 19 | #### Folder 2: `2_config` 20 | 21 | - `2_dcu_configure.ps1`: Configures DCU CLI settings. It disables certain notifications, reboots, and user consents. * 22 | 23 | #### File 2.5: BIOS Password Management 24 | 25 | - `2.5_dcu_admin_bios.ps1`: This script handles BIOS passwords and related configurations. Please note that this part of the script was salvaged from an online source. **Needs review and testing.** 26 | 27 | ### File 2.1: create logs storage folder 28 | 29 | - `2.1_create_logs_storage_folder.ps1`: this takes in a path as a parameter and creates a storage folder at the specified location. This is where all of the logs get stored after scanning 30 | 31 | ### Folder 3 `3_scans` 32 | 33 | #### File 3: Manual Scan Script 34 | 35 | - `3_dcu_scan_all.ps1`: Runs a scan for all possible update types using the CLI. Stores the output to the log storage folder --> path taken from Ninja custom fields. Additionally it is set up to analyze the response code and determine if an update is needed on the device. 36 | 37 | ### Folder 4: `4_apply_updates` 38 | 39 | ### File 4.1: Update All 40 | 41 | - `4.1_dcu_update_no_reboot.ps1`: Runs a scan and then update of all update types that do not require the comptuer to reboot in order to apply. Saves logs to storage folder. 42 | 43 | ### File 4.2: Update BIOS 44 | 45 | - `4.2_dcu_update_reboot.ps1`: scans for and applys updates to all update types including those that require a reboot to apply. forcers a reboot after 46 | --------------------------------------------------------------------------------