Private/Get-ADTWinGetPath.ps1

#-----------------------------------------------------------------------------
#
# MARK: Get-ADTWinGetPath
#
#-----------------------------------------------------------------------------

function Get-ADTWinGetPath
{
    # Internal function to get the WinGet Path. We can't rely on the output of Get-AppxPackage for some systems as it'll update, but Get-AppxPackage won't reflect the new path fast enough.
    function Out-ADTWinGetPath
    {
        # For the system user, get the path from Program Files directly.
        if ($Script:ADT.RunningAsSystem)
        {
            return (Get-ChildItem -Path "$([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles))\WindowsApps\Microsoft.DesktopAppInstaller*\winget.exe" | Sort-Object -Descending | Select-Object -First 1)
        }
        elseif ([System.IO.File]::Exists(($wingetPath = "$(Get-AppxPackage -Name Microsoft.DesktopAppInstaller -AllUsers:$Script:ADT.RunningAsSystem | Sort-Object -Property Version -Descending | Select-Object -ExpandProperty InstallLocation -First 1)\winget.exe")))
        {
            return $wingetPath
        }
    }

    # Test whether WinGet is installed and available at all.
    Write-ADTLogEntry -Message "Finding best file path for WinGet, please wait..."
    if (!($wingetPath = Out-ADTWinGetPath) -or ![System.IO.File]::Exists($wingetPath))
    {
        # Throw if we're not admin.
        if (!$Script:ADT.RunningAsAdmin)
        {
            $naerParams = @{
                Exception = [System.UnauthorizedAccessException]::new("WinGet is not installed. Please install Microsoft.DesktopAppInstaller and try again.")
                Category = [System.Management.Automation.ErrorCategory]::PermissionDenied
                ErrorId = 'MicrosoftDesktopAppInstallerCannotInstallFailure'
                RecommendedAction = "Please install Microsoft.DesktopAppInstaller as an admin, then try again."
            }
            throw (New-ADTErrorRecord @naerParams)
        }

        # Install Microsoft.DesktopAppInstaller.
        Install-ADTWinGetDesktopAppInstallerDependency

        # Throw if the installation was successful but we still don't have WinGet.
        if (!($wingetPath = Out-ADTWinGetPath) -or ![System.IO.File]::Exists($wingetPath))
        {
            $naerParams = @{
                Exception = [System.InvalidOperationException]::new("Failed to get a valid WinGet path after successfully pre-provisioning the app. Please report this issue for further analysis.")
                Category = [System.Management.Automation.ErrorCategory]::InvalidResult
                ErrorId = 'MicrosoftDesktopAppInstallerMissingFailure'
                RecommendedAction = "Please report this issue to the project's maintainer for further analysis."
            }
            throw (New-ADTErrorRecord @naerParams)
        }
    }

    # Test whether we have any output from winget.exe. If this is null, it typically means the appropriate MSVC++ runtime is not installed.
    if (!($wingetOutput = & $wingetPath))
    {
        # Throw if we're not admin.
        if (!$Script:ADT.RunningAsAdmin)
        {
            $naerParams = @{
                Exception = [System.UnauthorizedAccessException]::new("The installed version of WinGet was unable to run. Please ensure the latest Visual Studio 2015-2022 Runtime is installed and try again.")
                Category = [System.Management.Automation.ErrorCategory]::PermissionDenied
                ErrorId = 'VcRedistCannotInstallFailure'
                RecommendedAction = "Please install the latest Visual Studio 2015-2022 Runtime as an admin, then try again."
            }
            throw (New-ADTErrorRecord @naerParams)
        }

        # Install MSVCRT onto device.
        Install-ADTWinGetVcRedistDependency

        # Throw if we're still not able to run WinGet.
        if (!($wingetOutput = & $wingetPath))
        {
            $naerParams = @{
                Exception = [System.InvalidOperationException]::new("The installed version of WinGet was unable to run. This is possibly related to the Visual Studio 2015-2022 Runtime.")
                Category = [System.Management.Automation.ErrorCategory]::InvalidResult
                ErrorId = 'MicrosoftDesktopAppInstallerExecutionFailure'
                RecommendedAction = "Please verify that WinGet.exe can run on this system, then try again."
            }
            throw (New-ADTErrorRecord @naerParams)
        }
    }

    # Ensure winget.exe is above the minimum version.
    if ([System.Version](($wingetOutput | Select-Object -First 1) -replace '^.+\sv') -lt $Script:ADT.WinGetMinVersion)
    {
        # Throw if we're not admin.
        if (!$Script:ADT.RunningAsAdmin)
        {
            $naerParams = @{
                Exception = [System.UnauthorizedAccessException]::new("The installed version of WinGet is less than $($Script:ADT.WinGetMinVersion). Please update Microsoft.DesktopAppInstaller and try again.")
                Category = [System.Management.Automation.ErrorCategory]::PermissionDenied
                ErrorId = 'VcRedistCannotInstallFailure'
                RecommendedAction = "Please update Microsoft.DesktopAppInstaller as an admin, then try again."
            }
            throw (New-ADTErrorRecord @naerParams)
        }

        # Install the missing dependency and reset variables.
        Install-ADTWinGetDesktopAppInstallerDependency
        $wingetPath = Out-ADTWinGetPath

        # Ensure winget.exe is above the minimum version.
        if ([System.Version]($wingetVer = (($wingetOutput = & $wingetPath) | Select-Object -First 1) -replace '^.+\sv') -lt $Script:ADT.WinGetMinVersion)
        {
            $naerParams = @{
                Exception = [System.InvalidOperationException]::new("The installed WinGet version of $wingetVer is less than $($Script:ADT.WinGetMinVersion). Please check the DISM pre-provisioning logs and try again.")
                Category = [System.Management.Automation.ErrorCategory]::InvalidResult
                ErrorId = 'MicrosoftDesktopAppInstallerVersionError'
                RecommendedAction = "Please check the DISM pre-provisioning logs, then try again."
            }
            throw (New-ADTErrorRecord @naerParams)
        }

        # Reset WinGet sources after updating. Helps with a corner-case issue discovered.
        Write-ADTLogEntry -Message "Resetting all WinGet sources following update, please wait..."
        if (!($wgSrcRes = & $wingetPath source reset --force 2>&1).Equals('Resetting all sources...Done'))
        {
            Write-ADTLogEntry -Message "An issue occurred while resetting WinGet sources [$($wgSrcRes.TrimEnd('.'))]. Continuing with operation." -Severity 2
        }
    }

    # Return tested path to the caller.
    Write-ADTLogEntry -Message "Using WinGet path [$wingetPath]."
    return $wingetPath
}