private/Get-WinPEDriverPackageSurface.ps1
|
#Requires -PSEdition Core function Get-WinPEDriverPackageSurface { <# .SYNOPSIS Gets Microsoft Surface driver package metadata .DESCRIPTION Resolves a named Surface source from the global WinPE driver configuration and downloads its Microsoft Download Center details page. The function selects a current MSI link, preferring a file name containing Win11, derives version and release date, and attempts a HEAD request for file size. It emits one metadata row for each configured DriverPacks entry. Missing configuration and download-page failures are terminating; a failed HEAD request leaves FileSizeMB null. .PARAMETER Name Specifies the Surface source name in the global WinPE driver configuration. The source must define SearchUri and DriverPacks properties. .EXAMPLE PS> Get-WinPEDriverPackageSurface -Name 'surface-laptop-7' Returns metadata rows for each architecture configured for surface-laptop-7. .INPUTS None. This function does not accept pipeline input. .OUTPUTS System.Management.Automation.PSCustomObject. Returns zero or more objects with Architecture, ReadmeUri, PackageId, Version, ReleaseDate, FileName, FileSizeMB, DownloadUri, and Checksums properties. Version, ReleaseDate, or FileSizeMB can be null when the page does not expose usable values. .NOTES Author: David Segura Company: Recast Software Version: 1.0.0 Date: 2026-08-28 Dependencies: Network Resources: Configured Microsoft Download Center SearchUri and MSI URI DotNet Classes: System.IO.Path, System.Management.Automation.PSCustomObject #> [CmdletBinding()] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name ) $sourceData = $global:OSDeployModule.WinPEDrivers.$Name if (-not $sourceData) { throw "Surface source '$Name' was not found in winpedrivers.json." } $searchUri = $sourceData.SearchUri if ([string]::IsNullOrWhiteSpace($searchUri)) { throw "Surface SearchUri is not defined for '$Name' in winpedrivers.json." } Write-Verbose "[$($MyInvocation.MyCommand.Name)] Fetching Surface page '$searchUri' for '$Name'" $response = Invoke-WebRequest -Uri $searchUri -UseBasicParsing -ErrorAction Stop $msiLinks = @( $response.Links | Where-Object { $_.href -match '(?i)^https://download\.microsoft\.com/.+\.msi(?:\?|$)' } | ForEach-Object { $_.href } | Select-Object -Unique ) if (-not $msiLinks) { throw "Could not locate any MSI download links on '$searchUri'. Page format may have changed." } $preferredLinks = @($msiLinks | Where-Object { $_ -match '(?i)Win11' }) $selectedUri = if ($preferredLinks) { $preferredLinks[0] } else { $msiLinks[0] } $fileName = [System.IO.Path]::GetFileName(($selectedUri -split '\?')[0]) if ([string]::IsNullOrWhiteSpace($fileName)) { throw "Could not derive a file name from Surface download URI '$selectedUri'." } $versionMatch = [regex]::Match($fileName, '_(?<Version>\d+(?:\.\d+){3})\.msi$') $version = if ($versionMatch.Success) { $versionMatch.Groups['Version'].Value } else { $null } $releaseDate = $null $releaseDateMatch = [regex]::Match( $response.Content, '(?is)Date Published:\s*</h3>\s*<p[^>]*>\s*(?<Date>[^<]+?)\s*</p>' ) if ($releaseDateMatch.Success) { try { $releaseDate = (Get-Date -Date $releaseDateMatch.Groups['Date'].Value.Trim()).ToString('yyyy-MM-dd') } catch { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Could not parse release date '$($releaseDateMatch.Groups['Date'].Value.Trim())' for '$Name'" } } $fileSizeMb = $null try { $headResponse = Invoke-WebRequest -Uri $selectedUri -Method Head -UseBasicParsing -ErrorAction Stop $contentLength = @($headResponse.Headers['Content-Length']) | Select-Object -First 1 if ($contentLength) { $fileSizeMb = [math]::Round(([double]$contentLength) / 1MB, 1) } } catch { Write-Verbose "[$($MyInvocation.MyCommand.Name)] HEAD request failed for '$selectedUri': $($_.Exception.Message)" } $packResults = foreach ($pack in $sourceData.DriverPacks) { [PSCustomObject]@{ Architecture = $pack.Architecture ReadmeUri = $searchUri PackageId = if ($pack.PackageId) { $pack.PackageId } else { (($searchUri -split 'id=')[-1] -split '&')[0] } Version = $version ReleaseDate = $releaseDate FileName = $fileName FileSizeMB = $fileSizeMb DownloadUri = $selectedUri Checksums = [PSCustomObject]@{} } } foreach ($entry in $packResults) { Write-Verbose "[$($MyInvocation.MyCommand.Name)] '$Name' — Architecture='$($entry.Architecture)' Version='$($entry.Version)' ReleaseDate='$($entry.ReleaseDate)' FileName='$($entry.FileName)'" } $packResults } |