Enhances Dell driver retrieval by resolving missing CabUrl through CatalogIndexPC. Introduces Resolve-DellCabUrlFromModel function for improved reliability. Updates Save-DriversJson and Import-DriversJson functions to handle additional properties (SystemId, CabUrl, CabRelativePath) for Dell models. Refines Get-DellDrivers function to ensure consistent model handling and logging.

This commit is contained in:
rbalsleyMSFT
2025-10-24 15:19:56 -07:00
parent 7dd002396f
commit 4ce9183bd3
4 changed files with 143 additions and 76 deletions
+14 -5
View File
@@ -1432,7 +1432,7 @@ function Get-DellDrivers {
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[string]$Model, [string]$Model,
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[ValidateSet('x64','x86','ARM64')] [ValidateSet('x64', 'x86', 'ARM64')]
[string]$WindowsArch, [string]$WindowsArch,
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[int]$WindowsRelease [int]$WindowsRelease
@@ -1454,9 +1454,18 @@ function Get-DellDrivers {
if (-not $target) { throw "Requested Dell model '$Model' not found in index." } if (-not $target) { throw "Requested Dell model '$Model' not found in index." }
$cabUrl = $target.CabUrl $cabUrl = $target.CabUrl
if ([string]::IsNullOrWhiteSpace($cabUrl)) {
WriteLog "CabUrl missing for '$($target.Model)'; resolving via CatalogIndexPC."
$resolved = Resolve-DellCabUrlFromModel -DriversFolder $DriversFolder -ModelDisplay $target.Model
if ($null -eq $resolved -or [string]::IsNullOrWhiteSpace($resolved.CabUrl)) {
throw "Unable to resolve CabUrl for $($target.Model) from CatalogIndexPC."
}
$cabUrl = $resolved.CabUrl
$target.CabUrl = $cabUrl
}
$modelCabName = [IO.Path]::GetFileName($cabUrl) $modelCabName = [IO.Path]::GetFileName($cabUrl)
$modelCabPath = Join-Path $DriversFolder $modelCabName $modelCabPath = Join-Path $DriversFolder $modelCabName
$modelXmlPath = Join-Path $DriversFolder ($modelCabName -replace '\.cab$','.xml') $modelXmlPath = Join-Path $DriversFolder ($modelCabName -replace '\.cab$', '.xml')
if (Test-Path $modelCabPath) { Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue } if (Test-Path $modelCabPath) { Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue }
if (Test-Path $modelXmlPath) { Remove-Item $modelXmlPath -Force -ErrorAction SilentlyContinue } if (Test-Path $modelXmlPath) { Remove-Item $modelXmlPath -Force -ErrorAction SilentlyContinue }
@@ -1473,7 +1482,7 @@ function Get-DellDrivers {
} }
foreach ($pkg in $pkgs) { foreach ($pkg in $pkgs) {
$categorySafe = ($pkg.Category -replace '[\\\/\:\*\?\"\<\>\| ]','_') $categorySafe = ($pkg.Category -replace '[\\\/\:\*\?\"\<\>\| ]', '_')
$downloadFolder = Join-Path $DriversFolder (Join-Path $Model $categorySafe) $downloadFolder = Join-Path $DriversFolder (Join-Path $Model $categorySafe)
if (-not (Test-Path $downloadFolder)) { New-Item -Path $downloadFolder -ItemType Directory -Force | Out-Null } if (-not (Test-Path $downloadFolder)) { New-Item -Path $downloadFolder -ItemType Directory -Force | Out-Null }
$driverFilePath = Join-Path $downloadFolder $pkg.DriverFileName $driverFilePath = Join-Path $downloadFolder $pkg.DriverFileName
@@ -1535,8 +1544,8 @@ function Get-DellDrivers {
$driverPath = $component.path $driverPath = $component.path
$downloadUrl = $baseLocation + $driverPath $downloadUrl = $baseLocation + $driverPath
$driverFileName = [System.IO.Path]::GetFileName($driverPath) $driverFileName = [System.IO.Path]::GetFileName($driverPath)
$name = $component.Name.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' -replace '[\,]','-' $name = $component.Name.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]', '_' -replace '[\,]', '-'
$category = $component.Category.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' $category = $component.Category.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]', '_'
$version = [version]$component.vendorVersion $version = [version]$component.vendorVersion
$namePrefix = ($name -split '-')[0] $namePrefix = ($name -split '-')[0]
if (-not $latestDrivers[$category]) { $latestDrivers[$category] = @{} } if (-not $latestDrivers[$category]) { $latestDrivers[$category] = @{} }
@@ -231,4 +231,50 @@ function Get-DellLatestDriverPackages {
return $chosen return $chosen
} }
Export-ModuleMember -Function Convert-DellVendorVersion,Compare-DellVendorVersion,Get-DellCatalogIndex,Get-DellClientModels,Get-DellLatestDriverPackages # Resolve a Dell permodel CabUrl when missing by inspecting CatalogIndexPC
function Resolve-DellCabUrlFromModel {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$DriversFolder,
[Parameter()][string]$ModelDisplay,
[Parameter()][string]$SystemId
)
if ([string]::IsNullOrWhiteSpace($SystemId) -and -not [string]::IsNullOrWhiteSpace($ModelDisplay)) {
# Try to parse the trailing (XXXX) token (SystemId)
if ($ModelDisplay -match '\(([0-9A-Fa-f]{4})\)\s*$') {
$SystemId = $matches[1].ToUpperInvariant()
}
}
if ([string]::IsNullOrWhiteSpace($SystemId)) {
WriteLog "Resolve-DellCabUrlFromModel: No SystemId could be determined from '$ModelDisplay'."
return $null
}
try {
$indexXml = Get-DellCatalogIndex -DriversFolder $DriversFolder
# Reuse existing model parsing to avoid duplicating streaming logic
$allModels = Get-DellClientModels -CatalogIndexXmlPath $indexXml
$match = $allModels | Where-Object { $_.SystemId -eq $SystemId } | Select-Object -First 1
if ($null -eq $match) {
WriteLog "Resolve-DellCabUrlFromModel: SystemId '$SystemId' not found in CatalogIndexPC.xml."
return $null
}
WriteLog "Resolve-DellCabUrlFromModel: Resolved CabUrl for '$($match.ModelDisplay)' -> $($match.CabUrl)"
return [pscustomobject]@{
Brand = $match.Brand
ModelNumber = $match.ModelNumber
SystemId = $match.SystemId
CabRelativePath = $match.CabRelativePath
CabUrl = $match.CabUrl
ModelDisplay = $match.ModelDisplay
}
}
catch {
WriteLog "Resolve-DellCabUrlFromModel: Failure resolving CabUrl for '$ModelDisplay' / SystemId '$SystemId' : $($_.Exception.Message)"
return $null
}
}
Export-ModuleMember -Function Convert-DellVendorVersion,Compare-DellVendorVersion,Get-DellCatalogIndex,Get-DellClientModels,Get-DellLatestDriverPackages,Resolve-DellCabUrlFromModel
@@ -182,75 +182,35 @@ function Save-DellDriversTask {
if ($WindowsRelease -le 11) { if ($WindowsRelease -le 11) {
$cabUrl = $DriverItemData.CabUrl $cabUrl = $DriverItemData.CabUrl
if ([string]::IsNullOrWhiteSpace($cabUrl)) { if ([string]::IsNullOrWhiteSpace($cabUrl)) {
WriteLog "CabUrl missing for '$modelDisplay' falling back to legacy CatalogPC parsing." WriteLog "CabUrl missing for '$modelDisplay' resolving via CatalogIndexPC."
# Fallback legacy client method $resolved = Resolve-DellCabUrlFromModel -DriversFolder $DriversFolder -ModelDisplay $modelDisplay
$catalogCab = Join-Path $makeDriversPath 'CatalogPC.cab' if ($null -eq $resolved -or [string]::IsNullOrWhiteSpace($resolved.CabUrl)) {
$catalogXml = Join-Path $makeDriversPath 'CatalogPC.xml' throw "Unable to resolve CabUrl for $modelDisplay from CatalogIndexPC."
$catalogUrl = 'http://downloads.dell.com/catalog/CatalogPC.cab'
$need = $true
if (Test-Path $catalogXml) {
if (((Get-Date) - (Get-Item $catalogXml).CreationTime).TotalDays -lt 7) { $need = $false }
} }
if ($need) { $cabUrl = $resolved.CabUrl
if (Test-Path $catalogCab) { Remove-SafeFolder $catalogCab } # Optionally persist back into the incoming object if property exists
if (Test-Path $catalogXml) { Remove-SafeFolder $catalogXml } if ($DriverItemData.PSObject.Properties['CabUrl']) {
Start-BitsTransferWithRetry -Source $catalogUrl -Destination $catalogCab $DriverItemData.CabUrl = $cabUrl
Invoke-Process -FilePath Expand.exe -ArgumentList """$catalogCab"" ""$catalogXml""" | Out-Null
Remove-Item $catalogCab -Force -ErrorAction SilentlyContinue
} }
if (-not (Test-Path $catalogXml)) { throw "Legacy fallback failed; missing $catalogXml" }
[xml]$xmlContent = Get-Content -Path $catalogXml -Raw
$baseLocation = "https://$($xmlContent.manifest.baseLocation)/"
$softwareComponents = $xmlContent.Manifest.SoftwareComponent | Where-Object { $_.ComponentType.value -eq 'DRVR' }
$latestDrivers = @{}
foreach ($component in $softwareComponents) {
$models = $component.SupportedSystems.Brand.Model
foreach ($m in $models) {
if ($m.Display.'#cdata-section' -eq $modelDisplay) {
$validOS = $component.SupportedOperatingSystems.OperatingSystem | Where-Object { $_.osArch -eq $WindowsArch }
if (-not $validOS) { continue }
$driverPath = $component.path
$downloadUrl = $baseLocation + $driverPath
$fileName = [IO.Path]::GetFileName($driverPath)
$name = $component.Name.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_' -replace '[\,]','-'
$category = $component.Category.Display.'#cdata-section' -replace '[\\\/\:\*\?\"\<\>\| ]','_'
$version = [version]$component.vendorVersion
$namePrefix = ($name -split '-')[0]
if (-not $latestDrivers[$category]) { $latestDrivers[$category] = @{} }
if (-not $latestDrivers[$category][$namePrefix] -or $latestDrivers[$category][$namePrefix].Version -lt $version) {
$latestDrivers[$category][$namePrefix] = [pscustomobject]@{
Name = $name
DownloadUrl = $downloadUrl
DriverFileName = $fileName
Version = $version
Category = $category
}
}
}
}
}
foreach ($cat in $latestDrivers.Keys) { foreach ($drv in $latestDrivers[$cat].Values) { $packages += $drv } }
} }
else {
# Normal new model-based workflow
$modelCabName = [IO.Path]::GetFileName($cabUrl)
if ([string]::IsNullOrWhiteSpace($modelCabName)) { throw "Derived model cab name empty for $modelDisplay" }
$modelCabPath = Join-Path $makeDriversPath $modelCabName
$modelXmlPath = Join-Path $makeDriversPath ([IO.Path]::GetFileNameWithoutExtension($modelCabName) + '.xml')
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Downloading catalog...' } # Model-based workflow (always used for client pathway now)
if (Test-Path $modelCabPath) { Remove-SafeFolder $modelCabPath } $modelCabName = [IO.Path]::GetFileName($cabUrl)
if (Test-Path $modelXmlPath) { Remove-SafeFolder $modelXmlPath } if ([string]::IsNullOrWhiteSpace($modelCabName)) { throw "Derived model cab name empty for $modelDisplay" }
$modelCabPath = Join-Path $makeDriversPath $modelCabName
$modelXmlPath = Join-Path $makeDriversPath ([IO.Path]::GetFileNameWithoutExtension($modelCabName) + '.xml')
Start-BitsTransferWithRetry -Source $cabUrl -Destination $modelCabPath if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Downloading catalog...' }
Invoke-Process -FilePath Expand.exe -ArgumentList """$modelCabPath"" ""$modelXmlPath""" | Out-Null if (Test-Path $modelCabPath) { Remove-SafeFolder $modelCabPath }
Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue if (Test-Path $modelXmlPath) { Remove-SafeFolder $modelXmlPath }
if (-not (Test-Path $modelXmlPath)) { throw "Model XML not found after extraction: $modelXmlPath" }
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Selecting latest drivers...' } Start-BitsTransferWithRetry -Source $cabUrl -Destination $modelCabPath
$packages = Get-DellLatestDriverPackages -ModelXmlPath $modelXmlPath -WindowsArch $WindowsArch -WindowsRelease $WindowsRelease Invoke-Process -FilePath Expand.exe -ArgumentList """$modelCabPath"" ""$modelXmlPath""" | Out-Null
} Remove-Item $modelCabPath -Force -ErrorAction SilentlyContinue
if (-not (Test-Path $modelXmlPath)) { throw "Model XML not found after extraction: $modelXmlPath" }
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelDisplay -Status 'Selecting latest drivers...' }
$packages = Get-DellLatestDriverPackages -ModelXmlPath $modelXmlPath -WindowsArch $WindowsArch -WindowsRelease $WindowsRelease
} }
else { else {
# Server legacy logic unchanged (kept as before) # Server legacy logic unchanged (kept as before)
@@ -225,6 +225,15 @@ function Save-DriversJson {
$modelObject = @{ $modelObject = @{
Name = $driverItem.Model Name = $driverItem.Model
} }
if ($driverItem.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($driverItem.SystemId)) {
$modelObject.SystemId = $driverItem.SystemId
}
if ($driverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabUrl)) {
$modelObject.CabUrl = $driverItem.CabUrl
}
if ($driverItem.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabRelativePath)) {
$modelObject.CabRelativePath = $driverItem.CabRelativePath
}
} }
'HP' { 'HP' {
$modelObject = @{ $modelObject = @{
@@ -352,13 +361,35 @@ function Import-DriversJson {
} }
if ($importedModelObject.PSObject.Properties['MachineType'] -and $existingModel.PSObject.Properties['MachineType'] -and $existingModel.MachineType -ne $importedModelObject.MachineType) { if ($importedModelObject.PSObject.Properties['MachineType'] -and $existingModel.PSObject.Properties['MachineType'] -and $existingModel.MachineType -ne $importedModelObject.MachineType) {
$existingModel.MachineType = $importedModelObject.MachineType $existingModel.MachineType = $importedModelObject.MachineType
$existingModel.Id = $importedModelObject.MachineType # Update Id as well $existingModel.Id = $importedModelObject.MachineType
$updateExistingLenovo = $true $updateExistingLenovo = $true
} }
if ($updateExistingLenovo) { if ($updateExistingLenovo) {
WriteLog "Import-DriversJson: Updated ProductName/MachineType/Id for existing Lenovo model '$($existingModel.Model)'." WriteLog "Import-DriversJson: Updated ProductName/MachineType/Id for existing Lenovo model '$($existingModel.Model)'."
} }
} }
elseif ($makeName -eq 'Dell') {
# Update Dell extended fields if provided
if ($importedModelObject.PSObject.Properties['SystemId'] -and $existingModel.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.SystemId)) {
if ($existingModel.SystemId -ne $importedModelObject.SystemId) {
$existingModel.SystemId = $importedModelObject.SystemId
WriteLog "Import-DriversJson: Updated SystemId for existing Dell model '$($existingModel.Model)'."
}
}
if ($importedModelObject.PSObject.Properties['CabUrl'] -and $existingModel.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabUrl)) {
if ($existingModel.CabUrl -ne $importedModelObject.CabUrl) {
$existingModel.CabUrl = $importedModelObject.CabUrl
WriteLog "Import-DriversJson: Updated CabUrl for existing Dell model '$($existingModel.Model)'."
}
}
if ($importedModelObject.PSObject.Properties['CabRelativePath'] -and $existingModel.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabRelativePath)) {
if ($existingModel.CabRelativePath -ne $importedModelObject.CabRelativePath) {
$existingModel.CabRelativePath = $importedModelObject.CabRelativePath
WriteLog "Import-DriversJson: Updated CabRelativePath for existing Dell model '$($existingModel.Model)'."
}
}
}
$existingModelsUpdated++ $existingModelsUpdated++
WriteLog "Import-DriversJson: Marked existing model '$($existingModel.Make) - $($existingModel.Model)' as imported." WriteLog "Import-DriversJson: Marked existing model '$($existingModel.Make) - $($existingModel.Model)' as imported."
} }
@@ -395,7 +426,7 @@ function Import-DriversJson {
$newDriverModel = [PSCustomObject]@{ $newDriverModel = [PSCustomObject]@{
IsSelected = $true IsSelected = $true
Make = $makeName Make = $makeName
Model = $importedModelNameFromObject # Full display name Model = $importedModelNameFromObject
Link = $importedLink Link = $importedLink
Id = $importedId Id = $importedId
ProductName = $importedProductName ProductName = $importedProductName
@@ -406,6 +437,18 @@ function Import-DriversJson {
Arch = "" Arch = ""
DownloadStatus = "Imported" DownloadStatus = "Imported"
} }
if ($makeName -eq 'Dell') {
# Attach optional Dell extended fields if present
if ($importedModelObject.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.SystemId)) {
$newDriverModel | Add-Member -NotePropertyName SystemId -NotePropertyValue $importedModelObject.SystemId
}
if ($importedModelObject.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabUrl)) {
$newDriverModel | Add-Member -NotePropertyName CabUrl -NotePropertyValue $importedModelObject.CabUrl
}
if ($importedModelObject.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabRelativePath)) {
$newDriverModel | Add-Member -NotePropertyName CabRelativePath -NotePropertyValue $importedModelObject.CabRelativePath
}
}
$State.Data.allDriverModels.Add($newDriverModel) $State.Data.allDriverModels.Add($newDriverModel)
$newModelsAdded++ $newModelsAdded++
WriteLog "Import-DriversJson: Added new model '$($newDriverModel.Make) - $($newDriverModel.Model)' from import. ID: $($newDriverModel.Id), Link: $($newDriverModel.Link)" WriteLog "Import-DriversJson: Added new model '$($newDriverModel.Make) - $($newDriverModel.Model)' from import. ID: $($newDriverModel.Id), Link: $($newDriverModel.Link)"
@@ -753,7 +796,16 @@ function Invoke-DownloadSelectedDrivers {
} }
'Dell' { 'Dell' {
$modelObject = @{ $modelObject = @{
Name = $driverItem.Model # Model is the display name Name = $driverItem.Model
}
if ($driverItem.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($driverItem.SystemId)) {
$modelObject.SystemId = $driverItem.SystemId
}
if ($driverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabUrl)) {
$modelObject.CabUrl = $driverItem.CabUrl
}
if ($driverItem.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabRelativePath)) {
$modelObject.CabRelativePath = $driverItem.CabRelativePath
} }
} }
'HP' { 'HP' {