Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/lib/dell-bios-translation.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# oem-dell/lib/dell-bios-translation.ps1
#
# Dell BIOS settings translation table. Maps the canonical $env:BIOS_* variable
# names defined in CLAUDE.md to the Dell Command Configure (cctk.exe) argument
# syntax. Inlined into dell-configure.ps1 at CI build time.
#
# Provides:
# $script:DellBiosSettingsMap hashtable: canonical name -> cctk descriptor.
# Get-DellBiosSetting returns the descriptor for a canonical name.
#
# Descriptor shape:
# @{
# Native = '<cctk subcommand or option name>'
# Values = @{ <CanonicalValue> = '<cctk value>' ; ... }
# Form = 'KeyValue' | 'Subcommand'
# }
#
# Form='KeyValue' emits --<Native>=<value> (e.g. --tpm=on)
# Form='Subcommand' emits <Native> --<paramName>=<v> for things that need their
# own cctk subcommand line (e.g. bootorder --activebootlist=uefi).
#
# Password handling for $env:BIOS_AdminPassword / $env:BIOS_AdminPasswordNew is
# not in this table; dell-configure.ps1 emits the cctk password flags directly
# because the password mechanic is its own special case (current pw + new pw +
# clear pw all map to different cctk syntax).
#
# Settings that Dell does not support (or that we have not validated yet) are
# intentionally absent. dell-configure.ps1 logs and skips unknown canonical
# names so operators can set forward-looking vars without breaking the run.

$script:DellBiosSettingsMap = @{
BIOS_TPMEnabled = @{
Native = 'tpm'
Values = @{ Enabled = 'on'; Disabled = 'off' }
Form = 'KeyValue'
}
BIOS_TPMActivation = @{
Native = 'tpmactivation'
Values = @{ Activated = 'activate'; Deactivated = 'deactivate' }
Form = 'KeyValue'
}
BIOS_SecureBoot = @{
Native = 'secureboot'
Values = @{ Enabled = 'enabled'; Disabled = 'disabled' }
Form = 'KeyValue'
}
BIOS_VirtualizationCPU = @{
Native = 'virtualization'
Values = @{ Enabled = 'enable'; Disabled = 'disable' }
Form = 'KeyValue'
}
BIOS_VirtualizationIOMMU = @{
Native = 'vtfordirectio'
Values = @{ Enabled = 'enable'; Disabled = 'disable' }
Form = 'KeyValue'
}
BIOS_WakeOnLAN = @{
Native = 'wakeonlan'
Values = @{
Enabled = 'lan'
Disabled = 'disable'
LANOnly = 'lan'
LANWLAN = 'lanwlan'
}
Form = 'KeyValue'
}
BIOS_BootMode = @{
Native = 'bootorder'
Values = @{ UEFI = 'uefi'; Legacy = 'legacy' }
Form = 'Subcommand'
Param = 'activebootlist'
}
}

function Get-DellBiosSetting {
param([Parameter(Mandatory)] [string] $CanonicalName)
if ($script:DellBiosSettingsMap.ContainsKey($CanonicalName)) {
return $script:DellBiosSettingsMap[$CanonicalName]
}
return $null
}
43 changes: 43 additions & 0 deletions src/lib/dell-detection.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# oem-dell/lib/dell-detection.ps1
#
# Dell-specific detection helpers. Inlined into Dell leaf scripts at CI build
# time via a `# %INCLUDE` marker. No runtime fetch, no hash check.
#
# Provides:
# Test-DellHardware True if this machine is Dell.
# Get-DellInstalledModel Returns Win32_ComputerSystem.Model or $null.
# Test-DCUInstalled True if dcu-cli.exe is present at the canonical path.
# Get-DCUVersion Returns [version] of installed DCU, or $null.
# Get-DCUCliPath Returns the canonical dcu-cli.exe path.

$script:dcuCliPath = "$env:ProgramFiles\Dell\CommandUpdate\dcu-cli.exe"

function Test-DellHardware {
$manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue).Manufacturer
return ($manufacturer -like "Dell*")
}

function Get-DellInstalledModel {
return (Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue).Model
}

function Test-DCUInstalled {
return (Test-Path -Path $script:dcuCliPath)
}

function Get-DCUVersion {
if (-not (Test-DCUInstalled)) { return $null }
try {
$info = (Get-Item -Path $script:dcuCliPath -ErrorAction Stop).VersionInfo
if ($info.ProductVersion) {
return [version]$info.ProductVersion
}
return $null
} catch {
return $null
}
}

function Get-DCUCliPath {
return $script:dcuCliPath
}
21 changes: 21 additions & 0 deletions src/lib/oem-manufacturer-detect.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# oem-shared/lib/oem-manufacturer-detect.ps1
#
# Shared OEM manufacturer detection. Inlined into every OEM leaf script at
# CI build time via a `# %INCLUDE` marker. No runtime fetch, no hash check.
#
# Provides:
# Get-OEMManufacturer Returns "Dell" / "HP" / "Lenovo" / "Unknown".

function Get-OEMManufacturer {
$manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue).Manufacturer
if ([string]::IsNullOrEmpty($manufacturer)) {
return "Unknown"
}
switch -Wildcard ($manufacturer) {
"Dell*" { return "Dell" }
"HP*" { return "HP" }
"Hewlett*" { return "HP" }
"Lenovo*" { return "Lenovo" }
default { return "Unknown" }
}
}
135 changes: 135 additions & 0 deletions src/oem-dell/dell-command-update-install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
## PLEASE COMMENT YOUR VARIABLES DIRECTLY BELOW HERE IF YOU'RE RUNNING FROM A RMM
## NinjaRMM passes script preset variables as environment variables, so each is read via $env: in this script.
## $env:RMM - Set to "1" by NinjaRMM to indicate RMM (non-interactive) mode
## $env:Description - Ticket # or initials for audit trail
## $env:RMMScriptPath - Optional log directory base provided by the RMM
##
## Per-script variables:
## $env:DCUTargetVersion - Minimum acceptable DCU version (default: "5.7.0")
## $env:DCUInstallerURL - Override the Dell-hosted installer URL (see TODO below)

# %INCLUDE src/lib/oem-manufacturer-detect.ps1
# %INCLUDE src/lib/dell-detection.ps1

$ScriptLogName = "dell-command-update-install.log"

# --- Default optional RMM environment variables --------------------------

if ([string]::IsNullOrEmpty($env:DCUTargetVersion)) { $env:DCUTargetVersion = "5.7.0" }

# TODO: Confirm the canonical Dell-hosted EXE URL for DCU 5.7 Classic from
# https://www.dell.com/support/kbdoc/en-us/000177325/dell-command-update and
# pin it here. The placeholder below points at Dell's published download path
# pattern but the specific folder ID needs to be captured from a fresh
# manual download against the published KB so we know we're tracking the
# Dell-supported build.
if ([string]::IsNullOrEmpty($env:DCUInstallerURL)) {
$env:DCUInstallerURL = "https://dl.dell.com/FOLDER/Dell-Command-Update-Application_WIN_5.7.0_A00.EXE"
}

# --- Input handling: RMM vs interactive ----------------------------------

if ($env:RMM -ne "1") {
$ValidInput = 0
while ($ValidInput -ne 1) {
$env:Description = Read-Host "Please enter the ticket # and/or your initials for audit trail"
if ($env:Description) {
$ValidInput = 1
} else {
Write-Host "Invalid input. Please try again."
}
}
$LogPath = "$env:WINDIR\logs\$ScriptLogName"
} else {
if (-not [string]::IsNullOrEmpty($env:RMMScriptPath)) {
$LogPath = "$env:RMMScriptPath\logs\$ScriptLogName"
} else {
$LogPath = "$env:WINDIR\logs\$ScriptLogName"
}
if ([string]::IsNullOrEmpty($env:Description)) {
Write-Host "Description is null. This was most likely run automatically from the RMM and no information was passed."
$env:Description = "Dell Command Update Install"
}
}

$logDir = Split-Path -Path $LogPath -Parent
if (-not (Test-Path -Path $logDir)) {
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
}

# --- Script logic --------------------------------------------------------

$TranscriptStarted = $false
try {
Start-Transcript -Path $LogPath -ErrorAction Stop
$TranscriptStarted = $true
} catch {
Write-Host "Warning: Could not start transcript logging to $LogPath - $($_.Exception.Message)"
}

Write-Host "Description: $env:Description"
Write-Host "Log path: $LogPath"
Write-Host "RMM: $env:RMM"

if (-not (Test-DellHardware)) {
Write-Host "Not a Dell endpoint. Exiting."
if ($TranscriptStarted) { Stop-Transcript }
exit 0
}

# Internet check ... only the install step needs vendor download connectivity.
# dell-command-update-run / dell-configure / dell-debloat do NOT include this
# because they operate on already-installed tooling and should still work on
# an endpoint that's offline at the moment.
function Test-InternetAvailable {
try {
$resp = Invoke-WebRequest -Uri 'https://www.msftconnecttest.com/connecttest.txt' `
-UseBasicParsing -TimeoutSec 5 -ErrorAction Stop
return ($resp.StatusCode -eq 200)
} catch {
return $false
}
}

if (-not (Test-InternetAvailable)) {
Write-Host "No internet connectivity detected ... skipping DCU install. If DCU is already installed, dell-command-update-run will still work. Exit 0."
if ($TranscriptStarted) { Stop-Transcript }
exit 0
}

$targetVersion = [version]$env:DCUTargetVersion
$installedVersion = Get-DCUVersion

if ($null -ne $installedVersion -and $installedVersion -ge $targetVersion) {
Write-Host "DCU $installedVersion already meets target $targetVersion. Skipping install."
if ($TranscriptStarted) { Stop-Transcript }
exit 0
}

Write-Host "DCU installed: $installedVersion. Target: $targetVersion. Installing."

$installerPath = Join-Path $env:TEMP "dell-command-update-installer.exe"
try {
Invoke-WebRequest -Uri $env:DCUInstallerURL -OutFile $installerPath -UseBasicParsing -ErrorAction Stop
} catch {
Write-Host "Failed to download DCU installer from $env:DCUInstallerURL : $_"
if ($TranscriptStarted) { Stop-Transcript }
exit 1
}

Write-Host "Running silent install: $installerPath /s"
$proc = Start-Process -FilePath $installerPath -ArgumentList "/s" -Wait -PassThru -NoNewWindow
Write-Host "Installer exit code: $($proc.ExitCode)"

Remove-Item -Path $installerPath -Force -ErrorAction SilentlyContinue

$postVersion = Get-DCUVersion
if ($null -ne $postVersion -and $postVersion -ge $targetVersion) {
Write-Host "DCU $postVersion installed successfully."
if ($TranscriptStarted) { Stop-Transcript }
exit 0
} else {
Write-Host "DCU install verification failed. Installed version: $postVersion"
if ($TranscriptStarted) { Stop-Transcript }
exit 1
}
Loading