private/core-driverpack/Update-OSDCoreDriverPackCatalogSurface.ps1

function Update-OSDCoreDriverPackCatalogSurface {
    <#
    .SYNOPSIS
        Updates the Microsoft Surface driver pack catalog bundled with the module.
 
    .DESCRIPTION
        Reads the bundled Surface catalog JSON, resolves live Microsoft download pages for
        entries with UpdatePage values, enriches FileName, Url, and ReleaseDate where live
        values are found, and replaces the local module Surface catalog file.
 
    .PARAMETER LocalDriverPackCatalog
        Path to the local Surface catalog JSON file to update.
 
    .PARAMETER OSDProduct
        Limits live catalog updates to entries whose SystemId matches this device product value.
 
    .PARAMETER PassThru
        Returns Surface driver pack catalog objects after updating the local catalog.
 
    .EXAMPLE
        Update-OSDCoreDriverPackCatalogSurface
 
        Updates the Surface driver pack catalog bundled with the module.
 
    .EXAMPLE
        Update-OSDCoreDriverPackCatalogSurface -PassThru
 
        Updates the Surface catalog and returns catalog objects.
 
    .OUTPUTS
        None by default. PSCustomObject[] when PassThru is specified.
 
    .NOTES
        Base catalog: core\driverpacks\surface.json.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$LocalDriverPackCatalog = (Join-Path $($MyInvocation.MyCommand.Module.ModuleBase) 'core\driverpacks\surface.json'),

        [Parameter(Mandatory = $false)]
        [string]$OSDProduct,

        [Parameter(Mandatory = $false)]
        [switch]$PassThru
    )

    $Error.Clear()
    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start"

    $tempCatalogPath = Join-Path -Path $env:TEMP -ChildPath 'osdcloud-driverpack-surface.json'
    if (Test-Path -LiteralPath $tempCatalogPath) {
        Remove-Item -LiteralPath $tempCatalogPath -Force -ErrorAction SilentlyContinue
    }

    $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
    $msiPattern = 'https://download\.microsoft\.com/download/[^"''<>\s]+\.msi'
    $datePatterns = @(
        '"detailsSection_file_date"\s*:\s*"(\d{1,2}/\d{1,2}/\d{4})"',
        '"datePublished"\s*:\s*"(\d{1,2}/\d{1,2}/\d{4})',
        '(?is)Date Published.{0,500}?(\d{1,2}/\d{1,2}/\d{4})'
    )
    $updatePageCache = @{}
    $networkCalls = 0
    $fallbackCalls = 0

    try {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Indexing $LocalDriverPackCatalog"
        $jsonCatalogContent = Get-Content -Path $LocalDriverPackCatalog -Raw -Encoding UTF8 -ErrorAction Stop | ConvertFrom-Json
        if (-not $jsonCatalogContent) {
            $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                [System.Exception]::new('Failed to load Surface driver pack catalog content'),
                'CatalogLoadFailed',
                [System.Management.Automation.ErrorCategory]::InvalidData,
                $LocalDriverPackCatalog
            )
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }

        $catalogVersion = Get-Date -Format yy.MM.dd
        Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Building Surface driver pack catalog (CatalogVersion: $catalogVersion)"

        $matchesOSDProduct = {
            param (
                [Parameter(Mandatory = $true)]
                $Item
            )

            if ([string]::IsNullOrWhiteSpace($OSDProduct)) {
                return $true
            }

            foreach ($systemId in @($Item.SystemId)) {
                if ([string]::Equals([string]$systemId, $OSDProduct, [System.StringComparison]::OrdinalIgnoreCase)) {
                    return $true
                }
            }

            return $false
        }

        $targetCatalogContent = @(if ([string]::IsNullOrWhiteSpace($OSDProduct)) {
                $jsonCatalogContent
            }
            else {
                $jsonCatalogContent | Where-Object { & $matchesOSDProduct -Item $_ }
            })

        if ([string]::IsNullOrWhiteSpace($OSDProduct)) {
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Updating all Surface driver pack entries."
        }
        else {
            Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [INFO] Recast: OSDCloud is updating the Surface DriverPack catalog for $OSDProduct."
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Updating Surface driver pack entries matching OSDProduct '$OSDProduct'. Matched $($targetCatalogContent.Count) entries."
            if ($targetCatalogContent.Count -eq 0) {
                # Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No Surface driver pack catalog entries match OSDProduct '$OSDProduct'. Skipping live update checks."
            }
        }

        $uniqueUpdatePages = @($targetCatalogContent.UpdatePage | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique)
        if ($uniqueUpdatePages.Count -gt 0) {
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Resolving $($uniqueUpdatePages.Count) unique UpdatePage URLs"
        }

        foreach ($updatePage in $uniqueUpdatePages) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Refreshing DriverPacks at $updatePage"
            $networkCalls++
            try {
                $response = Invoke-WebRequest -Uri $updatePage -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -ErrorAction Stop
                $html = $response.Content

                $allMsi = @(
                    [regex]::Matches($html, $msiPattern) |
                    ForEach-Object { $_.Value } |
                    Select-Object -Unique
                )

                if ($allMsi.Count -eq 0 -and $updatePage -match '[?&]id=(\d+)') {
                    $confirmUri = "https://www.microsoft.com/en-us/download/confirmation.aspx?id=$($Matches[1])"
                    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Downloading $confirmUri"
                    $fallbackCalls++
                    $networkCalls++
                    $response = Invoke-WebRequest -Uri $confirmUri -UseBasicParsing -UserAgent $userAgent -MaximumRedirection 5 -ErrorAction Stop
                    $html = $response.Content
                    $allMsi = @(
                        [regex]::Matches($html, $msiPattern) |
                        ForEach-Object { $_.Value } |
                        Select-Object -Unique
                    )
                }

                if ($allMsi.Count -gt 0) {
                    $win11Uris = @($allMsi | Where-Object { $_ -match 'Win11' })
                    $candidates = if ($win11Uris.Count -gt 0) { $win11Uris } else { $allMsi }
                    $bestUri = $candidates |
                        Sort-Object {
                            if ($_ -match '_(\d{5})_') { [int]$Matches[1] } else { 0 }
                        } -Descending |
                        Select-Object -First 1

                    $newDate = $null
                    $now = [datetime]::UtcNow
                    foreach ($datePattern in $datePatterns) {
                        foreach ($match in [regex]::Matches($html, $datePattern)) {
                            try {
                                $parsed = [datetime]::ParseExact($match.Groups[1].Value, 'M/d/yyyy', $null)
                                if ($parsed.Year -ge 2015 -and $parsed -le $now.AddMonths(3)) {
                                    $newDate = $parsed.ToString('yy.MM.dd')
                                    break
                                }
                            }
                            catch { }
                        }

                        if ($newDate) {
                            break
                        }
                    }

                    $updatePageCache[$updatePage] = @{
                        Error       = $null
                        FileName    = ($bestUri -replace '.+/', '')
                        Url         = $bestUri
                        ReleaseDate = $newDate
                    }
                }
                else {
                    $updatePageCache[$updatePage] = @{ Error = 'No MSI links found' }
                    Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No MSI links found for $updatePage"
                }
            }
            catch {
                $updatePageCache[$updatePage] = @{ Error = $_.Exception.Message }
                Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Failed to resolve $updatePage`: $($_.Exception.Message)"
            }
        }

        $results = foreach ($item in $jsonCatalogContent) {
            $isUpdateTarget = & $matchesOSDProduct -Item $item
            $itemCatalogVersion = $item.CatalogVersion
            $releaseDate = $item.ReleaseDate
            $fileName = $item.FileName
            $url = $item.Url

            if ($isUpdateTarget) {
                $itemCatalogVersion = $catalogVersion
            }

            if ($isUpdateTarget -and $item.UpdatePage -and $updatePageCache.ContainsKey($item.UpdatePage)) {
                $cached = $updatePageCache[$item.UpdatePage]
                if (-not $cached.Error) {
                    $fileName = $cached.FileName
                    $url = $cached.Url
                    if ($cached.ReleaseDate) {
                        $releaseDate = $cached.ReleaseDate
                    }
                }
            }

            $displayName = $item.Name
            if ($isUpdateTarget) {
                $baseName = $item.Name -replace '\s*\[.*?\]$', ''
                $displayName = "$baseName [$releaseDate]"
            }

            $objectProperties = [Ordered]@{
                CatalogVersion  = $itemCatalogVersion
                ReleaseDate     = $releaseDate
                Name            = $displayName
                Manufacturer    = $item.Manufacturer
                Model           = $item.Model
                SystemId        = $item.SystemId
                FileName        = $fileName
                Url             = $url
                OperatingSystem = $item.OperatingSystem
                OSArchitecture  = $item.OSArchitecture
                HashMD5         = $item.HashMD5
                UpdatePage      = $item.UpdatePage
            }
            [PSCustomObject]$objectProperties
        }

        $results | ConvertTo-Json -Depth 10 | Out-File -FilePath $tempCatalogPath -Encoding utf8 -Force
        $updatedCatalogContent = Get-Content -Path $tempCatalogPath -Raw -Encoding UTF8 -ErrorAction Stop | ConvertFrom-Json
        if (-not $updatedCatalogContent) {
            $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                [System.Exception]::new('Generated Surface driver pack catalog JSON is invalid'),
                'CatalogValidationFailed',
                [System.Management.Automation.ErrorCategory]::InvalidData,
                $tempCatalogPath
            )
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        }

        if ($PSCmdlet.ShouldProcess($LocalDriverPackCatalog, 'Update Surface driver pack catalog')) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Updating $LocalDriverPackCatalog"
            Copy-Item -Path $tempCatalogPath -Destination $LocalDriverPackCatalog -Force
        }

        Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Performance: UpdatePages=$($uniqueUpdatePages.Count), NetworkCalls=$networkCalls, FallbackCalls=$fallbackCalls"
        Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Found $($results.Count) Surface driver packs"

        if ($PassThru.IsPresent) {
            Get-OSDCoreDriverPackCatalogSurface -LocalDriverPackCatalog $LocalDriverPackCatalog
        }
    }
    finally {
        if (Test-Path $tempCatalogPath) {
            Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Removing temporary catalog file"
            Remove-Item -Path $tempCatalogPath -Force -ErrorAction SilentlyContinue
        }
        Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End"
    }
}