private/Get-WinPEDriverPackageDell.ps1

#Requires -PSEdition Core

function Get-WinPEDriverPackageDell {
    <#
    .SYNOPSIS
        Gets Dell WinPE 11 driver pack metadata
 
    .DESCRIPTION
        Reads the Dell UpdateUri from the global WinPE driver configuration, downloads the
        Dell knowledge base HTML, and parses the first WinPE CAB row in the current driver
        summary table. Dates and file sizes are normalized for catalog use. The function
        downloads no driver package. Configuration, request, or parsing failures produce a
        warning and no object.
 
    .EXAMPLE
        PS> Get-WinPEDriverPackageDell
 
        Returns the current amd64 Dell WinPE 11 package metadata parsed from the configured
        knowledge base page.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        System.Management.Automation.PSCustomObject. Returns one object with Architecture,
        ReadmeUri, PackageId, Version, ReleaseDate, FileName, FileSizeMB, DownloadUri, and
        Checksums properties, or no object on failure.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-08-28
 
        Dependencies:
          Network Resources: Configured Dell UpdateUri
          DotNet Classes: System.Globalization.CultureInfo, System.Net.WebUtility,
                          System.Management.Automation.PSCustomObject
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param ()

    try {
        $searchUri = $global:OSDeployModule.WinPEDrivers.'dell'.UpdateUri
        if ([string]::IsNullOrWhiteSpace($searchUri)) {
            throw "dell UpdateUri is not defined in winpedrivers.json."
        }

        $normalizeHtml = {
            param([AllowNull()][string]$Value)

            if ([string]::IsNullOrWhiteSpace($Value)) {
                return ''
            }

            $decoded = [System.Net.WebUtility]::HtmlDecode($Value)
            $text = [regex]::Replace($decoded, '<[^>]+>', ' ')
            ([regex]::Replace($text, '\s+', ' ')).Trim()
        }

        $convertReleaseDate = {
            param([string]$Value)

            foreach ($format in @('M/d/yyyy', 'MM/dd/yyyy', 'dd MMM yyyy', 'yyyy-MM-dd')) {
                try {
                    return [datetime]::ParseExact(
                        $Value,
                        $format,
                        [System.Globalization.CultureInfo]::InvariantCulture
                    ).ToString('yyyy-MM-dd')
                }
                catch {
                }
            }

            try { ([datetime]$Value).ToString('yyyy-MM-dd') }
            catch { $Value }
        }

        $convertFileSizeToMB = {
            param([string]$Value)

            $match = [regex]::Match($Value, '(?i)(\d+(?:\.\d+)?)\s*(KB|MB|GB)')
            if (-not $match.Success) {
                return $null
            }

            $size = [double]$match.Groups[1].Value
            switch ($match.Groups[2].Value.ToUpperInvariant()) {
                'GB' { return [math]::Round($size * 1024, 2) }
                'KB' { return [math]::Round($size / 1024, 2) }
                default { return [math]::Round($size, 2) }
            }
        }

        $getCellValue = {
            param(
                [string[]]$Cells,
                [int]$Index
            )

            if ($Index -lt $Cells.Count) {
                return $Cells[$Index]
            }

            $null
        }

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Fetching Dell KB page '$searchUri'"
        $response = Invoke-WebRequest -Uri $searchUri -UseBasicParsing -ErrorAction Stop
        $html = $response.Content

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Parsing CurrentDriverCABSummary table"
        $tableMatch = [regex]::Match(
            $html,
            '(?is)<table[^>]+id=["'']CurrentDriverCABSummary["''][^>]*>(?<Table>.*?)</table>'
        )

        $driverInfo = $null
        if ($tableMatch.Success) {
            foreach ($rowMatch in [regex]::Matches($tableMatch.Groups['Table'].Value, '(?is)<tr[^>]*>(?<Row>.*?)</tr>')) {
                $rowHtml = $rowMatch.Groups['Row'].Value
                $downloadMatch = [regex]::Match(
                    $rowHtml,
                    '(?i)href=["''](?<Uri>https://downloads\.dell\.com/[^"''<> ]+\.cab)["'']'
                )
                if (-not $downloadMatch.Success) {
                    continue
                }

                $cells = @(
                    [regex]::Matches($rowHtml, '(?is)<td[^>]*>(?<Cell>.*?)</td>') |
                        ForEach-Object { & $normalizeHtml $_.Groups['Cell'].Value }
                )

                if (($rowHtml -notmatch '(?i)winpe') -and (-not ($cells -match '(?i)winpe'))) {
                    continue
                }

                $driverInfo = [PSCustomObject]@{
                    PackageId    = & $getCellValue $cells 2
                    Version      = & $getCellValue $cells 3
                    ReleaseDate  = & $convertReleaseDate (& $getCellValue $cells 4)
                    FileSizeMB   = & $convertFileSizeToMB (& $getCellValue $cells 5)
                    DownloadUri  = $downloadMatch.Groups['Uri'].Value
                    FileName     = Split-Path -Path $downloadMatch.Groups['Uri'].Value -Leaf
                }
                break
            }
        }

        if (-not $driverInfo) {
            Write-Warning "[$(Get-Date -Format s)] Could not locate the Dell WinPE 11 driver pack on '$searchUri'."
            return
        }

        Write-Verbose "[$($MyInvocation.MyCommand.Name)] PackageId='$($driverInfo.PackageId)' Version='$($driverInfo.Version)' ReleaseDate='$($driverInfo.ReleaseDate)' FileName='$($driverInfo.FileName)'"

        @(
            [PSCustomObject]@{
                Architecture = 'amd64'
                ReadmeUri    = $searchUri
                PackageId    = $driverInfo.PackageId
                Version      = $driverInfo.Version
                ReleaseDate  = $driverInfo.ReleaseDate
                FileName     = $driverInfo.FileName
                FileSizeMB   = $driverInfo.FileSizeMB
                DownloadUri  = $driverInfo.DownloadUri
                Checksums    = [PSCustomObject]@{}
            }
        )
    }
    catch {
        Write-Warning "[$(Get-Date -Format s)] $($_.Exception.Message)"
    }
}