Skip to content

Instantly share code, notes, and snippets.

@RandallFlagg
Last active July 13, 2025 01:22
Show Gist options
  • Save RandallFlagg/ce2b68b2c49da210c27eafb2ff48f180 to your computer and use it in GitHub Desktop.
Save RandallFlagg/ce2b68b2c49da210c27eafb2ff48f180 to your computer and use it in GitHub Desktop.
Winget
<#
.SYNOPSIS
A PowerShell script to export or import installed applications using the Windows Package Manager (winget).
.DESCRIPTION
This script allows you to either export a list of currently installed winget packages to a JSON file
or import a list from a JSON file to install them. It includes validation, comprehensive logging,
and detailed reporting on successful and failed installations.
.PARAMETER Action
Specifies the operation to perform. Must be either 'Export' or 'Import'.
.PARAMETER FilePath
Specifies the full path to the JSON file to be used for the export or import operation.
.PARAMETER LogPath
Optional. Specifies the directory where the transcript and result logs will be saved.
Defaults to a 'logs' folder in the script's directory.
.PARAMETER Usage
Displays the help information and usage examples for the script.
.EXAMPLE
# Show usage instructions
.\YourScriptName.ps1 -Usage
.EXAMPLE
# Exports installed packages to a file on the Desktop, with logs saved to the default location.
.\YourScriptName.ps1 -Action Export -FilePath "$env:USERPROFILE\Desktop\winget_packages.json"
.EXAMPLE
# Imports packages and saves logs to a custom path.
.\YourScriptName.ps1 -Action Import -FilePath "C:\temp\packages.json" -LogPath "C:\temp\winget_logs"
#>
param(
[Parameter(Mandatory = $false, HelpMessage = "Specify 'Export' to save the package list or 'Import' to install from a list.")]
[string]$Action,
[Parameter(Mandatory = $false, HelpMessage = "Enter the full path for the JSON file.")]
[string]$FilePath,
[Parameter(Mandatory = $false, HelpMessage = "Directory to store log files.")]
[string]$LogPath = (Join-Path $PSScriptRoot "logs"),
[Parameter(Mandatory = $false, HelpMessage = "Display the script's usage instructions.")]
[switch]$Usage
)
# Get the script's own filename dynamically at the global scope
$scriptName = $MyInvocation.MyCommand.Name
# Function to display usage information, replacing Get-Help for better portability
function Show-Usage {
Write-Host "`n.SYNOPSIS" -ForegroundColor Cyan
Write-Host " A PowerShell script to export or import installed applications using the Windows Package Manager (winget)."
Write-Host "`n.DESCRIPTION" -ForegroundColor Cyan
Write-Host " This script allows you to either export a list of currently installed winget packages to a JSON file"
Write-Host " or import a list from a JSON file to install them. It includes validation, comprehensive logging,"
Write-Host " and detailed reporting on successful and failed installations."
Write-Host "`n.PARAMETER Action" -ForegroundColor Cyan
Write-Host " Specifies the operation to perform. Must be either 'Export' or 'Import'."
Write-Host "`n.PARAMETER FilePath" -ForegroundColor Cyan
Write-Host " Specifies the full path to the JSON file to be used for the export or import operation."
Write-Host "`n.PARAMETER LogPath" -ForegroundColor Cyan
Write-Host " Optional. Specifies the directory where the transcript and result logs will be saved."
Write-Host " Defaults to a 'logs' folder in the script's directory."
Write-Host "`n.PARAMETER Usage" -ForegroundColor Cyan
Write-Host " Displays the help information and usage examples for the script."
Write-Host "`n.EXAMPLE" -ForegroundColor Cyan
Write-Host " # Show usage instructions"
Write-Host " .$scriptName -Usage"
Write-Host "`n.EXAMPLE" -ForegroundColor Cyan
Write-Host " # Exports installed packages to a file on the Desktop, with logs saved to the default location."
Write-Host " .$scriptName -Action Export -FilePath `"$env:USERPROFILE\Desktop\winget_packages.json`""
Write-Host "`n.EXAMPLE" -ForegroundColor Cyan
Write-Host " # Imports packages and saves logs to a custom path."
Write-Host " .$scriptName -Action Import -FilePath `"$env:USERPROFILE\Desktop\winget_packages.json`" -LogPath `"$env:USERPROFILE\Desktop\winget_logs`""
Write-Host ""
}
# If -Usage switch is used, or mandatory parameters are missing, or Action is invalid, show help and exit.
if ($Usage -or (-not $Action) -or (-not $FilePath) -or ($Action -ne 'Export' -and $Action -ne 'Import')) {
Show-Usage # Call the custom usage function
return
}
# --- Main Function ---
function Invoke-WingetAction {
# Create the log directory if it doesn't exist
if (-not (Test-Path -Path $LogPath)) {
New-Item -Path $LogPath -ItemType Directory -Force | Out-Null
}
# Define log file paths
$transcriptLog = Join-Path $LogPath "Transcript-$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log"
$successLog = Join-Path $LogPath "Success.log"
$failureLog = Join-Path $LogPath "Failure.log"
# Start logging all console output
Start-Transcript -Path $transcriptLog
try {
Write-Host "Starting winget '$Action' operation..." -ForegroundColor Green
Write-Host "Full transcript will be saved to: $transcriptLog"
switch ($Action) {
'Export' {
$Directory = Split-Path -Path $FilePath -Parent
# Validate that the parent directory exists
if (-not (Test-Path -Path $Directory)) {
throw "Error: The directory '$Directory' does not exist. Please create it or provide a valid path."
}
# Validate that FilePath includes a filename (i.e., is not just a directory)
if ([string]::IsNullOrEmpty((Split-Path -Path $FilePath -Leaf))) {
throw "Error: When exporting, the FilePath must include a filename (e.g., 'C:\path\to\packages.json'), not just a directory."
}
# New check: If the FilePath already exists, ensure it's a file, not a directory.
if (Test-Path -Path $FilePath -PathType Container) {
throw "Error: The provided FilePath '$FilePath' exists and is a directory. Please provide a path to a file."
}
Write-Host "Exporting installed packages to: $FilePath"
# Execute the winget export command. We do NOT include versions to ensure import gets the latest.
winget export --source winget -o $FilePath
}
'Import' {
if (-not (Test-Path -Path $FilePath -PathType Leaf)) {
throw "Error: The file '$FilePath' was not found. Please provide a valid file path."
}
Write-Host "Importing packages from: $FilePath"
Write-Host "Results will be logged to:"
Write-Host " - Success: $successLog"
Write-Host " - Failure: $failureLog"
# Clear previous result logs
Clear-Content $successLog, $failureLog -ErrorAction SilentlyContinue
# Execute the winget import command and capture all output (standard and error)
# The --ignore-versions flag ensures winget fetches the latest version of each package.
$importOutput = winget import -i $FilePath --ignore-versions --accept-package-agreements --accept-source-agreements 2>&1
# Parse the output to determine success and failures
$packages = (Get-Content $FilePath | ConvertFrom-Json).Packages.PackageIdentifier
foreach ($package in $packages) {
# Check for success message related to the package
if ($importOutput -match "Successfully installed.*$package" -or $importOutput -match "$package\s+Already installed") {
"$package" | Add-Content -Path $successLog
}
else {
# Find the specific error for the failed package
$errorLine = $importOutput | Select-String -Pattern "$package" -Context 0, 2 | Select-String -Pattern "Installer failed|No applicable installer found|error"
"$package - Reason: $($errorLine | Out-String)" | Add-Content -Path $failureLog
}
}
Write-Host "`n--- Import Summary ---"
Write-Host "Successful packages logged to $successLog"
Get-Content $successLog -ErrorAction SilentlyContinue
Write-Host "`nFailed packages logged to $failureLog"
Get-Content $failureLog -ErrorAction SilentlyContinue
Write-Host "----------------------`n"
}
}
Write-Host "Winget '$Action' operation completed." -ForegroundColor Green
}
catch {
# Catch any terminating errors from the script or winget process
Write-Error "A critical error occurred during the '$Action' operation: $_"
}
finally {
# Ensure the transcript is always stopped
Stop-Transcript
}
}
# Run the main function
Invoke-WingetAction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment