Public/Functions/Catalog/Get-OSDCatalogMicrosoftDriverPack.ps1

<#
.SYNOPSIS
Returns the Microsoft Surface DriverPacks
 
.DESCRIPTION
Returns the Microsoft Surface DriverPacks
 
.PARAMETER Compatible
Filters results based on your current Product
 
.LINK
https://osd.osdeploy.com
 
.NOTES
#>

function Get-OSDCatalogMicrosoftDriverPack {
    [CmdletBinding()]
    param (
        [System.Management.Automation.SwitchParameter]$Compatible,
        [System.String]$DownloadPath,
        [System.Management.Automation.SwitchParameter]$Force
    )
#=================================================
# OSDCatalog
# https://docs.microsoft.com/en-us/surface/manage-surface-driver-and-firmware-updates
# https://docs.microsoft.com/en-us/surface/surface-system-sku-reference
# https://www.reddit.com/r/Surface/comments/mlhqw5/all_direct_download_links_for_surface/
#=================================================
$OSDCatalog = @'
[
    {
        "Name": "Surface 3 WiFi",
        "Model": "Surface 3",
        "Product": "Surface_3",
        "PackageID": "49040"
    },
    {
        "Name": "Surface 3 LTE ATT",
        "Model": "Surface 3",
        "Product": "Surface_3_US1",
        "PackageID": "49039"
    },
    {
        "Name": "Surface 3 LTE Verizon",
        "Model": "Surface 3",
        "Product": "Surface_3_US2",
        "PackageID": "49037"
    },
    {
        "Name": "Surface 3 LTE North America",
        "Model": "Surface 3",
        "Product": "Surface_3_NAG",
        "PackageID": "49037"
    },
    {
        "Name": "Surface 3 LTE Rest of World",
        "Model": "Surface 3",
        "Product": "Surface_3_ROW",
        "PackageID": "49041"
    },
    {
        "Name": "Surface Book",
        "Model": "Surface Book",
        "Product": "Surface_Book",
        "PackageID": "49497"
    },
    {
        "Name": "Surface Book 2 13",
        "Model": "Surface Book 2",
        "Product": "Surface_Book_1832",
        "PackageID": "56261"
    },
    {
        "Name": "Surface Book 2 15",
        "Model": "Surface Book 2",
        "Product": "Surface_Book_1793",
        "PackageID": "56261"
    },
    {
        "Name": "Surface Book 3 13",
        "Model": "Surface Book 3",
        "Product": "Surface_Book_3_1900",
        "PackageID": "101315"
    },
    {
        "Name": "Surface Book 3 15",
        "Model": "Surface Book 3",
        "Product": "Surface_Book_3_1899",
        "PackageID": "101315"
    },
    {
        "Name": "Surface Go Commercial",
        "Model": "Surface Go",
        "Product": "Surface_Go_1824_Commercial",
        "PackageID": "57439"
    },
    {
        "Name": "Surface Go Consumer",
        "Model": "Surface Go",
        "Product": "Surface_Go_1824_Consumer",
        "PackageID": "57439"
    },
    {
        "Name": "Surface Go LTE Commercial",
        "Model": "Surface Go",
        "Product": "Surface_Go_1825_Commercial",
        "PackageID": "57601"
    },
    {
        "Name": "Surface Go 2 Commercial",
        "Model": "Surface Go 2",
        "Product": "Surface_Go_2_1926",
        "PackageID": "101304"
    },
    {
        "Name": "Surface Go 2 Consumer",
        "Model": "Surface Go 2",
        "Product": "Surface_Go_2_1901",
        "PackageID": "101304"
    },
    {
        "Name": "Surface Go 2 LTE",
        "Model": "Surface Go 2",
        "Product": "Surface_Go_2_1927",
        "PackageID": "101304"
    },
    {
        "Name": "Surface Go 3 Commercial",
        "Model": "Surface Go 3",
        "Product": "Surface_Go_3_1926",
        "PackageID": "103504"
    },
    {
        "Name": "Surface Go 3 Consumer",
        "Model": "Surface Go 3",
        "Product": "Surface_Go_3_1901",
        "PackageID": "103504"
    },
    {
        "Name": "Surface Go 3 LTE",
        "Model": "Surface Go 3",
        "Product": "Surface_Go_3_2022",
        "PackageID": "103504"
    },
    {
        "Name": "Surface Hub 2",
        "Model": "Surface Hub 2",
        "Product": "Surface_Hub_2",
        "PackageID": "101974"
    },
    {
        "Name": "Surface Hub 2S 50",
        "Model": "Surface Hub 2S",
        "Product": "Surface Hub 2S",
        "PackageID": "101974"
    },
    {
        "Name": "Surface Hub 2S 85",
        "Model": "Surface Hub 2S",
        "Product": "Surface Hub 2S 85",
        "PackageID": "101974"
    },
    {
        "Name": "Surface Laptop",
        "Model": "Surface Laptop",
        "Product": "Surface_Laptop",
        "PackageID": "55489"
    },
    {
        "Name": "Surface Laptop 2 Commercial",
        "Model": "Surface Laptop 2",
        "Product": "Surface_Laptop_2_1769_Commercial",
        "PackageID": "57515"
    },
    {
        "Name": "Surface Laptop 2 Consumer",
        "Model": "Surface Laptop 2",
        "Product": "Surface_Laptop_2_1769_Consumer",
        "PackageID": "57515"
    },
    {
        "Name": "Surface Laptop 3 13 Intel",
        "Model": "Surface Laptop 3",
        "Product": "Surface_Laptop_3_1867:1868",
        "PackageID": "100429"
    },
    {
        "Name": "Surface Laptop 3 13 AMD",
        "Model": "Surface Laptop 3",
        "Product": "Surface_Laptop_3_1873",
        "PackageID": "100428"
    },
    {
        "Name": "Surface Laptop 3 15 Intel",
        "Model": "Surface Laptop 3",
        "Product": "Surface_Laptop_3_1872",
        "PackageID": "100429"
    },
    {
        "Name": "Surface Laptop 4 13 AMD",
        "Model": "Surface Laptop 4",
        "Product": "Surface_Laptop_4_1958:1959",
        "PackageID": "102923"
    },
    {
        "Name": "Surface Laptop 4 13 Intel",
        "Model": "Surface Laptop 4",
        "Product": "Surface_Laptop_4_1950:1951",
        "PackageID": "102924"
    },
    {
        "Name": "Surface Laptop 4 15 AMD",
        "Model": "Surface Laptop 4",
        "Product": "Surface_Laptop_4_1952:1953",
        "PackageID": "102923"
    },
    {
        "Name": "Surface Laptop 4 15 Intel",
        "Model": "Surface Laptop 4",
        "Product": "Surface_Laptop_4_1978:1979",
        "PackageID": "102924"
    },
    {
        "Name": "Surface Laptop Go",
        "Model": "Surface Laptop Go",
        "Product": "Surface_Laptop_Go_1943",
        "PackageID": "102261"
    },
    {
        "Name": "Surface Laptop Studio",
        "Model": "Surface Laptop Studio",
        "Product": "Surface_Laptop_Studio_1964",
        "PackageID": "103505"
    },
    {
        "Name": "Surface Pro",
        "Model": "Surface Pro",
        "Product": "Surface_Pro",
        "PackageID": "49038"
    },
    {
        "Name": "Surface Pro 2",
        "Model": "Surface Pro 2",
        "Product": "Surface_Pro_2",
        "PackageID": "49042"
    },
    {
        "Name": "Surface Pro 3",
        "Model": "Surface Pro 3",
        "Product": "Surface_Pro_3",
        "PackageID": "38826"
    },
    {
        "Name": "Surface Pro 4",
        "Model": "Surface Pro 4",
        "Product": "Surface_Pro_4",
        "PackageID": "49498"
    },
    {
        "Name": "Surface Pro (5th Gen)",
        "Model": "Surface Pro",
        "Product": "Surface_Pro_1796",
        "PackageID": "55484"
    },
    {
        "Name": "Surface Pro with LTE Advanced",
        "Model": "Surface Pro",
        "Product": "Surface_Pro_1807",
        "PackageID": "56278"
    },
    {
        "Name": "Surface Pro 6 Commercial",
        "Model": "Surface Pro 6",
        "Product": "Surface_Pro_6_1796_Commercial",
        "PackageID": "57514"
    },
    {
        "Name": "Surface Pro 6 Consumer",
        "Model": "Surface Pro 6",
        "Product": "Surface_Pro_6_1796_Consumer",
        "PackageID": "57514"
    },
    {
        "Name": "Surface Pro 7",
        "Model": "Surface Pro 7",
        "Product": "Surface_Pro_7_1866",
        "PackageID": "100419"
    },
    {
        "Name": "Surface Pro 7+",
        "Model": "Surface Pro 7+",
        "Product": "Surface_Pro_7+_1960",
        "PackageID": "102633"
    },
    {
        "Name": "Surface Pro 7+ LTE",
        "Model": "Surface Pro 7+",
        "Product": "Surface_Pro_7+_with_LTE_Advanced_1961",
        "PackageID": "102633"
    },
    {
        "Name": "Surface Pro 8",
        "Model": "Surface Pro 8",
        "Product": "Surface_Pro_8_for_Business_1983",
        "PackageID": "103503"
    },
    {
        "Name": "Surface Pro 8 Consumer",
        "Model": "Surface Pro 8",
        "Product": "Surface_Pro_8_1983",
        "PackageID": "103503"
    },
    {
        "Name": "Surface Pro 8 LTE",
        "Model": "Surface Pro 8",
        "Product": "Surface_Pro_8_for_Business_with_LTE_Advanced_1982",
        "PackageID": "103503"
    },
    {
        "Name": "Surface Studio",
        "Model": "Surface Studio",
        "Product": "Surface_Studio",
        "PackageID": "54311"
    },
    {
        "Name": "Surface Studio 2",
        "Model": "Surface Studio 2",
        "Product": "Surface_Studio_2_1707_Commercial",
        "PackageID": "57593"
    }
]
'@

    #=================================================
    # Reference
    #=================================================
    #https://docs.microsoft.com/en-us/surface/surface-system-sku-reference
    #https://docs.microsoft.com/en-us/surface/manage-surface-driver-and-firmware-updates
    #https://support.microsoft.com/en-us/surface/download-drivers-and-firmware-for-surface-09bb2e09-2a4b-cb69-0951-078a7739e120

    #Supported Operating Systems
    #https://support.microsoft.com/en-us/surface/surface-supported-operating-systems-9559cc3c-7a38-31b6-d9fb-571435e84cd1
    #=================================================
    # Paths
    #=================================================
    $UseCatalog             = 'Offline'
    $CloudCatalogUri        = 'https://support.microsoft.com/en-us/surface/download-drivers-and-firmware-for-surface-09bb2e09-2a4b-cb69-0951-078a7739e120'
    $BuildCatalogFile        = Join-Path $env:TEMP (Join-Path 'OSD' 'OSDCatalogMicrosoftDriverPack.json')
    $OfflineCatalogFile     = "$($MyInvocation.MyCommand.Module.ModuleBase)\Catalogs\OSDCatalog\OSDCatalogMicrosoftDriverPack.json"
    $DownloadsBaseUrl         = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id='
    #=================================================
    # Create Paths
    #=================================================
    if (-not(Test-Path (Join-Path $env:TEMP 'OSD'))) {
        $null = New-Item -Path (Join-Path $env:TEMP 'OSD') -ItemType Directory -Force
    }
    #=================================================
    # Test Build Catalog
    #=================================================
    if (Test-Path $BuildCatalogFile) {
        Write-Verbose "Build Catalog already created at $BuildCatalogFile"    

        $GetItemBuildCatalogFile = Get-Item $BuildCatalogFile

        #If the Build Catalog is older than 12 hours, delete it
        if (((Get-Date) - $GetItemBuildCatalogFile.LastWriteTime).TotalHours -gt 12) {
            Write-Verbose "Removing previous Build Catalog"
            $null = Remove-Item $GetItemBuildCatalogFile.FullName -Force
        }
        else {
            $UseCatalog = 'Build'
        }
    }
    #=================================================
    # Test Cloud Catalog
    #=================================================
    if ($Force) {
        $UseCatalog = 'Cloud'
    }
    if ($UseCatalog -eq 'Cloud') {
        if (Test-WebConnection -Uri $CloudCatalogUri) {
            #Catalog is Cloud and can be downloaded
        }
        else {
            $UseCatalog = 'Offline'
        }
    }
    #=================================================
    # UseCatalog Cloud
    #=================================================
    if ($UseCatalog -eq 'Cloud') {
        $Results = $OSDCatalog | ConvertFrom-Json
        $MasterResults = @()
    
        $MasterResults = foreach ($Item in $Results) {
            Write-Verbose "Processing $($Item.Name)"
            $DriverPage = $DownloadsBaseUrl + $Item.PackageID
            $Downloads = (Invoke-WebRequest -Uri $DriverPage -UseBasicParsing).Links
            $Downloads = $Downloads | Where-Object {$_.href -match 'download.microsoft.com'}
            $Downloads = $Downloads | Where-Object {($_.href -match 'Win11') -or ($_.href -match 'Win10')}
            $Downloads = $Downloads | Sort-Object href | Select-Object href -Unique
            #$Downloads = $Downloads | Select-Object -Last 1
            #$Item.Url = ($Downloads).href
            #$Item.FileName = Split-Path $Item.Url -Leaf

            #=================================================
            # Create Object
            #=================================================
            foreach ($Download in $Downloads) {
                $DownloadUrl = $Download.href
                Write-Verbose "Testing Download File at $DownloadUrl"

                $DownloadHeaders = (Invoke-WebRequest -Method Head -Uri $DownloadUrl -UseBasicParsing).Headers

                $ReleaseDate = Get-Date ($DownloadHeaders['Last-Modified']) -Format "yy.MM.dd"
                $FileName = Split-Path $DownloadUrl -Leaf

                if ($FileName -match 'Win11') {
                    $OSVersion = 'Windows 11 x64'
                }
                else {
                    $OSVersion = 'Windows 10 x64'
                }
                
                $ByteArray = [System.Convert]::FromBase64String($DownloadHeaders['Content-MD5'])
                $HexObject = $ByteArray | Format-Hex
                $HashMD5 = ($HexObject.Bytes | ForEach-Object {"{0:X}" -f $_}) -join ''

                $UniqueFileName = $FileName
                $UniqueFileName = $UniqueFileName -replace '_Win', ' Win'
                $UniqueFileName = $UniqueFileName.Split(' ')[1]
                $UniqueFileName = $UniqueFileName -replace 'Win10_', 'Win10 '
                $UniqueFileName = $UniqueFileName -replace 'Win11_', 'Win11 '
                $UniqueFileName = $UniqueFileName.Split('_')[0]

                $UniqueName = "Microsoft $($Item.Name) $UniqueFileName"

                $ObjectProperties = [ordered] @{
                    CatalogVersion          = Get-Date -Format yy.MM.dd
                    Status                  = $null
                    Component               = 'DriverPack'
                    ReleaseDate             = $ReleaseDate
                    Manufacturer            = 'Microsoft'
                    Model                   = $Item.Model
                    Product                 = $Item.Product
                    Name                    = $UniqueName
                    PackageID               = $Item.PackageID
                    FileName                = $FileName
                    Url                     = $DownloadUrl
                    OSVersion               = $OSVersion
                    HashMD5                 = $HashMD5
                }
                New-Object -TypeName PSObject -Property $ObjectProperties
            }
        }
        $MasterResults | ConvertTo-Json | Out-File $BuildCatalogFile -Encoding ascii -Width 2000 -Force

        if (Test-Path $BuildCatalogFile) {
            $UseCatalog = 'Build'
        }
        else {
            Write-Verbose "Could not locate $BuildCatalogFile"
            $UseCatalog = 'Offline'
        }
    }
    #=================================================
    # UseCatalog Build
    #=================================================
    if ($UseCatalog -eq 'Build') {
        Write-Verbose "Importing the Build Catalog at $BuildCatalogFile"
        $MasterResults = Get-Content -Path $BuildCatalogFile | ConvertFrom-Json
    }
    #=================================================
    # UseCatalog Offline
    #=================================================
    if ($UseCatalog -eq 'Offline') {
        Write-Verbose "Importing the Offline Catalog at $OfflineCatalogFile"
        $MasterResults = Get-Content -Path $OfflineCatalogFile | ConvertFrom-Json
    }
    #=================================================
    # Compatible
    #=================================================
    if ($PSBoundParameters.ContainsKey('Compatible')) {
        $MyComputerProduct = Get-MyComputerProduct
        Write-Verbose "Filtering Catalog for items compatible with Product $MyComputerProduct"
        $MasterResults = $MasterResults | Where-Object {$_.Product -contains $MyComputerProduct}
    }
    #=================================================
    # DownloadPath
    #=================================================
    if ($PSBoundParameters.ContainsKey('DownloadPath')) {
        $MasterResults = $MasterResults | Out-GridView -Title 'Select one or more files to Download' -PassThru -ErrorAction Stop
        foreach ($Item in $MasterResults) {
            $OutFile = Save-WebFile -SourceUrl $Item.Url -DestinationDirectory $DownloadPath -DestinationName $Item.FileName -Verbose
            $Item | ConvertTo-Json | Out-File "$($OutFile.FullName).json" -Encoding ascii -Width 2000 -Force
        }
    }
    #=================================================
    # Results
    #=================================================
    $MasterResults | Sort-Object -Property Name
    #=================================================
}