2023-08-08 17:54:47 -05:00
<# PSScriptInfo
2023-07-26 23:12:17 -05:00
2023-10-19 17:12:55 -05:00
. VERSION 3.0 . 1
2023-07-26 23:12:17 -05:00
2023-08-08 17:54:47 -05:00
. GUID 3b581edb - 5d90 - 4fa1 -ba15 - 4f2377275463
2023-07-26 23:12:17 -05:00
2023-08-08 17:54:47 -05:00
. AUTHOR asheroto , 1ckov , MisterZeus , ChrisTitusTech
. COMPANYNAME asheroto
2023-09-07 14:37:27 -05:00
. TAGS PowerShell Windows winget win get install installer fix script setup
2023-08-08 17:54:47 -05:00
. PROJECTURI https : / / github . com / asheroto / winget-install
. RELEASENOTES
[ Version 0.0.1 ] - Initial Release .
[ Version 0.0.2 ] - Implemented function to get the latest version of winget and its license .
[ Version 0.0.3 ] - Signed file for PSGallery .
[ Version 0.0.4 ] - Changed URI to grab latest release instead of releases and preleases .
[ Version 0.0.5 ] - Updated version number of dependencies .
[ Version 1.0.0 ] - Major refactor code , see release notes for more information .
[ Version 1.0.1 ] - Fixed minor bug where version 2.8 was hardcoded in URL .
[ Version 1.0.2 ] - Hardcoded UI Xaml version 2.8 . 4 as a failsafe in case the API fails . Added CheckForUpdates , Version , Help function s . Various bug fixes .
[ Version 1.0.3 ] - Added error message to catch block . Fixed bug where appx package was not being installed .
[ Version 1.0.4 ] - MisterZeus optimized code for readability .
[ Version 2.0.0 ] - Major refactor . Reverted to UI . Xaml 2.7 . 3 for stability . Adjusted script to fix install issues due to winget changes ( thank you ChrisTitusTech ) . Added in all architecture support .
[ Version 2.0.1 ] - Renamed repo and URL references from winget-installer to winget-install . Added extra space after the last line of output .
[ Version 2.0.2 ] - Adjusted CheckForUpdates to include Install-Script instructions and extra spacing .
2023-09-07 14:37:27 -05:00
[ Version 2.1.0 ] - Added alternate method / URL for dependencies in case the main URL is down . Fixed licensing issue when winget is installed on Server 2022 .
[ Version 2.1.1 ] - Switched primary / alternate methods . Added Cleanup function to avoid errors when cleaning up temp files . Added output of URL for alternate method . Suppressed Add-AppxProvisionedPackage output . Improved success message . Improved verbiage . Improve PS script comments . Added check if the URL is empty . Moved display of URL beneath the check .
[ Version 3.0.0 ] - Major changes . Added OS version detection checks - detects OS version , release ID , ensures compatibility . Forces older file installation for Server 2022 to avoid issues after installing . Added DebugMode , DisableCleanup , Force . Renamed CheckForUpdates to CheckForUpdate . Improved output . Improved error handling . Improved comments . Improved code readability . Moved CheckForUpdate into function . Added PowerShellGalleryName . Renamed Get-OSVersion to Get-OSInfo . Moved architecture detection into Get-OSInfo . Renamed Get-NewestLink to Get-WingetDownloadUrl . Have Get-WingetDownloadUrl not get preview releases .
2023-10-19 17:12:55 -05:00
[ Version 3.0.1 ] - Updated Get-OSInfo function to fix issues when used on non-English systems . Improved error handling of " resources in use " error .
2023-08-08 17:54:47 -05:00
#>
<#
. SYNOPSIS
2023-09-07 14:37:27 -05:00
Downloads and installs the latest version of winget and its dependencies . Updates the PATH variable if needed .
2023-08-08 17:54:47 -05:00
. DESCRIPTION
2023-09-07 14:37:27 -05:00
Downloads and installs the latest version of winget and its dependencies . Updates the PATH variable if needed .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
This script is designed to be straightforward and easy to use , removing the hassle of manually downloading , installing , and configuring winget . To make the newly installed winget available for use , a system reboot may be required after running the script .
This function should be run with administrative privileges .
2023-08-08 17:54:47 -05:00
. EXAMPLE
winget-install
2023-09-07 14:37:27 -05:00
. PARAMETER DebugMode
Enables debug mode , which shows additional information for debugging .
. PARAMETER DisableCleanup
Disables cleanup of the script and prerequisites after installation .
. PARAMETER Force
Ensures installation of winget and its dependencies , even if already present .
. PARAMETER CheckForUpdate
Checks if there is an update available for the script .
2023-08-08 17:54:47 -05:00
. PARAMETER Version
Displays the version of the script .
. PARAMETER Help
2023-09-07 14:37:27 -05:00
Displays the full help information for the script .
2023-08-08 17:54:47 -05:00
. NOTES
2023-10-19 17:12:55 -05:00
Version : 3.0 . 1
2023-08-08 17:54:47 -05:00
Created by : asheroto
. LINK
Project Site : https : / / github . com / asheroto / winget-install
#>
[ CmdletBinding ( ) ]
param (
[ switch ] $Version ,
[ switch ] $Help ,
2023-09-07 14:37:27 -05:00
[ switch ] $CheckForUpdate ,
[ switch ] $DisableCleanup ,
[ switch ] $DebugMode ,
[ switch ] $Force
2023-08-08 17:54:47 -05:00
)
# Version
2023-10-19 17:12:55 -05:00
$CurrentVersion = '3.0.1'
2023-08-08 17:54:47 -05:00
$RepoOwner = 'asheroto'
$RepoName = 'winget-install'
2023-09-07 14:37:27 -05:00
$PowerShellGalleryName = 'winget-install'
2023-08-08 17:54:47 -05:00
# Versions
$ProgressPreference = 'SilentlyContinue' # Suppress progress bar (makes downloading super fast)
2023-09-07 14:37:27 -05:00
$ConfirmPreference = 'None' # Suppress confirmation prompts
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
# Display version if -Version is specified
2023-08-08 17:54:47 -05:00
if ( $Version . IsPresent ) {
$CurrentVersion
exit 0
2023-07-26 23:12:17 -05:00
}
2023-09-07 14:37:27 -05:00
# Display full help if -Help is specified
2023-08-08 17:54:47 -05:00
if ( $Help ) {
Get-Help -Name $MyInvocation . MyCommand . Source -Full
exit 0
2023-07-26 23:12:17 -05:00
}
2023-09-07 14:37:27 -05:00
# Display $PSVersionTable and Get-Host if -Verbose is specified
2023-08-08 17:54:47 -05:00
if ( $PSBoundParameters . ContainsKey ( 'Verbose' ) -and $PSBoundParameters [ 'Verbose' ] ) {
$PSVersionTable
Get-Host
}
2023-07-26 23:12:17 -05:00
2023-09-07 14:37:27 -05:00
function Get-TempFolder {
<#
. SYNOPSIS
Gets the path of the current user ' s temp folder .
. DESCRIPTION
This function retrieves the path of the current user ' s temp folder .
. EXAMPLE
Get-TempFolder
#>
return [ System.IO.Path ] :: GetTempPath ( )
}
function Get-OSInfo {
<#
. SYNOPSIS
Retrieves detailed information about the operating system version and architecture .
. DESCRIPTION
This function queries both the Windows registry and the Win32_OperatingSystem class to gather comprehensive information about the operating system . It returns details such as the release ID , display version , name , type ( Workstation / Server ) , numeric version , edition ID , version ( object that includes major , minor , and build numbers ) , and architecture ( OS architecture , not processor architecture ) .
. EXAMPLE
Get-OSInfo
This example retrieves the OS version details of the current system and returns an object with properties like ReleaseId , DisplayVersion , Name , Type , NumericVersion , EditionId , Version , and Architecture .
. EXAMPLE
( Get-OSInfo ) . Version . Major
This example retrieves the major version number of the operating system . The Get-OSInfo function returns an object with a Version property , which itself is an object containing Major , Minor , and Build properties . You can access these sub-properties using dot notation .
. EXAMPLE
$osDetails = Get-OSInfo
Write-Output " OS Name: $( $osDetails . Name ) "
Write-Output " OS Type: $( $osDetails . Type ) "
Write-Output " OS Architecture: $( $osDetails . Architecture ) "
This example stores the result of Get-OSInfo in a variable and then accesses various properties to print details about the operating system .
#>
[ CmdletBinding ( ) ]
param ( )
try {
# Get registry values
$registryValues = Get-ItemProperty " HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion "
$releaseIdValue = $registryValues . ReleaseId
$displayVersionValue = $registryValues . DisplayVersion
$nameValue = $registryValues . ProductName
$editionIdValue = $registryValues . EditionId
# Strip out "Server" from the $editionIdValue if it exists
$editionIdValue = $editionIdValue -replace " Server " , " "
# Get OS details using Get-CimInstance because the registry key for Name is not always correct with Windows 11
$osDetails = Get-CimInstance -ClassName Win32_OperatingSystem
$nameValue = $osDetails . Caption
# Get architecture details of the OS (not the processor)
2023-10-19 17:12:55 -05:00
# Get only the numbers
$architecture = ( $osDetails . OSArchitecture -replace " [^\d] " ) . Trim ( )
2023-09-07 14:37:27 -05:00
# If 32-bit or 64-bit replace with x32 and x64
2023-10-19 17:12:55 -05:00
if ( $architecture -eq " 32 " ) {
2023-09-07 14:37:27 -05:00
$architecture = " x32 "
2023-10-19 17:12:55 -05:00
} elseif ( $architecture -eq " 64 " ) {
2023-09-07 14:37:27 -05:00
$architecture = " x64 "
}
# Get OS version details (as version object)
$versionValue = [ System.Environment ] :: OSVersion . Version
# Determine product type
# Reference: https://learn.microsoft.com/en-us/dotnet/api/microsoft.powershell.commands.producttype?view=powershellsdk-1.1.0
if ( $osDetails . ProductType -eq 1 ) {
$typeValue = " Workstation "
} elseif ( $osDetails . ProductType -eq 2 -or $osDetails . ProductType -eq 3 ) {
$typeValue = " Server "
} else {
$typeValue = " Unknown "
}
# Extract numerical value from Name
$numericVersion = ( $nameValue -replace " [^\d] " ) . Trim ( )
# Create and return custom object with the required properties
$result = [ PSCustomObject ] @ {
ReleaseId = $releaseIdValue
DisplayVersion = $displayVersionValue
Name = $nameValue
Type = $typeValue
NumericVersion = $numericVersion
EditionId = $editionIdValue
Version = $versionValue
Architecture = $architecture
}
return $result
} catch {
Write-Error " Unable to get OS version details. `n Error: $_ "
exit 1
}
}
2023-08-08 17:54:47 -05:00
function Get-GitHubRelease {
<#
. SYNOPSIS
Fetches the latest release information of a GitHub repository .
2023-07-26 23:12:17 -05:00
2023-08-08 17:54:47 -05:00
. DESCRIPTION
This function uses the GitHub API to get information about the latest release of a specified repository , including its version and the date it was published .
. PARAMETER Owner
The GitHub username of the repository owner .
. PARAMETER Repo
The name of the repository .
. EXAMPLE
Get-GitHubRelease -Owner " asheroto " -Repo " winget-install "
This command retrieves the latest release version and published datetime of the winget-install repository owned by asheroto .
#>
[ CmdletBinding ( ) ]
param (
[ string ] $Owner ,
[ string ] $Repo
)
try {
$url = " https://api.github.com/repos/ $Owner / $Repo /releases/latest "
$response = Invoke-RestMethod -Uri $url -ErrorAction Stop
$latestVersion = $response . tag_name
$publishedAt = $response . published_at
# Convert UTC time string to local time
$UtcDateTime = [ DateTime ] :: Parse ( $publishedAt , [ System.Globalization.CultureInfo ] :: InvariantCulture , [ System.Globalization.DateTimeStyles ] :: RoundtripKind )
$PublishedLocalDateTime = $UtcDateTime . ToLocalTime ( )
[ PSCustomObject ] @ {
LatestVersion = $latestVersion
PublishedDateTime = $PublishedLocalDateTime
}
} catch {
Write-Error " Unable to check for updates. `n Error: $_ "
exit 1
}
2023-07-26 23:12:17 -05:00
}
2023-09-07 14:37:27 -05:00
function CheckForUpdate {
param (
[ string ] $RepoOwner ,
[ string ] $RepoName ,
[ version ] $CurrentVersion ,
[ string ] $PowerShellGalleryName
)
$Data = Get-GitHubRelease -Owner $RepoOwner -Repo $RepoName
if ( $Data . LatestVersion -gt $CurrentVersion ) {
Write-Output " `n A new version of $RepoName is available. `n "
Write-Output " Current version: $CurrentVersion . "
Write-Output " Latest version: $( $Data . LatestVersion ) . "
Write-Output " Published at: $( $Data . PublishedDateTime ) . `n "
Write-Output " You can download the latest version from https://github.com/ $RepoOwner / $RepoName /releases `n "
if ( $PowerShellGalleryName ) {
Write-Output " Or you can run the following command to update: "
Write-Output " Install-Script $PowerShellGalleryName -Force `n "
}
} else {
Write-Output " `n $RepoName is up to date. `n "
Write-Output " Current version: $CurrentVersion . "
Write-Output " Latest version: $( $Data . LatestVersion ) . "
Write-Output " Published at: $( $Data . PublishedDateTime ) . "
Write-Output " `n Repository: https://github.com/ $RepoOwner / $RepoName /releases `n "
}
exit 0
}
2023-08-08 17:54:47 -05:00
function Write-Section($text ) {
<#
. SYNOPSIS
Prints a text block surrounded by a section divider for enhanced output readability .
. DESCRIPTION
2023-09-07 14:37:27 -05:00
This function takes a string input and prints it to the console , surrounded by a section divider made of hash characters .
It is designed to enhance the readability of console output .
2023-07-26 23:12:17 -05:00
2023-08-08 17:54:47 -05:00
. PARAMETER text
The text to be printed within the section divider .
. EXAMPLE
Write-Section " Downloading Files... "
This command prints the text " Downloading Files... " surrounded by a section divider .
#>
Write-Output " "
Write-Output ( " # " * ( $text . Length + 4 ) )
Write-Output " # $text # "
Write-Output ( " # " * ( $text . Length + 4 ) )
Write-Output " "
2023-07-26 23:12:17 -05:00
}
2023-09-07 14:37:27 -05:00
function Get-WingetDownloadUrl {
2023-08-08 17:54:47 -05:00
<#
. SYNOPSIS
Retrieves the download URL of the latest release asset that matches a specified pattern from the GitHub repository .
. DESCRIPTION
2023-09-07 14:37:27 -05:00
This function uses the GitHub API to get information about the latest release of the winget-cli repository .
It then retrieves the download URL for the release asset that matches a specified pattern .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER Match
2023-08-08 17:54:47 -05:00
The pattern to match in the asset names .
. EXAMPLE
2023-09-07 14:37:27 -05:00
Get-WingetDownloadUrl " msixbundle "
2023-08-08 17:54:47 -05:00
This command retrieves the download URL for the latest release asset with a name that contains " msixbundle " .
#>
[ CmdletBinding ( ) ]
2023-09-07 14:37:27 -05:00
param (
[ Parameter ( Mandatory = $true ) ]
[ string ] $Match
)
$uri = " https://api.github.com/repos/microsoft/winget-cli/releases "
Write-Debug " Getting information from $uri "
$releases = Invoke-RestMethod -uri $uri -Method Get -ErrorAction stop
Write-Debug " Getting latest release... "
foreach ( $release in $releases ) {
if ( $release . name -match " preview " ) {
continue
}
$data = $release . assets | Where-Object name -Match $Match
if ( $data ) {
return $data . browser_download_url
}
}
Write-Debug " Falling back to the latest release... "
$latestRelease = $releases | Select-Object -First 1
$data = $latestRelease . assets | Where-Object name -Match $Match
2023-08-08 17:54:47 -05:00
return $data . browser_download_url
2023-07-26 23:12:17 -05:00
}
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
function Get-WingetStatus {
<#
. SYNOPSIS
Checks if winget is installed .
. DESCRIPTION
This function checks if winget is installed .
. EXAMPLE
Get-WingetStatus
#>
# Check if winget is installed
$winget = Get-Command -Name winget -ErrorAction SilentlyContinue
# If winget is installed, return $true
if ( $null -ne $winget ) {
return $true
}
# If winget is not installed, return $false
return $false
}
2023-08-08 17:54:47 -05:00
function Update-PathEnvironmentVariable {
<#
. SYNOPSIS
Updates the PATH environment variable with a new path for both the User and Machine levels .
. DESCRIPTION
The function will add a new path to the PATH environment variable , making sure it is not a duplicate .
If the new path is already in the PATH variable , the function will skip adding it .
This function operates at both User and Machine levels .
. PARAMETER NewPath
The new directory path to be added to the PATH environment variable .
. EXAMPLE
Update-PathEnvironmentVariable -NewPath " C:\NewDirectory "
This command will add the directory " C:\NewDirectory " to the PATH variable at both the User and Machine levels .
#>
param (
[ string ] $NewPath
)
foreach ( $Level in " Machine " , " User " ) {
# Get the current PATH variable
$path = [ Environment ] :: GetEnvironmentVariable ( " PATH " , $Level )
# Check if the new path is already in the PATH variable
if ( ! $path . Contains ( $NewPath ) ) {
2023-09-07 14:37:27 -05:00
if ( $DebugMode ) {
Write-Output " Adding $NewPath to PATH variable for $Level ... "
} else {
Write-Output " Adding PATH variable for $Level ... "
}
2023-08-08 17:54:47 -05:00
# Add the new path to the PATH variable
$path = ( $path + " ; " + $NewPath ) . Split ( ';' ) | Select-Object -Unique
$path = $path -join ';'
# Set the new PATH variable
[ Environment ] :: SetEnvironmentVariable ( " PATH " , $path , $Level )
} else {
2023-09-07 14:37:27 -05:00
if ( $DebugMode ) {
Write-Output " $NewPath already present in PATH variable for $Level , skipping. "
} else {
Write-Output " PATH variable already present for $Level , skipping. "
}
2023-08-08 17:54:47 -05:00
}
2023-07-26 23:12:17 -05:00
}
2023-08-08 17:54:47 -05:00
}
function Handle-Error {
<#
. SYNOPSIS
Handles common errors that may occur during an installation process .
. DESCRIPTION
This function takes an ErrorRecord object and checks for certain known error codes .
Depending on the error code , it writes appropriate warning messages or throws the error .
. PARAMETER ErrorRecord
The ErrorRecord object that represents the error that was caught . This object contains
information about the error , including the exception that was thrown .
. EXAMPLE
try {
# Some code that may throw an error...
} catch {
Handle-Error $_
}
This example shows how you might use the Handle-Error function in a try-catch block .
If an error occurs in the try block , the catch block catches it and calls Handle-Error ,
passing the error ( represented by the $_ variable ) to the function .
#>
param ( $ErrorRecord )
# Store current value
$OriginalErrorActionPreference = $ErrorActionPreference
# Set to silently continue
$ErrorActionPreference = 'SilentlyContinue'
if ( $ErrorRecord . Exception . Message -match '0x80073D06' ) {
Write-Warning " Higher version already installed. "
Write-Warning " That's okay, continuing... "
} elseif ( $ErrorRecord . Exception . Message -match '0x80073CF0' ) {
Write-Warning " Same version already installed. "
Write-Warning " That's okay, continuing... "
} elseif ( $ErrorRecord . Exception . Message -match '0x80073D02' ) {
# Stop execution and return the ErrorRecord so that the calling try/catch block throws the error
Write-Warning " Resources modified are in-use. Try closing Windows Terminal / PowerShell / Command Prompt and try again. "
Write-Warning " If the problem persists, restart your computer. "
return $ErrorRecord
2023-09-07 14:37:27 -05:00
} elseif ( $ErrorRecord . Exception . Message -match 'Unable to connect to the remote server' ) {
Write-Warning " Cannot connect to the Internet to download the required files. "
Write-Warning " Try running the script again and make sure you are connected to the Internet. "
Write-Warning " Sometimes the nuget.org server is down, so you may need to try again later. "
return $ErrorRecord
} elseif ( $ErrorRecord . Exception . Message -match " The remote name could not be resolved " ) {
Write-Warning " Cannot connect to the Internet to download the required files. "
Write-Warning " Try running the script again and make sure you are connected to the Internet. "
Write-Warning " Make sure DNS is working correctly on your computer. "
2023-08-08 17:54:47 -05:00
} else {
# For other errors, we should stop the execution and return the ErrorRecord so that the calling try/catch block throws the error
return $ErrorRecord
2023-07-26 23:12:17 -05:00
}
2023-08-08 17:54:47 -05:00
# Reset to original value
$ErrorActionPreference = $OriginalErrorActionPreference
2023-07-26 23:12:17 -05:00
}
2023-09-07 14:37:27 -05:00
function Cleanup {
<#
. SYNOPSIS
Deletes a file or directory specified without prompting for confirmation or displaying errors .
2023-07-26 23:12:17 -05:00
2023-09-07 14:37:27 -05:00
. DESCRIPTION
This function takes a path to a file or directory and deletes it without prompting for confirmation or displaying errors .
If the path is a directory , the function will delete the directory and all its contents .
. PARAMETER Path
The path of the file or directory to be deleted .
. PARAMETER Recurse
If the path is a directory , this switch specifies whether to delete the directory and all its contents .
. EXAMPLE
Cleanup -Path " C:\Temp "
This example deletes the directory " C:\Temp " and all its contents .
. EXAMPLE
Cleanup -Path " C:\Temp " -Recurse
This example deletes the directory " C:\Temp " and all its contents .
. EXAMPLE
Cleanup -Path " C:\Temp\file.txt "
This example deletes the file " C:\Temp\file.txt " .
#>
param (
[ string ] $Path ,
[ switch ] $Recurse
)
try {
if ( Test-Path -Path $Path ) {
if ( $Recurse -and ( Get-Item -Path $Path ) -is [ System.IO.DirectoryInfo ] ) {
Get-ChildItem -Path $Path -Recurse | Remove-Item -Force -Recurse
Remove-Item -Path $Path -Force -Recurse
} else {
Remove-Item -Path $Path -Force
}
}
if ( $DebugMode ) {
Write-Output " Deleted: $Path "
}
} catch {
# Errors are ignored
2023-08-08 17:54:47 -05:00
}
2023-07-26 23:12:17 -05:00
}
2023-09-07 14:37:27 -05:00
function Install-Prerequisite {
<#
. SYNOPSIS
Downloads and installs a prerequisite for winget .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. DESCRIPTION
This function takes a name , version , URL , alternate URL , content type , and body and downloads and installs the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER Name
The name of the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER Version
The version of the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER Url
The URL of the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER AlternateUrl
The alternate URL of the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER ContentType
The content type of the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER Body
The body of the prerequisite .
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
. PARAMETER NupkgVersion
The nupkg version of the prerequisite .
. PARAMETER AppxFileVersion
The appx file version of the prerequisite .
. EXAMPLE
Install-Prerequisite -Name " VCLibs " -Version " 14.00 " -Url " https://store.rg-adguard.net/api/GetFiles " -AlternateUrl " https://aka.ms/Microsoft.VCLibs. $arch .14.00.Desktop.appx " -ContentType " application/x-www-form-urlencoded " -Body " type=PackageFamilyName&url=Microsoft.VCLibs.140.00_8wekyb3d8bbwe&ring=RP&lang=en-US "
Where $arch is the architecture type of the current system .
#>
param (
[ string ] $Name ,
[ string ] $Url ,
[ string ] $AlternateUrl ,
[ string ] $ContentType ,
[ string ] $Body ,
[ string ] $NupkgVersion ,
[ string ] $AppxFileVersion
)
$osVersion = Get-OSInfo
$arch = $osVersion . Architecture
Write-Section " Downloading & installing ${arch} ${Name} ... "
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
$ThrowReason = @ {
Message = " "
Code = 0
}
2023-08-08 17:54:47 -05:00
try {
2023-09-07 14:37:27 -05:00
# ============================================================================ #
# Windows 10 / Server 2022 detection
# ============================================================================ #
# Function to extract domain from URL
function Get-DomainFromUrl($url ) {
$uri = [ System.Uri ] $url
$domain = $uri . Host -replace " ^www\. "
return $domain
2023-08-08 17:54:47 -05:00
}
2023-09-07 14:37:27 -05:00
# If Server 2022 or Windows 10, force non-store version of VCLibs (return true)
$messageTemplate = " {OS} detected. Using {DOMAIN} version of {NAME}. "
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
# Determine the OS-specific information
$osType = $osVersion . Type
$osNumericVersion = $osVersion . NumericVersion
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
if ( ( $osType -eq " Server " -and $osNumericVersion -eq 2022 ) -or ( $osType -eq " Workstation " -and $osNumericVersion -eq 10 ) ) {
if ( $osType -eq " Server " ) {
$osName = " Server 2022 "
} else {
$osName = " Windows 10 "
}
$domain = Get-DomainFromUrl $AlternateUrl
$ThrowReason . Message = ( $messageTemplate -replace " {OS} " , $osName ) -replace " {NAME} " , $Name -replace " {DOMAIN} " , $domain
$ThrowReason . Code = 1
throw
}
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
# ============================================================================ #
# Primary method
# ============================================================================ #
$url = Invoke-WebRequest -Uri $Url -Method " POST " -ContentType $ContentType -Body $Body -UseBasicParsing | ForEach-Object Links | Where-Object outerHTML -match " $Name .+_ ${arch} __8wekyb3d8bbwe.appx " | ForEach-Object href
# If the URL is empty, try the alternate method
if ( $url -eq " " ) {
$ThrowReason . Message = " URL is empty "
$ThrowReason . Code = 2
throw
}
2023-10-19 17:12:55 -05:00
if ( $DebugMode ) {
Write-Output " URL: ${url} `n "
}
Write-Output " Installing ${arch} ${Name} ... "
2023-09-07 14:37:27 -05:00
Add-AppxPackage $url -ErrorAction Stop
Write-Output " `n $Name installed successfully. "
} catch {
# Alternate method
2023-10-19 17:12:55 -05:00
if ( $_ . Exception . Message -match '0x80073D02' ) {
# If resources in use exception, fail immediately
Handle-Error $_
throw
}
2023-08-08 17:54:47 -05:00
try {
2023-09-07 14:37:27 -05:00
$url = $AlternateUrl
# Throw reason if alternate method is required
if ( $ThrowReason . Code -eq 0 ) {
Write-Warning " Error when trying to download or install $Name . Trying alternate method... "
} else {
Write-Warning $ThrowReason . Message
}
Write-Output " "
# If the URL is empty, throw error
if ( $url -eq " " ) {
throw " URL is empty "
}
# Specific logic for VCLibs alternate method
if ( $Name -eq " VCLibs " ) {
if ( $DebugMode ) {
Write-Output " URL: $( $url ) `n "
}
Write-Output " Installing ${arch} ${Name} ... "
Add-AppxPackage $url -ErrorAction Stop
Write-Output " `n $Name installed successfully. "
}
# Specific logic for UI.Xaml
if ( $Name -eq " UI.Xaml " ) {
$TempFolder = Get-TempFolder
$uiXaml = @ {
url = $url
appxFolder = " tools/AppX/ $arch /Release/ "
appxFilename = " Microsoft.UI.Xaml. $AppxFileVersion .appx "
nupkgFilename = Join-Path -Path $TempFolder -ChildPath " Microsoft.UI.Xaml. $NupkgVersion .nupkg "
nupkgFolder = Join-Path -Path $TempFolder -ChildPath " Microsoft.UI.Xaml. $NupkgVersion "
}
# Debug
if ( $DebugMode ) {
$formattedDebugOutput = ( $uiXaml | ConvertTo-Json -Depth 10 -Compress ) -replace '\\\\' , '\'
Write-Output " uiXaml: "
Write-Output $formattedDebugOutput
Write-Output " "
}
# Downloading
Write-Output " Downloading UI.Xaml... "
if ( $DebugMode ) {
Write-Output " URL: $( $uiXaml . url ) "
}
Invoke-WebRequest -Uri $uiXaml . url -OutFile $uiXaml . nupkgFilename
# Check if folder exists and delete if needed (will occur whether DisableCleanup is $true or $false)
Cleanup -Path $uiXaml . nupkgFolder -Recurse
# Extracting
Write-Output " Extracting... `n "
if ( $DebugMode ) {
Write-Output " Into folder: $( $uiXaml . nupkgFolder ) `n "
}
Add-Type -Assembly System . IO . Compression . FileSystem
[ IO.Compression.ZipFile ] :: ExtractToDirectory ( $uiXaml . nupkgFilename , $uiXaml . nupkgFolder )
# Prep for install
Write-Output " Installing ${arch} ${Name} ... "
$XamlAppxFolder = Join-Path -Path $uiXaml . nupkgFolder -ChildPath $uiXaml . appxFolder
$XamlAppxPath = Join-Path -Path $XamlAppxFolder -ChildPath $uiXaml . appxFilename
# Debugging
if ( $DebugMode ) { Write-Output " Installing appx Packages in: $XamlAppxFolder " }
# Install
Get-ChildItem -Path $XamlAppxPath -Filter * . appx | ForEach-Object {
if ( $DebugMode ) { Write-Output " Installing appx Package: $( $_ . Name ) " }
Add-AppxPackage $_ . FullName -ErrorAction Stop
}
Write-Output " `n UI.Xaml installed successfully. "
# Cleanup
if ( $DisableCleanup -eq $false ) {
if ( $DebugMode ) { Write-Output " " } # Extra line break for readability if DebugMode is enabled
Cleanup -Path $uiXaml . nupkgFilename
Cleanup -Path $uiXaml . nupkgFolder -Recurse $true
}
}
2023-08-08 17:54:47 -05:00
} catch {
2023-09-07 14:37:27 -05:00
# If unable to connect to remote server and Windows 10 or Server 2022, display warning message
$ShowOldVersionMessage = $False
if ( $_ . Exception . Message -match " Unable to connect to the remote server " ) {
# Determine the correct Windows caption and set $ShowOutput to $True if conditions are met
if ( $osVersion . Type -eq " Workstation " -and $osVersion . NumericVersion -eq 10 ) {
$WindowsCaption = " Windows 10 "
$ShowOldVersionMessage = $True
} elseif ( $osVersion . Type -eq " Server " -and $osVersion . NumericVersion -eq 2022 ) {
$WindowsCaption = " Server 2022 "
$ShowOldVersionMessage = $True
}
# Output the warning message if $ShowOldVersionMessage is $True, otherwise output the generic error message
if ( $ShowOldVersionMessage ) {
$OldVersionMessage = " There is an issue connecting to the server to download $Name . Unfortunately this is a known issue with the prerequisite server URLs - sometimes they are down. Since you're using $WindowsCaption you must use the non-store versions of the prerequisites, the prerequisites from the Windows store will not work, so you may need to try again later or install manually. "
Write-Warning $OldVersionMessage
} else {
Write-Warning " Error when trying to download or install $Name . Please try again later or manually install $Name . "
}
}
2023-08-08 17:54:47 -05:00
$errorHandled = Handle-Error $_
if ( $null -ne $errorHandled ) {
throw $errorHandled
}
$errorHandled = $null
}
}
2023-09-07 14:37:27 -05:00
}
# ============================================================================ #
# Initial checks
# ============================================================================ #
# Check for updates if -CheckForUpdate is specified
if ( $CheckForUpdate ) {
CheckForUpdate -RepoOwner $RepoOwner -RepoName $RepoName -CurrentVersion $CurrentVersion -PowerShellGalleryName $PowerShellGalleryName
}
# Heading
Write-Output " winget-install $CurrentVersion "
2023-10-19 17:12:55 -05:00
Write-Output " To check for updates, run winget-install -CheckForUpdate "
2023-09-07 14:37:27 -05:00
# Set OS version
$osVersion = Get-OSInfo
# Set architecture type
$arch = $osVersion . Architecture
# If it's a workstation, make sure it is Windows 10+
if ( $osVersion . Type -eq " Workstation " -and $osVersion . NumericVersion -lt 10 ) {
Write-Error " winget is only compatible with Windows 10 or greater. "
exit 1
}
# If it's a workstation with Windows 10, make sure it's version 1809 or greater
if ( $osVersion . Type -eq " Workstation " -and $osVersion . NumericVersion -eq 10 -and $osVersion . ReleaseId -lt 1809 ) {
Write-Error " winget is only compatible with Windows 10 version 1809 or greater. "
exit 1
}
# If it's a server, it needs to be 2022+
if ( $osVersion . Type -eq " Server " -and $osVersion . NumericVersion -lt 2022 ) {
Write-Error " winget is only compatible with Windows Server 2022+. "
exit 1
}
# Check if winget is already installed
if ( Get-WingetStatus ) {
if ( $Force -eq $false ) {
Write-Output " winget is already installed, exiting... "
exit 0
}
}
# ============================================================================ #
# Beginning of installation process
# ============================================================================ #
try {
# ============================================================================ #
# Install prerequisites
# ============================================================================ #
# VCLibs
Install-Prerequisite -Name " VCLibs " -Version " 14.00 " -Url " https://store.rg-adguard.net/api/GetFiles " -AlternateUrl " https://aka.ms/Microsoft.VCLibs. $arch .14.00.Desktop.appx " -ContentType " application/x-www-form-urlencoded " -Body " type=PackageFamilyName&url=Microsoft.VCLibs.140.00_8wekyb3d8bbwe&ring=RP&lang=en-US "
# UI.Xaml
Install-Prerequisite -Name " UI.Xaml " -Version " 2.7.3 " -Url " https://store.rg-adguard.net/api/GetFiles " -AlternateUrl " https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.3 " -ContentType " application/x-www-form-urlencoded " -Body " type=ProductId&url=9P5VK8KZB5QZ&ring=RP&lang=en-US " -NupkgVersion " 2.7.3 " -AppxFileVersion " 2.7 "
# ============================================================================ #
# Install winget
# ============================================================================ #
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
$TempFolder = Get-TempFolder
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
# Output
Write-Section " Downloading & installing winget... "
2023-08-08 17:54:47 -05:00
Write-Output " Retrieving download URL for winget from GitHub... "
2023-09-07 14:37:27 -05:00
$wingetUrl = Get-WingetDownloadUrl -Match " msixbundle "
2023-08-08 17:54:47 -05:00
$wingetPath = Join-Path -Path $tempFolder -ChildPath " winget.msixbundle "
2023-09-07 14:37:27 -05:00
$wingetLicenseUrl = Get-WingetDownloadUrl -Match " License1.xml "
2023-08-08 17:54:47 -05:00
$wingetLicensePath = Join-Path -Path $tempFolder -ChildPath " license1.xml "
2023-09-07 14:37:27 -05:00
# If the URL is empty, throw error
if ( $wingetUrl -eq " " ) {
throw " URL is empty "
}
Write-Output " Downloading winget... "
if ( $DebugMode ) {
Write-Output " `n URL: $wingetUrl "
Write-Output " Saving as: $wingetPath "
}
2023-08-08 17:54:47 -05:00
Invoke-WebRequest -Uri $wingetUrl -OutFile $wingetPath
2023-09-07 14:37:27 -05:00
Write-Output " Downloading license... "
if ( $DebugMode ) {
Write-Output " `n URL: $wingetLicenseUrl "
Write-Output " Saving as: $wingetLicensePath "
}
2023-08-08 17:54:47 -05:00
Invoke-WebRequest -Uri $wingetLicenseUrl -OutFile $wingetLicensePath
2023-09-07 14:37:27 -05:00
Write-Output " `n Installing winget... "
2023-08-08 17:54:47 -05:00
2023-09-07 14:37:27 -05:00
# Debugging
if ( $DebugMode ) {
Write-Output " wingetPath: $wingetPath "
Write-Output " wingetLicensePath: $wingetLicensePath "
}
2023-08-08 17:54:47 -05:00
# Try to install winget
try {
# Add-AppxPackage will throw an error if the app is already installed or higher version installed, so we need to catch it and continue
2023-09-07 14:37:27 -05:00
Add-AppxProvisionedPackage -Online -PackagePath $wingetPath -LicensePath $wingetLicensePath -ErrorAction SilentlyContinue | Out-Null
Write-Output " `n winget installed successfully. "
2023-08-08 17:54:47 -05:00
} catch {
$errorHandled = Handle-Error $_
if ( $null -ne $errorHandled ) {
throw $errorHandled
}
$errorHandled = $null
}
2023-09-07 14:37:27 -05:00
# Cleanup
if ( $DisableCleanup -eq $false ) {
if ( $DebugMode ) { Write-Output " " } # Extra line break for readability if DebugMode is enabled
Cleanup -Path $wingetPath
Cleanup -Path $wingetLicensePath
}
# ============================================================================ #
# PATH environment variable
# ============================================================================ #
2023-08-08 17:54:47 -05:00
# Add the WindowsApps directory to the PATH variable
Write-Section " Checking and adding WindowsApps directory to PATH variable for current user if not present... "
$WindowsAppsPath = [ IO.Path ] :: Combine ( [ Environment ] :: GetEnvironmentVariable ( " LOCALAPPDATA " ) , " Microsoft " , " WindowsApps " )
Update-PathEnvironmentVariable -NewPath $WindowsAppsPath
2023-09-07 14:37:27 -05:00
# ============================================================================ #
2023-08-08 17:54:47 -05:00
# Finished
2023-09-07 14:37:27 -05:00
# ============================================================================ #
2023-08-08 17:54:47 -05:00
Write-Section " Installation complete! "
2023-09-07 14:37:27 -05:00
# Timeout for 5 seconds to check winget
Write-Output " Checking if winget is installed and working... "
Start-Sleep -Seconds 3
# Check if winget is installed
if ( Get-WingetStatus -eq $true ) {
Write-Output " winget is installed and working now, you can go ahead and use it. "
} else {
2023-10-19 17:12:55 -05:00
Write-Warning " winget is installed but is not detected as a command. Try using winget now. If it doesn't work, wait about 1 minute and try again (it is sometimes delayed). Also try restarting your computer. "
2023-09-07 14:37:27 -05:00
Write-Warning " If you restart your computer and the command still isn't recognized, please read the Troubleshooting section `n of the README: https://github.com/asheroto/winget-install#troubleshooting `n "
Write-Warning " Make sure you have the latest version of the script by running this command: $PowerShellGalleryName -CheckForUpdate "
}
2023-08-08 17:54:47 -05:00
} catch {
2023-09-07 14:37:27 -05:00
# ============================================================================ #
2023-08-08 17:54:47 -05:00
# Error handling
2023-09-07 14:37:27 -05:00
# ============================================================================ #
2023-08-08 17:54:47 -05:00
Write-Section " WARNING! An error occurred during installation! "
2023-09-07 14:37:27 -05:00
Write-Warning " If messages above don't help and the problem persists, please read the Troubleshooting section `n of the README: https://github.com/asheroto/winget-install#troubleshooting "
Write-Warning " Make sure you have the latest version of the script by running this command: $PowerShellGalleryName -CheckForUpdate "
2023-08-08 17:54:47 -05:00
# If it's not 0x80073D02 (resources in use), show error
if ( $_ . Exception . Message -notmatch '0x80073D02' ) {
2023-09-07 14:37:27 -05:00
if ( $DebugMode ) {
Write-Warning " Line number : $( $_ . InvocationInfo . ScriptLineNumber ) "
}
2023-08-08 17:54:47 -05:00
Write-Warning " Error: $( $_ . Exception . Message ) `n "
}
2023-10-19 17:12:55 -05:00
}