# --------------------------------------------- #
# Automated AppX Package Installer - by Elektro #
# --------------------------------------------- #
# This script will find any AppX package files within the current directory (without recursion),
# print info about the package, print a message in case of the package is already installed,
# and install the package in case of it is not installed, handling errors during installation.
# ---------------------------------------------------------------------
# Takes a string argument that points to an AppX package name or file,
# then it uses a regular expression to match the package string pattern
# and returns a custom object with the Name, Version, Architecture,
# PublisherId and other properties.
#
# Parameters:
# -PackageString: A string that points to the file name
# or full path of an AppX package file.
#
# Returns:
# A PSCustomObject object with these properties defined:
# [String] Name
# [String] Version
# [String] Architecture
# [String] PublisherId
# [String] FullName
function Get-AppXPackageInfo {
param (
[Parameter(Mandatory=$true)] [string]$PackageString
)
#$dirname = [System.IO.Path]::GetDirectoryName($PackageString)
#if ([string]::IsNullOrEmpty($dirname)) {
# $dirname = $PWD
#}
$filename = [System.IO.Path]::GetFileName($PackageString) -replace "(?i)\.appxbundle$", "" -replace "(?i)\.msixbundle$", "" -replace "(?i)\.appx$", ""
$fileExt = [System.IO.Path]::GetExtension($PackageString)
$regex = '^(?<Name>.+?)_(?<Version>.+?)_(?<Architecture>.+?)_(?<chars>~)?_(?<PublisherId>.+)$'
$match = $filename -match $regex
if (!$match) {
throw "Unable to parse the string package: '$PackageString'"
}
[string]$packageName = $matches['Name']
[string]$packageVersion = $matches['Version']
[string]$packageArchitecture = $matches['Architecture']
[string]$packagePublisherId = $matches['PublisherId']
[string]$chars = $matches['chars']
[string]$packageFullName = "${packageName}_${packageVersion}_${packageArchitecture}_${chars}_${packagePublisherId}$fileExt"
#[string]$packageFullPath = [System.IO.Path]::Combine($dirname, "$filename$fileExt")
[PSCustomObject]@{
Name = $packageName
Version = $packageVersion
Architecture = $packageArchitecture
PublisherId = $packagePublisherId
FullName = $packageFullName
#FullPath = $packageFullPath
}
}
# Determines whether an Appx package matching the specified
# name, version and architecture is installed in the system.
#
# Parameters:
# -Name........: The package name.
# -Version.....: The package version.
# -Architecture: The package architecture ("x86", "x64").
#
# Returns:
# $true if the AppX package is installed in the system,
# $false otherwise.
function IsAppxPackageInstalled() {
param (
[Parameter(Mandatory=$true)] [string]$name,
[Parameter(Mandatory=$true)] [string]$version,
[Parameter(Mandatory=$true)] [string]$architecture
)
if ($architecture -match "neutral") {
$architecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture
}
$packages = Get-AppxPackage "$($name)*" -ErrorAction Stop | Where-Object {
$_.Name.ToLower() -eq $name.ToLower() -and
$_.Version.ToLower() -eq $version.ToLower() -and
"$($_.Architecture)".ToLower() -eq $architecture.ToLower()
}
return ($packages.Count -gt 0)
}
<#
===========================================================================================
| |
| Main |
| |
===========================================================================================
#>
[System.Console]::Title = "Automated AppX Package Installer - by Elektro"
[CultureInfo]::CurrentUICulture = "en-US"
try { Set-ExecutionPolicy -ExecutionPolicy "Unrestricted" -Scope "Process" } catch { }
# Hides the progress-bar for 'Add-AppxPackage' cmdlet in this script.
# Thanks to @Santiago Squarzon for the tip: https://stackoverflow.com/questions/75716867
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.3#progresspreference
$ProgressPreference = "Ignore"
Do {
Clear-Host
Write-Output ""
Write-Output " $($host.ui.RawUI.WindowTitle)"
Write-Output " +=====================================================+"
Write-Output " | |"
Write-Output " | This script will find any AppX package files |"
Write-Output " | within the current directory (without recursion), |"
Write-Output " | print info about the package, print a message |"
Write-Output " | in case of the package is already installed, |"
Write-Output " | and install the package in case of it is |"
Write-Output " | not installed, handling errors during installation. |"
Write-Output " | |"
Write-Output " +=====================================================+"
Write-Output ""
Write-Host " -Continue? (Y/N)"
$key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
$char = $key.Character.ToString().ToUpper()
if ($char -ne "Y" -and $char -ne "N") {
[console]::beep(1500, 500)
}
} while ($char -ne "Y" -and $char -ne "N")
if ($char -eq "N") {Exit(1)} else {Clear-Host}
$appxFiles = Get-ChildItem -LiteralPath "$PSScriptRoot" -Filter "*.*" -File | Where-Object { $_.Extension -match "appx" -or $_.Extension -match "appxbundle" -or $_.Extension -match "msixbundle"}
if ($appxFiles.Count -eq 0) {
Write-Warning "No Appx files were found in the current directory."
Write-Host ""
$LastExitCode = 2
} else {
$appxFiles | ForEach-Object {
# The AppX package string. It can be a file name (with or without extension), or a full path.
$packageString = $_.FullName
$packageInfo = Get-AppXPackageInfo -PackageString $packageString
$nameVerArch = "$($packageInfo.Name) v$($packageInfo.Version) ($($packageInfo.Architecture))"
Write-Host "Package Info.:" -ForegroundColor Gray
Write-Host ($packageInfo | Format-List | Out-String).Trim() -ForegroundColor DarkGray
Write-Host ""
$isInstalled = IsAppxPackageInstalled -Name $packageInfo.Name -Version $packageInfo.Version -Architecture $packageInfo.Architecture
if ($isInstalled) {
Write-Warning "Package $nameVerArch is already installed."
Write-Warning "Installation is not required. Ignoring..."
} else {
Write-Host "Package $nameVerArch is ready to install."
Write-Host "Installing package..."
try {
Add-AppxPackage -Path "$($_.FullName)" -ErrorAction Stop
Write-Host "Package $nameVerArch has been successfully installed." -BackgroundColor Black -ForegroundColor Green
} catch {
Write-Host "Error installing package $nameVerArch" -BackgroundColor Black -ForegroundColor Red
Write-Host ""
Write-Error -Message ($_.Exception | Format-List * -Force | Out-String)
$LastExitCode = 1
Break
}
}
Write-Host ""
$LastExitCode = 0
}
}
Write-Host "Program will terminate now with exit code $LastExitCode. Press any key to exit..."
$key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Exit($LastExitCode)