mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 10:19:36 -06:00
c57e7ebdfe
Refactors the application download logic to correctly handle packages from the 'msstore' source, which does not support architecture specification. - The download process no longer passes an architecture parameter for msstore apps. - The architecture selection dropdown in the UI is now disabled for msstore apps to prevent invalid configurations. - Imported or searched msstore apps will display 'NA' for their architecture.
480 lines
22 KiB
PowerShell
480 lines
22 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Provides functions for interacting with WinGet and the Microsoft Store to find, download, and configure applications.
|
|
|
|
.DESCRIPTION
|
|
This module contains a set of functions designed to automate application management using the WinGet package manager and the Microsoft Store.
|
|
It supports checking for and installing WinGet, downloading applications, handling different application types (Win32 and UWP), and generating silent installation commands for Win32 applications.
|
|
This module is used by both the build script (BuildFFUVM.ps1) and the UI (BuildFFUVM_UI.ps1) to manage application downloads and configuration.
|
|
#>
|
|
function Get-Application {
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppName,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppId,
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet('winget', 'msstore')]
|
|
[string]$Source,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppsPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WindowsArch,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OrchestrationPath
|
|
)
|
|
|
|
# Block Company Portal from winget source
|
|
# I refuse to code around the poor packaging of this app
|
|
if ($AppId -eq 'Microsoft.CompanyPortal' -and $Source -eq 'winget') {
|
|
WriteLog "Skipping download of Company Portal from the 'winget' source. This version has packaging inconsistencies. Please use the 'msstore' source instead."
|
|
return 4 # Return specific error code for this case
|
|
}
|
|
|
|
# Determine base folder path for checking existence
|
|
$appIsWin32ForCheck = ($Source -eq 'msstore' -and $AppId.StartsWith("XP"))
|
|
$appBaseFolderPathForCheck = ""
|
|
if ($Source -eq 'winget' -or $appIsWin32ForCheck) {
|
|
$appBaseFolderPathForCheck = Join-Path -Path "$AppsPath\Win32" -ChildPath $AppName
|
|
}
|
|
else {
|
|
$appBaseFolderPathForCheck = Join-Path -Path "$AppsPath\MSStore" -ChildPath $AppName
|
|
}
|
|
|
|
# Check if the app (any architecture) has already been downloaded by checking for its content folder.
|
|
# This prevents re-downloading if BuildFFUVM.ps1 is run after downloading via the UI.
|
|
if (Test-Path -Path $appBaseFolderPathForCheck -PathType Container) {
|
|
# Check if the folder is not empty.
|
|
if (Get-ChildItem -Path $appBaseFolderPathForCheck -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1) {
|
|
WriteLog "Application '$AppName' appears to be already downloaded as content exists in '$appBaseFolderPathForCheck'. Skipping download."
|
|
return 0 # Success, already present
|
|
}
|
|
}
|
|
|
|
# Validate app exists in repository
|
|
$wingetSearchResult = Find-WinGetPackage -id $AppId -MatchOption Equals -Source $Source
|
|
if (-not $wingetSearchResult) {
|
|
if ($VerbosePreference -ne 'Continue') {
|
|
Write-Error "$AppName not found in $Source repository."
|
|
Write-Error "Check the AppList.json file and make sure the AppID is correct."
|
|
}
|
|
WriteLog "$AppName not found in $Source repository."
|
|
WriteLog "Check the AppList.json file and make sure the AppID is correct."
|
|
return 1 # Return error code
|
|
}
|
|
|
|
# Determine architectures to download
|
|
$architecturesToDownload = if ($WindowsArch -eq 'x86 x64') { @('x86', 'x64') } else { @($WindowsArch) }
|
|
$overallResult = 0
|
|
|
|
# For msstore, we don't specify architecture, so we only need to loop once.
|
|
if ($Source -eq 'msstore') {
|
|
$architecturesToDownload = @('neutral') # Use a placeholder to loop once
|
|
}
|
|
|
|
foreach ($arch in $architecturesToDownload) {
|
|
if ($Source -eq 'msstore') {
|
|
WriteLog "Processing '$AppName' for all architectures."
|
|
}
|
|
else {
|
|
WriteLog "Processing '$AppName' for architecture '$arch'."
|
|
}
|
|
|
|
# Determine app type and folder path
|
|
$appIsWin32 = ($Source -eq 'msstore' -and $AppId.StartsWith("XP"))
|
|
if ($Source -eq 'winget' -or $appIsWin32) {
|
|
$appBaseFolderPath = Join-Path -Path "$AppsPath\Win32" -ChildPath $AppName
|
|
}
|
|
else {
|
|
$appBaseFolderPath = Join-Path -Path "$AppsPath\MSStore" -ChildPath $AppName
|
|
}
|
|
|
|
# If downloading multiple archs for a Win32 app, create a subfolder
|
|
$appFolderPath = $appBaseFolderPath
|
|
$subFolderForCommand = $null
|
|
if ($architecturesToDownload.Count -gt 1 -and ($Source -eq 'winget' -or $appIsWin32)) {
|
|
$appFolderPath = Join-Path -Path $appBaseFolderPath -ChildPath $arch
|
|
$subFolderForCommand = $arch
|
|
}
|
|
|
|
# Create app folder
|
|
New-Item -Path $appFolderPath -ItemType Directory -Force | Out-Null
|
|
|
|
# Build download parameters and log information
|
|
$downloadParams = @{
|
|
id = $AppId
|
|
DownloadDirectory = $appFolderPath
|
|
Source = $Source
|
|
}
|
|
|
|
if ($Source -ne 'msstore') {
|
|
$downloadParams.Architecture = $arch
|
|
WriteLog "Downloading $AppName for $arch architecture..."
|
|
WriteLog "WinGet command: Export-WinGetPackage -id $AppId -DownloadDirectory `"$appFolderPath`" -Architecture $arch -Source $Source"
|
|
}
|
|
else {
|
|
WriteLog "Downloading $AppName for all architectures..."
|
|
WriteLog 'MSStore app downloads require authentication with an Entra ID account. You may be prompted twice for credentials, once for the app and another for the license file.'
|
|
WriteLog "WinGet command: Export-WinGetPackage -id $AppId -DownloadDirectory `"$appFolderPath`" -Source $Source"
|
|
}
|
|
|
|
# Download the app
|
|
$wingetDownloadResult = Export-WinGetPackage @downloadParams
|
|
|
|
# Handle download status
|
|
if ($wingetDownloadResult.status -ne 'Ok') {
|
|
# For winget source, try downloading without architecture if the specified one fails
|
|
if (($Source -eq 'winget') -and ($wingetDownloadResult.status -eq 'NoApplicableInstallers' -or $wingetDownloadResult.status -eq 'NoApplicableInstallerFound')) {
|
|
WriteLog "No installer found for $arch architecture. Attempting to download without specifying architecture..."
|
|
# Remove the architecture parameter and try again
|
|
$downloadParams.Remove('Architecture')
|
|
$wingetDownloadResult = Export-WinGetPackage @downloadParams
|
|
}
|
|
|
|
# Re-evaluate status after potential second attempt
|
|
if ($wingetDownloadResult.status -ne 'Ok') {
|
|
# Handle Store-specific publisher restriction error
|
|
if ($Source -eq 'msstore' -and $wingetDownloadResult.ExtendedErrorCode -match '0x8A150084') {
|
|
$errorMessage = "The Microsoft Store app $AppName does not support downloads by the publisher. Please remove it from the AppList.json. If there's a winget source version of the application, try using that instead. Exiting."
|
|
WriteLog $errorMessage
|
|
Remove-Item -Path $appFolderPath -Recurse -Force
|
|
Write-Error $errorMessage
|
|
return 3 # Return specific error code for publisher restriction
|
|
}
|
|
# Handle other download failures
|
|
else {
|
|
$errormsg = "Download failed for $AppName with status: $($wingetDownloadResult.status) $($wingetDownloadResult.ExtendedErrorCode)"
|
|
WriteLog $errormsg
|
|
Remove-Item -Path $appFolderPath -Recurse -Force
|
|
Write-Error $errormsg
|
|
return 1 # Return generic error code
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "Downloaded $AppName without specifying architecture."
|
|
}
|
|
}
|
|
|
|
WriteLog "$AppName ($arch) downloaded to $appFolderPath"
|
|
|
|
# Handle zip files
|
|
$zipFile = Get-ChildItem -Path $appFolderPath -Filter "*.zip" -File -ErrorAction SilentlyContinue
|
|
if ($zipFile) {
|
|
WriteLog "Found zip file: $($zipFile.FullName). Extracting..."
|
|
Expand-Archive -Path $zipFile.FullName -DestinationPath $appFolderPath -Force
|
|
WriteLog "Extraction complete. Removing zip file."
|
|
Remove-Item -Path $zipFile.FullName -Force
|
|
WriteLog "Zip file removed."
|
|
}
|
|
|
|
# Handle winget source apps that have appx, appxbundle, msix, or msixbundle extensions but were downloaded to the Win32 folder
|
|
$installerFiles = Get-ChildItem -Path "$appFolderPath\*" -Exclude "*.yaml", "*.xml" -File -ErrorAction SilentlyContinue
|
|
$uwpExtensions = @(".appx", ".appxbundle", ".msix", ".msixbundle")
|
|
$isUwpApp = $false
|
|
if ($installerFiles) {
|
|
foreach ($file in $installerFiles) {
|
|
if ($uwpExtensions -contains $file.Extension) {
|
|
$isUwpApp = $true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($isUwpApp -and $appFolderPath -match 'Win32') {
|
|
# Handle UWP apps
|
|
$NewAppPath = "$AppsPath\MSStore\$AppName"
|
|
WriteLog "$AppName is a UWP app. Moving to $NewAppPath"
|
|
WriteLog "Creating $NewAppPath"
|
|
New-Item -Path "$AppsPath\MSStore\$AppName" -ItemType Directory -Force | Out-Null
|
|
WriteLog "Moving $AppName to $NewAppPath"
|
|
Move-Item -Path "$appFolderPath\*" -Destination "$AppsPath\MSStore\$AppName" -Force
|
|
WriteLog "Removing $appFolderPath"
|
|
Remove-Item -Path $appFolderPath -Force -Recurse
|
|
WriteLog "$AppName moved to $NewAppPath"
|
|
$result = 0 # Success for UWP app
|
|
}
|
|
# If app is in Win32 folder, add the silent install command to the WinGetWin32Apps.json file
|
|
elseif ($appFolderPath -match 'Win32') {
|
|
WriteLog "$AppName is a Win32 app. Adding silent install command to $OrchestrationPath\WinGetWin32Apps.json"
|
|
$result = Add-Win32SilentInstallCommand -AppFolder $AppName -AppFolderPath $appFolderPath -OrchestrationPath $OrchestrationPath -SubFolder $subFolderForCommand
|
|
}
|
|
else {
|
|
# For any other case, set result to 0 (success)
|
|
$result = 0
|
|
}
|
|
|
|
if ($result -ne 0) { $overallResult = $result }
|
|
|
|
# Handle MSStore specific post-processing
|
|
if ($Source -eq 'msstore' -and $appFolderPath -match 'MSStore') {
|
|
# Handle ARM64-specific dependencies
|
|
if ($arch -eq 'ARM64') {
|
|
WriteLog 'Windows architecture is ARM64. Removing dependencies that are not ARM64.'
|
|
$dependencies = Get-ChildItem -Path "$appFolderPath\Dependencies" -ErrorAction SilentlyContinue
|
|
if ($dependencies) {
|
|
foreach ($dependency in $dependencies) {
|
|
if ($dependency.Name -notmatch 'ARM64') {
|
|
WriteLog "Removing dependency file $($dependency.FullName)"
|
|
Remove-Item -Path $dependency.FullName -Recurse -Force
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Clean up multiple versions (keep only the latest)
|
|
WriteLog "$AppName has completed downloading. Identifying the latest version of $AppName."
|
|
$packages = Get-ChildItem -Path "$appFolderPath\*" -Exclude "Dependencies\*", "*.xml", "*.yaml" -File -ErrorAction Stop
|
|
|
|
# Find latest version based on signature date
|
|
$latestPackage = $packages | Sort-Object { (Get-AuthenticodeSignature $_.FullName).SignerCertificate.NotBefore } -Descending | Select-Object -First 1
|
|
|
|
# Remove older versions
|
|
WriteLog "Latest version of $AppName has been identified as $latestPackage. Removing old versions of $AppName that may have downloaded."
|
|
foreach ($package in $packages) {
|
|
if ($package.FullName -ne $latestPackage.FullName) {
|
|
try {
|
|
WriteLog "Removing $($package.FullName)"
|
|
Remove-Item -Path $package.FullName -Force
|
|
}
|
|
catch {
|
|
WriteLog "Failed to delete: $($package.FullName) - $_"
|
|
throw $_
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} # End foreach ($arch in $architecturesToDownload)
|
|
|
|
return $overallResult
|
|
}
|
|
function Get-Apps {
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppList,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$AppsPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WindowsArch,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OrchestrationPath
|
|
)
|
|
|
|
# Load and validate app list
|
|
$apps = Get-Content -Path $AppList -Raw | ConvertFrom-Json
|
|
if (-not $apps) {
|
|
WriteLog "No apps were specified in AppList.json file."
|
|
return
|
|
}
|
|
|
|
# Process WinGet apps
|
|
$wingetApps = $apps.apps | Where-Object { $_.source -eq "winget" }
|
|
if ($wingetApps) {
|
|
WriteLog 'Winget apps to be installed:'
|
|
$wingetApps | ForEach-Object { WriteLog $_.Name }
|
|
}
|
|
|
|
# Process Store apps
|
|
$StoreApps = $apps.apps | Where-Object { $_.source -eq "msstore" }
|
|
if ($StoreApps) {
|
|
WriteLog 'Store apps to be installed:'
|
|
$StoreApps | ForEach-Object { WriteLog $_.Name }
|
|
}
|
|
|
|
# Ensure WinGet is available
|
|
Confirm-WinGetInstallation -WindowsArch $WindowsArch
|
|
|
|
# Create necessary folders
|
|
$win32Folder = Join-Path -Path $AppsPath -ChildPath "Win32"
|
|
$storeAppsFolder = Join-Path -Path $AppsPath -ChildPath "MSStore"
|
|
|
|
# Process WinGet apps
|
|
if ($wingetApps) {
|
|
if (-not (Test-Path -Path $win32Folder -PathType Container)) {
|
|
WriteLog "Creating folder for Winget Win32 apps: $win32Folder"
|
|
New-Item -Path $win32Folder -ItemType Directory -Force | Out-Null
|
|
WriteLog "Folder created successfully."
|
|
}
|
|
|
|
foreach ($wingetApp in $wingetApps) {
|
|
try {
|
|
$appArch = if ($wingetApp.PSObject.Properties['architecture']) { $wingetApp.architecture } else { $WindowsArch }
|
|
Get-Application -AppName $wingetApp.Name -AppId $wingetApp.Id -Source 'winget' -AppsPath $AppsPath -WindowsArch $appArch -OrchestrationPath $OrchestrationPath
|
|
}
|
|
catch {
|
|
WriteLog "Error occurred while processing $($wingetApp.Name): $_"
|
|
throw $_
|
|
}
|
|
}
|
|
}
|
|
|
|
# Process Store apps
|
|
if ($StoreApps) {
|
|
if (-not (Test-Path -Path $storeAppsFolder -PathType Container)) {
|
|
New-Item -Path $storeAppsFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
foreach ($storeApp in $StoreApps) {
|
|
try {
|
|
$appArch = if ($storeApp.PSObject.Properties['architecture']) { $storeApp.architecture } else { $WindowsArch }
|
|
Get-Application -AppName $storeApp.Name -AppId $storeApp.Id -Source 'msstore' -AppsPath $AppsPath -WindowsArch $appArch -OrchestrationPath $OrchestrationPath
|
|
}
|
|
catch {
|
|
WriteLog "Error occurred while processing $($storeApp.Name): $_"
|
|
throw $_
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function Install-WinGet {
|
|
param (
|
|
[string]$Architecture
|
|
)
|
|
$packages = @(
|
|
@{Name = "VCLibs"; Url = "https://aka.ms/Microsoft.VCLibs.$Architecture.14.00.Desktop.appx"; File = "Microsoft.VCLibs.$Architecture.14.00.Desktop.appx" },
|
|
@{Name = "UIXaml"; Url = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.$Architecture.appx"; File = "Microsoft.UI.Xaml.2.8.$Architecture.appx" },
|
|
@{Name = "WinGet"; Url = "https://aka.ms/getwinget"; File = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" }
|
|
)
|
|
foreach ($package in $packages) {
|
|
$destination = Join-Path -Path $env:TEMP -ChildPath $package.File
|
|
WriteLog "Downloading $($package.Name) from $($package.Url) to $destination"
|
|
Start-BitsTransferWithRetry -Source $package.Url -Destination $destination
|
|
WriteLog "Installing $($package.Name)..."
|
|
# Don't show progress bar for Add-AppxPackage - there's a weird issue where the progress stays on the screen after the apps are installed
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
Add-AppxPackage -Path $destination -ErrorAction SilentlyContinue
|
|
# Set progress preference back to default
|
|
$ProgressPreference = 'Continue'
|
|
WriteLog "Removing $($package.Name)..."
|
|
Remove-Item -Path $destination -Force -ErrorAction SilentlyContinue
|
|
}
|
|
WriteLog "WinGet installation complete."
|
|
}
|
|
function Confirm-WinGetInstallation {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$WindowsArch
|
|
)
|
|
|
|
WriteLog 'Checking if WinGet is installed...'
|
|
$minVersion = [version]"1.8.1911"
|
|
|
|
# Check WinGet PowerShell module
|
|
$wingetModule = Get-InstalledModule -Name Microsoft.Winget.Client -ErrorAction SilentlyContinue
|
|
$wingetModuleVersion = [version]$wingetModule.Version
|
|
if ($wingetModuleVersion -lt $minVersion -or -not $wingetModule) {
|
|
WriteLog 'Microsoft.Winget.Client module is not installed or is an older version. Installing the latest version...'
|
|
|
|
# Handle PSGallery trust settings
|
|
$PSGalleryTrust = (Get-PSRepository -Name 'PSGallery').InstallationPolicy
|
|
if ($PSGalleryTrust -eq 'Untrusted') {
|
|
WriteLog 'Temporarily setting PSGallery as a trusted repository...'
|
|
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
|
|
}
|
|
|
|
Install-Module -Name Microsoft.Winget.Client -Force -Repository 'PSGallery'
|
|
|
|
if ($PSGalleryTrust -eq 'Untrusted') {
|
|
WriteLog 'Setting PSGallery back to untrusted repository...'
|
|
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
|
|
WriteLog 'Done'
|
|
}
|
|
}
|
|
else {
|
|
WriteLog "Installed Microsoft.Winget.Client module version: $($wingetModule.Version)"
|
|
}
|
|
|
|
# Check WinGet CLI
|
|
$wingetVersion = Get-WinGetVersion
|
|
if (-not $wingetVersion) {
|
|
WriteLog "WinGet is not installed. Installing WinGet..."
|
|
Install-WinGet -Architecture $WindowsArch
|
|
}
|
|
elseif ($wingetVersion -match 'v?(\d+\.\d+\.\d+)' -and [version]$matches[1] -lt $minVersion) {
|
|
WriteLog "The installed version of WinGet $($matches[1]) does not support downloading MSStore apps. Installing the latest version of WinGet..."
|
|
Install-WinGet -Architecture $WindowsArch
|
|
}
|
|
else {
|
|
WriteLog "Installed WinGet version: $wingetVersion"
|
|
}
|
|
}
|
|
function Add-Win32SilentInstallCommand {
|
|
param (
|
|
[string]$AppFolder,
|
|
[string]$AppFolderPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OrchestrationPath,
|
|
[string]$SubFolder
|
|
)
|
|
$appName = $AppFolder
|
|
$installerPath = Get-ChildItem -Path "$appFolderPath\*" -Include "*.exe", "*.msi" -File -ErrorAction Stop
|
|
if (-not $installerPath) {
|
|
WriteLog "No win32 app installers were found. Skipping the inclusion of $AppFolder"
|
|
Remove-Item -Path $AppFolderPath -Recurse -Force
|
|
return 1
|
|
}
|
|
$yamlFile = Get-ChildItem -Path "$appFolderPath\*" -Include "*.yaml" -File -ErrorAction Stop
|
|
$yamlContent = Get-Content -Path $yamlFile -Raw
|
|
$silentInstallSwitch = [regex]::Match($yamlContent, 'Silent:\s*(.+)').Groups[1].Value.Replace("'", "").Trim()
|
|
if (-not $silentInstallSwitch) {
|
|
WriteLog "Silent install switch for $appName could not be found. Skipping the inclusion of $appName."
|
|
Remove-Item -Path $appFolderPath -Recurse -Force
|
|
return 2
|
|
}
|
|
$installer = Split-Path -Path $installerPath -Leaf
|
|
|
|
$basePath = "D:\win32\$AppFolder"
|
|
if (-not [string]::IsNullOrEmpty($SubFolder)) {
|
|
$basePath = "$basePath\$SubFolder"
|
|
}
|
|
|
|
if ($installerPath.Extension -eq ".exe") {
|
|
$silentInstallCommand = "$basePath\$installer"
|
|
}
|
|
elseif ($installerPath.Extension -eq ".msi") {
|
|
$silentInstallCommand = "msiexec"
|
|
$silentInstallSwitch = "/i `"$basePath\$installer`" $silentInstallSwitch"
|
|
}
|
|
|
|
# Path to the JSON file
|
|
$wingetWin32AppsJson = "$OrchestrationPath\WinGetWin32Apps.json"
|
|
|
|
# Initialize or load existing JSON data
|
|
if (Test-Path -Path $wingetWin32AppsJson) {
|
|
[array]$appsData = Get-Content -Path $wingetWin32AppsJson -Raw | ConvertFrom-Json
|
|
|
|
# Get highest priority value
|
|
if ($appsData.Count -gt 0) {
|
|
$highestPriority = $appsData.Count + 1
|
|
}
|
|
}
|
|
else {
|
|
$appsData = @()
|
|
$highestPriority = 1
|
|
}
|
|
|
|
# Create new app entry
|
|
$newApp = [PSCustomObject]@{
|
|
Priority = $highestPriority
|
|
Name = if (-not [string]::IsNullOrEmpty($SubFolder)) { "$appName ($SubFolder)" } else { $appName }
|
|
CommandLine = $silentInstallCommand
|
|
Arguments = $silentInstallSwitch
|
|
}
|
|
|
|
$appsData += $newApp
|
|
$appsData | ConvertTo-Json -Depth 10 | Set-Content -Path $wingetWin32AppsJson
|
|
|
|
WriteLog "Added $($newApp.Name) to WinGetWin32Apps.json with priority $highestPriority"
|
|
|
|
# Return 0 for success
|
|
return 0
|
|
}
|
|
|
|
# --------------------------------------------------------------------------
|
|
# SECTION: Module Export
|
|
# --------------------------------------------------------------------------
|
|
|
|
# Export functions needed by both BuildFFUVM and the UI Core module
|
|
Export-ModuleMember -Function Get-Application, Get-Apps, Confirm-WinGetInstallation, Add-Win32SilentInstallCommand, Install-Winget |