Creating an install script from WinGet Export
I recently bought a new laptop, my first new personal machine since 2015, and with that comes the joy of setting up all of my utilities and applications again. My good friend Rob Sonders (Twitter|LinkedIn) has been raving about WinGet and how it has simplified his life, so I figured this was the perfect time to try it out. So far I have been really impressed with how simple it makes installing software from all kinds of different sources. I immediately recognized how easy this can be used to script the setup of new workstations, and decided to start maintaining a running list of all the applications I’ve installed with WinGet.
The challenge is that very quickly that list of applications has grown (over 40 for me already). Since I’m a firm believer in “automate all the things”, I decided to use some PowerShell to export the WinGet packages and format them as a WinGet install script that could easily be run to set up a new environment. Here is the script, and then I’ll run through the individual steps for those that may be new to PowerShell.
The Script
#Variables
$filename = "Get_WingetInstallScript_$((Get-Date).ToFileTime()).tmp"
$outputFile = "$env:TEMP\$filename"
#Do a WinGet Export to a temporary file
winget export -o $outputFile
#Read in the file to a variable and convert from JSON
$json = Get-Content $outputFile | ConvertFrom-Json
#Output the install commands
foreach ($package in $json.Sources.Packages)
{
Write-Output "winget install $($package.PackageIdentifier)"
}Step 1: Defining the Variables
#Variables
$filename = "Get_WingetInstallScript_$((Get-Date).ToFileTime()).tmp"
$outputFile = "$env:TEMP\$filename"I like to define variables in a section at the beginning of my scripts. I do this for a few reasons:
- It keeps the code cleaner as any configurable settings are right at the top instead of buried in the code
- It makes it easier if I plan to change this to a function later, as this variable section would likely become a parameters section
Here I’m defining the file name first, a combination of static text identifying the nature of the file and a file date to avoid naming conflicts. Next I define the output file which is simply the file name tacked on to the end of the users temp path.
Step 2: Call WinGet Export
winget export -o $outputFileWinGet has a helpful export function that we can use to export the list of packages. This command will output to the screen any packages that do not have sources…like Microsoft Store apps that are not available in WinGet, and will output the remaining packages to the defined output file.
Step 3: Read the file back in to a Variable
$json = Get-Content $outputFile | ConvertFrom-JsonThe next step is to read the file that we just created into a PowerShell object that we can work with. I do this with the Get-Content command to read the file in as a string, and then use ConvertFrom-Json to tell PowerShell that the data is in JSON format and to convert it to an object. I could have done this in 2 different steps, but this shows the power of piping multiple commands together.
Step 4: Output the Install Commands
foreach ($package in $json.Sources.Packages)
{
Write-Output "winget install $($package.PackageIdentifier)"
}Lastly find the collection of packages and use a foreach loop to the output a formatted string. This output can then be pasted into a command window, or saved off as a script that can then be put into source control…which is what I’m choosing to do.
Final Thoughts
This is a fairly basic script, but does exactly what I needed it to do. I could extend the script to save the content directly to a file, but I chose not to do that in this first round as I want to add comments throughout the script to document individual packages. For this reason I would rather copy and paste the output into an existing script under source control. I could also envision adding an optional switch that would include the version number of packages, in the event that an environment needs to be duplicated exactly. The last potential upgrade that comes to mind is comparison logic. This would allow me to run the script regularly and directly update the source control file to add any new packages that are added over time.
But I’ve been told that I have a tendancy for scope creep…so I’ll stick with this for now.