Private/Get-WingetManifest.ps1
|
<#
.SYNOPSIS Fetches and parses a Winget package manifest with explicit architecture and scope resolution. .DESCRIPTION Queries the official Winget catalog, downloads the version/installer manifest YAML, filters installers by target architecture (x64, arm64, x86) and scope (machine), and extracts declared switches, ProductCode, and success codes. #> function Get-WingetManifest { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [string]$PackageId, [Parameter()] [string]$Version = '', [Parameter()] [ValidateSet('x64', 'arm64', 'x86')] [string]$Architecture = 'x64', [Parameter()] [string]$CacheDir = (Join-Path $env:LOCALAPPDATA 'WingetIntune\ManifestCache') ) if (-not (Test-Path $CacheDir)) { New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null } $firstLetter = $PackageId.Substring(0, 1).ToLowerInvariant() $pathParts = $PackageId.Split('.') $publisher = $pathParts[0] $appName = if ($pathParts.Count -gt 1) { [string]::Join('/', $pathParts[1..($pathParts.Count - 1)]) } else { $publisher } Write-Host " [+] Querying Winget manifest for '$PackageId' (Target Arch: $Architecture)..." -ForegroundColor Cyan $manifestData = [PSCustomObject]@{ PackageId = $PackageId Version = $Version Name = $PackageId Publisher = $publisher Architecture = $Architecture InstallerType = 'exe' InstallerUrl = '' InstallerSha256 = '' InstallerSwitches = @{ Silent = '' SilentWithProgress = '' Custom = '' InstallLocation = '' } InstallerSuccessCodes = @(0, 3010, 1641) ProductCode = '' UpgradeCode = '' AppsAndFeaturesEntries = @() } # 1. Query via winget CLI with architecture filter if installed if (Get-Command 'winget.exe' -ErrorAction SilentlyContinue) { try { $showRaw = winget show --exact --id $PackageId --source winget --accept-source-agreements 2>$null foreach ($line in ($showRaw -split "`r?`n")) { if ($line -match '^Version:\s*(.+)$') { $manifestData.Version = $Matches[1].Trim() } if ($line -match '^Publisher:\s*(.+)$') { $manifestData.Publisher = $Matches[1].Trim() } if ($line -match '^Installer Type:\s*(.+)$') { $manifestData.InstallerType = $Matches[1].Trim().ToLowerInvariant() } if ($line -match '^Installer SHA256:\s*(.+)$') { $manifestData.InstallerSha256 = $Matches[1].Trim() } if ($line -match '^Installer Url:\s*(.+)$') { $manifestData.InstallerUrl = $Matches[1].Trim() } } } catch { } } # 2. Query GitHub CDN raw manifest YAML with architecture matching if ($manifestData.Version) { $ver = $manifestData.Version $manifestYamlUrls = @( "https://raw.githubusercontent.com/microsoft/winget-pkgs/master/manifests/$firstLetter/$publisher/$appName/$ver/$publisher.$appName.installer.yaml", "https://raw.githubusercontent.com/microsoft/winget-pkgs/master/manifests/$firstLetter/$publisher/$appName/$ver/$PackageId.installer.yaml", "https://raw.githubusercontent.com/microsoft/winget-pkgs/master/manifests/$firstLetter/$publisher/$appName/$ver/$publisher.$appName.yaml", "https://raw.githubusercontent.com/microsoft/winget-pkgs/master/manifests/$firstLetter/$publisher/$appName/$ver/$PackageId.yaml" ) foreach ($url in $manifestYamlUrls) { try { $yamlContent = (Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop).Content if ($yamlContent) { $inSwitches = $false $inInstallers = $false $currentInstallerArch = '' $isTargetArch = $true foreach ($line in ($yamlContent -split "`r?`n")) { if ($line -match '^InstallerType:\s*(.+)$') { $manifestData.InstallerType = $Matches[1].Trim().ToLowerInvariant() } if ($line -match '^ProductCode:\s*(.+)$') { $manifestData.ProductCode = $Matches[1].Trim() } # Handle Architecture blocks if ($line -match '^\s+Architecture:\s*(.+)$') { $currentInstallerArch = $Matches[1].Trim().ToLowerInvariant() $isTargetArch = ($currentInstallerArch -eq $Architecture.ToLowerInvariant() -or $currentInstallerArch -eq 'neutral') } if ($line -match '^\s+InstallerUrl:\s*(.+)$' -and $isTargetArch) { $manifestData.InstallerUrl = $Matches[1].Trim() } if ($line -match '^\s+InstallerSha256:\s*(.+)$' -and $isTargetArch) { $manifestData.InstallerSha256 = $Matches[1].Trim() } if ($line -match '^InstallerSwitches:' -or $line -match '^\s+InstallerSwitches:') { $inSwitches = $true continue } if ($inSwitches) { if ($line -match '^\s+Silent:\s*[''"]?(.*?)[''"]?\s*$') { $manifestData.InstallerSwitches.Silent = $Matches[1].Trim() } if ($line -match '^\s+SilentWithProgress:\s*[''"]?(.*?)[''"]?\s*$') { $manifestData.InstallerSwitches.SilentWithProgress = $Matches[1].Trim() } if ($line -match '^\s+Custom:\s*[''"]?(.*?)[''"]?\s*$') { $manifestData.InstallerSwitches.Custom = $Matches[1].Trim() } if ($line -match '^\S') { $inSwitches = $false } } } break } } catch { } } } # Cache manifest descriptor locally $cacheFile = Join-Path $CacheDir ($PackageId + ".manifest.json") $manifestData | ConvertTo-Json -Depth 10 | Out-File -FilePath $cacheFile -Force -Encoding utf8 return $manifestData } |