Tasks/300-Install-ABWinGetPackages.ps1

function Install-ABWinGetPackages {
    <#
    .SYNOPSIS
    Installs apps using WinGet (machine scope).
    .DESCRIPTION
    Installs the Microsoft.WinGet.Client module, repairs/updates WinGet, installs the Visual C++ runtime that WinGet
    needs when running as SYSTEM (matching the OS architecture), and then installs each package. A failure in one of
    the preparation steps is logged and the remaining steps still run; WinGet is in-box on current Windows 11 builds.
    .PARAMETER PackageIds
    WinGet package IDs (e.g. Google.Chrome) or Microsoft Store IDs (e.g. 9WZDNCRD29V9), one per line.
    .PARAMETER VCRedistUrl
    Download URL for the x64 Visual C++ redistributable. Leave empty to skip installing it on x64 devices.
    .PARAMETER VCRedistArmUrl
    Download URL for the ARM64 Visual C++ redistributable. Leave empty to skip installing it on ARM64 devices.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)] [string[]] $PackageIds,
        [Parameter(Mandatory = $false)] [string] $VCRedistUrl = 'https://aka.ms/vs/17/release/vc_redist.x64.exe',
        [Parameter(Mandatory = $false)] [string] $VCRedistArmUrl = 'https://aka.ms/vs/17/release/vc_redist.arm64.exe'
    )

    try {
        Assert-ABNuGetProvider
        Write-ABLog 'Installing Microsoft.WinGet.Client module'
        Install-Module -Name Microsoft.WinGet.Client -Force -Scope AllUsers -Repository PSGallery -ErrorAction Stop | Out-Null
        Import-Module Microsoft.WinGet.Client -ErrorAction Stop
    }
    catch {
        Write-ABLog "Unable to install the WinGet.Client module: $_"
    }

    # Repair-WinGetPackageManager updates WinGet, but it periodically breaks (unsupported in Windows PowerShell, CDN
    # timeouts, etc.). WinGet is in-box on current Windows 11 builds, so a failure here isn't fatal.
    Write-ABLog 'Installing latest WinGet package and dependencies'
    try {
        Repair-WinGetPackageManager -Force -Latest -ErrorAction Stop | Out-Null
    }
    catch {
        Write-ABLog " WinGet repair failed, will use the existing WinGet if present: $_"
    }

    # Use the Visual C++ runtime that matches the OS architecture
    $vcUrl = if (Test-ABArm64) { $VCRedistArmUrl } else { $VCRedistUrl }
    if ($vcUrl) {
        try {
            $vcPath = Join-Path $env:TEMP 'vc_redist.exe'
            Invoke-ABDownload -Url $vcUrl -Destination $vcPath
            Write-ABLog 'Installing Visual C++ redistributable'
            Start-Process -FilePath $vcPath -ArgumentList '/install', '/quiet', '/norestart' -Wait
            Remove-Item $vcPath -Force -ErrorAction SilentlyContinue
        }
        catch {
            Write-ABLog " Unable to install the Visual C++ redistributable: $_"
        }
    }

    # Use the newest winget.exe (the version is part of the package folder name)
    $wingetExe = Resolve-Path "$($env:ProgramFiles)\WindowsApps\Microsoft.DesktopAppInstaller_*__8wekyb3d8bbwe\winget.exe" -ErrorAction SilentlyContinue |
        Sort-Object { try { [version](($_.Path -split '_')[1]) } catch { [version]'0.0' } } |
        Select-Object -Last 1 -ExpandProperty Path
    if (-not $wingetExe) {
        throw 'winget.exe not found, unable to install the WinGet packages'
    }
    $wingetVer = & "$wingetExe" --version
    Write-ABLog "WinGet version is: $wingetVer"

    foreach ($id in $PackageIds) {
        Write-ABLog "WinGet installing: $id"
        try {
            & "$wingetExe" install $id --silent --scope machine --accept-package-agreements --accept-source-agreements
            Write-ABLog " WinGet exit code: $LASTEXITCODE"
        }
        catch {
            Write-ABLog "WinGet installing error: $_"
        }
    }
}