Whenever I reinstall my Windows laptop (Or a new management VM), I have a lot of apps, modules, RSAT Tools, Sysinternals tools, and settings that need to be installed or configured. So… Let’s automate that 🙂
Below are my installation scripts which you can use to install everything, or just the programs or the PowerShell modules. Options are there to do so and you can easily add/remove things in the JSON file and let it run again. Just put the three files in one location, run install_apps.cmd as Administrator and lean back…
Shortcuts for newly installed software will be removed from your desktop, hate those ;), and programs that start automatically after installation will be closed. Be aware of the fact that your machine might restart during installation. Save your work, and you can re-run the script to continue installing the rest (It will check and skip things that are already installed)
Install_Apps.cmd
@echo off TITLE Installing Apps, Features and PowerShell Modules... echo Your computer might restart, please save your work before continuing! echo After restart, you can restart this script to finish installation pause powershell -executionpolicy bypass -noprofile -file "%~dp0install_apps.ps1" -All echo Logging is available in %Temp%\Install.log echo Press any to update installed apps or CTRL-C to exit and then reboot your system manually pause winget upgrade --all --silent --force echo. echo Done updating, press any key to quit and reboot your system manually pause
Install_Apps.ps1
<# .SYNOPSIS Installs Programs, Modules, Features and settings using Winget and PowerShell .DESCRIPTION Installs/Configures all options from the .json file .PARAMETER All Install all Programs, Modules, Features and Settings .PARAMETER Apps Install all Software .PARAMETER Features Install all Windows Features .PARAMETER MicrosftVCRuntime Install all Microsoft VC++ Runtimes .PARAMETER PowerShellCommands Execute all PowerShell commands like Update-Help .PARAMETER PowerShellModules Install all PowerShell Modules .PARAMETER PowerShellModulesUpdate Update all installed PowerShell Modules to the latest version .PARAMETER PowerShellProfile Update the PowerShell Profile .PARAMETER RSATTools Install the Windows Remote System Administration Tools .PARAMETER $SCCMTools Install the System Center Configuration Manager tools like CMTrace .PARAMETER SysInternalsSuite Install the Windows Remote System Administration Tools .PARAMETER IntuneWinAppUtil Install the IntuneWinAppUtil in c:\windows\system32 .INPUTS Defaults to -All if no other Parameters are specified .OUTPUTS Screen output and TransAction log which is available in %Temp%\Install.log .EXAMPLE PS> Install_Apps.ps1 -Apps Installs all Applications .EXAMPLE PS> Install_Apps.ps1 -SCCMTools -PowerShellModule Installs the System Center Configuration Manager Tools and installs all PowerShell Modules .LINK None #> #Parameters [CmdletBinding(DefaultParameterSetName = "All")] param ( [Parameter(Mandatory = $False, HelpMessage = "Install all Software, Modules, Features and Settings", ParameterSetName = "All")][Switch]$All, [Parameter(Mandatory = $false, HelpMessage = "Install all Software", ParameterSetName = "Optional")][Switch]$Apps, [Parameter(Mandatory = $false, HelpMessage = "Install all Windows Features", ParameterSetName = "Optional")][Switch]$Features, [Parameter(Mandatory = $false, HelpMessage = "Install all Microsoft VC++ Runtimes", ParameterSetName = "Optional")][Switch]$MicrosftVCRuntime, [Parameter(Mandatory = $false, HelpMessage = "Execute all PowerShell commands like Update-Help", ParameterSetName = "Optional")][Switch]$PowerShellCommands, [Parameter(Mandatory = $false, HelpMessage = "Install all PowerShell Modules", ParameterSetName = "Optional")][Switch]$PowerShellModules, [Parameter(Mandatory = $false, HelpMessage = "Update all installed PowerShell Modules to the latest version", ParameterSetName = "Optional")][Switch]$PowerShellModulesUpdate, [Parameter(Mandatory = $false, HelpMessage = "Update the PowerShell Profile", ParameterSetName = "Optional")][Switch]$PowerShellProfile, [Parameter(Mandatory = $false, HelpMessage = "Install the Windows Remote System Administration Tools", ParameterSetName = "Optional")][Switch]$RSATTools, [Parameter(Mandatory = $false, HelpMessage = "Install the System Center Configuration Manager tools like CMTrace", ParameterSetName = "Optional")][Switch]$SCCMTools, [Parameter(Mandatory = $false, HelpMessage = "Install all SysInternals Suite tools and add them to the system path", ParameterSetName = "Optional")][Switch]$SysInternalsSuite, [Parameter(Mandatory = $false, HelpMessage = "Install the IntuneWinAppUtil to c:\windows\system32", ParameterSetName = "Optional")][Switch]$IntuneWinAppUtil ) if ($PSCmdlet.ParameterSetName -eq 'All') { Write-Host ("No parameter was specified and using all options") -ForegroundColor Green $All = $True } #Requires -RunAsAdministrator #Start Transcript logging in Temp folder Start-Transcript $ENV:TEMP\install.log #Set-Executionpolicy and no prompting Set-ExecutionPolicy Bypass -Force:$True -Confirm:$false -ErrorAction SilentlyContinue Set-Variable -Name 'ConfirmPreference' -Value 'None' -Scope Global #Change invoke-webrequest progress bar to hidden for faster downloads $ProgressPreference = 'SilentlyContinue' #Import list of apps, features and modules that can be installed using json file $json = Get-Content "$($PSScriptRoot)\Install_apps.json" | ConvertFrom-Json #Check if Winget is installed, if not install it by installing VCLibs (Prerequisite) followed by Winget itself if ($Apps -or $MicrosftVCRuntime -or $All) { if (!(Get-AppxPackage -Name Microsoft.Winget.Source)) { Write-Host ("Winget was not found and installing now") -ForegroundColor Yellow Invoke-Webrequest -uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -Outfile $ENV:TEMP\Microsoft.VCLibs.x64.14.00.Desktop.appx Invoke-Webrequest -uri https://aka.ms/getwinget -Outfile $ENV:TEMP\winget.msixbundle Add-AppxPackage $ENV:TEMP\Microsoft.VCLibs.x64.14.00.Desktop.appx -ErrorAction SilentlyContinue Add-AppxPackage -Path $ENV:TEMP\winget.msixbundle -ErrorAction SilentlyContinue } } if ($MicrosftVCRuntime -or $All) { #Install Microsoft Visual C++ Runtimes using WinGet Write-Host ("Installing Microsoft Visual C++ Runtime versions but skipping install if already present") -ForegroundColor Green $CurrentVC = Get-WmiObject -Class Win32_Product -Filter "Name LIKE '%Visual C++%'" -ErrorAction SilentlyContinue | Select-Object Name Foreach ($App in $json.MicrosftVCRuntime) { Write-Host ("Checking if {0} is already installed..." -f $App) if (!($CurrentVC | Select-String $App.split('+')[2].SubString(0, 4) | Select-String $App.split('-')[1])) { Write-Host ("{0} was not found and installing now" -f $App) -ForegroundColor Yellow winget.exe install $App --silent --force --source winget --accept-package-agreements --accept-source-agreements } } } if ($Apps -or $All) { #Install applications using WinGet Write-Host ("Installing Applications but skipping install if already present") -ForegroundColor Green Foreach ($App in $json.Apps) { Write-Host ("Checking if {0} is already installed..." -f $App) winget.exe list --id $App --accept-source-agreements | Out-Null if ($LASTEXITCODE -eq '-1978335212') { Write-Host ("{0} was not found and installing now" -f $App.Split('.')[1]) -ForegroundColor Yellow winget.exe install $App --silent --force --source winget --accept-package-agreements --accept-source-agreements Foreach ($Application in $json.ProcessesToKill) { get-process $Application -ErrorAction SilentlyContinue | Stop-Process -Force:$True -Confirm:$false } } } #Clean-up downloaded Winget Packages Remove-Item $ENV:TEMP\Winget -Recurse -Force:$True -ErrorAction:SilentlyContinue #Cleanup shortcuts from installed applications Foreach ($File in $json.filestoclean) { Write-Host ("Cleaning {0} from personal ad public Windows Desktop" -f $File) -ForegroundColor Green $UserDesktop = ([Environment]::GetFolderPath("Desktop")) Get-ChildItem C:\users\public\Desktop\$File -ErrorAction SilentlyContinue | Where-Object LastWriteDate -LE ((Get-Date).AddHours( - 1)) | Remove-Item -Force:$True Get-ChildItem $UserDesktop\$File -ErrorAction SilentlyContinue | Where-Object LastWriteDate -LE ((Get-Date).AddHours( - 1)) | Remove-Item -Force:$True Get-ChildItem C:\users\public\Desktop\$File -Hidden -ErrorAction SilentlyContinue | Where-Object LastWriteDate -LE ((Get-Date).AddHours( - 1)) | Remove-Item -Force:$True Get-ChildItem $UserDesktop\$File -Hidden -ErrorAction SilentlyContinue | Where-Object LastWriteDate -LE ((Get-Date).AddHours( - 1)) | Remove-Item -Force:$True } } if ($SCCMTools -or $All) { #Download and install System Center 2012 R2 Configuration Manager Toolkit for CMTRACE tool Write-Host ("Checking if System Center 2012 R2 Configuration Manager Toolkit is already installed") -ForegroundColor Green if (!(Test-Path 'C:\Program Files (x86)\ConfigMgr 2012 Toolkit R2')) { Write-Host ("SCCM 2012 R2 Toolkit was not found and installing now") -ForegroundColor Yellow Invoke-Webrequest -uri https://download.microsoft.com/download/5/0/8/508918E1-3627-4383-B7D8-AA07B3490D21/ConfigMgrTools.msi -UseBasicParsing -Outfile $ENV:TEMP\ConfigMgrTools.msi msiexec.exe /i $ENV:TEMP\ConfigMgrTools.msi /qn } } if ($SysInternalsSuite -or $All) { #Download and extract SysInternals Suite and add to system path Write-Host ("Checking if SysInternals Suite is present") -ForegroundColor Green if (!(Test-Path 'C:\Program Files (x86)\SysInterals Suite')) { Write-Host ("SysInternalsSuite was not found and installing now") -ForegroundColor Yellow Invoke-Webrequest -uri https://download.sysinternals.com/files/SysinternalsSuite.zip -Outfile $ENV:TEMP\SysInternalsSuite.zip Expand-Archive -LiteralPath $ENV:TEMP\SysInternalsSuite.zip -DestinationPath 'C:\Program Files (x86)\SysInterals Suite' $OldPath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path $NewPath = $OldPath + ';C:\Program Files (x86)\SysInterals Suite\' Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $NewPath } } if ($IntuneWinAppUtil -or $All) { #Download IntuneWinAppUtil to c:\windows\system32 Write-Host ("Checking if IntuneWinAppUtil Suite is present") -ForegroundColor Green if (!(Test-Path 'c:\windows\system32\IntuneWinAppUtil.exe')) { Write-Host ("IntuneWinAppUtil was not found and installing now") -ForegroundColor Yellow Invoke-Webrequest -uri https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool/raw/master/IntuneWinAppUtil.exe -Outfile c:\windows\system32\intunewinapputil.exe } } if ($Features -or $All) { #Install Features Write-Host ("Installing Features but skipping install if already present") -ForegroundColor Green Foreach ($Feature in $json.Features) { Write-Host ("Checking if {0} is already installed..." -f $Feature) if ((Get-WindowsOptionalFeature -Online -FeatureName:$Feature).State -ne 'Enabled') { Write-Host ("{0} was not found and installing now" -f $Feature) -ForegroundColor Yellow Enable-WindowsOptionalFeature -Online -FeatureName:$Feature -NoRestart:$True -ErrorAction SilentlyContinue | Out-Null } } } if ($PowerShellModules -or $All) { #Install PowerShell Modules Write-Host ("Installing Modules but skipping install if already present") -ForegroundColor Green [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Set-PSRepository PSGallery -InstallationPolicy Trusted Foreach ($Module in $json.PowerShellModules) { Write-Host ("Checking if the {0} is already installed..." -f $Module) if (!(Get-Module $Module -ListAvailable)) { Write-Host ("{0} PowerShell Module was not found and installing now" -f $Module) -ForegroundColor Yellow Install-Module -Name $Module -Scope AllUsers -Force:$True -AllowClobber:$True } } } if ($RSATTools -or $All) { #Install selected RSAT Tools Write-Host ("Installing RSAT components but skipping install if already present") -ForegroundColor Green Foreach ($Tool in $json.RSATTools) { Write-Host ("Checking if {0} is already installed..." -f $Tool.Split('~')[0]) if ((Get-WindowsCapability -Online -Name:$Tool).State -ne 'Installed') { Write-Host ("{0} was not found and installing now" -f $Tool.Split('~')[0]) -ForegroundColor Yellow DISM.exe /Online /add-capability /CapabilityName:$Tool /NoRestart /Quiet | Out-Null } } } if ($PowerShellProfile -or $All) { #Add settings to PowerShell Profile (Creating Profile if not exist) Write-Host ("Adding settings to PowerShell Profile but skipping setting if already present") -ForegroundColor Green Foreach ($Setting in $json.PowerShellProfile) { Write-Host ("Checking if {0} is already added..." -f $Setting) if (!(Test-Path $profile)) { New-Item -Path $profile -ItemType:File -Force:$True | out-null } if (!(Get-Content $profile | Select-String -Pattern $Setting -SimpleMatch)) { Write-Host ("{0} was not found and adding now" -f $Setting) -ForegroundColor Yellow Add-Content $profile "`n$($Setting)" } } } if ($PowerShellModulesUpdate -or $All) { #Update PowerShell Modules if needed Write-Host ("Checking for older versions of PowerShell Modules and removing those if present") -ForegroundColor Green Set-PSRepository PSGallery -InstallationPolicy Trusted Foreach ($Module in Get-InstalledModule | Select-Object Name) { Write-Host ("Checking for older versions of the {0} PowerShell Module" -f $Module.Name) $AllVersions = Get-InstalledModule -Name $Module.Name -AllVersions -ErrorAction:SilentlyContinue $AllVersions = $AllVersions | Sort-Object PublishedDate -Descending $MostRecentVersion = $AllVersions[0].Version if ($AllVersions.Count -gt 1 ) { Foreach ($Version in $AllVersions) { if ($Version.Version -ne $MostRecentVersion) { Write-Host ("Uninstalling previous version {0} of Module {1}" -f $Version.Version, $Module.Name) -ForegroundColor Yellow Uninstall-Module -Name $Module.Name -RequiredVersion $Version.Version -Force:$True } } } } } if ($PowerShellCommands -or $All) { #Run PowerShell commandline options Write-Host ("Running Commandline options and this could take a while") -ForegroundColor Green Foreach ($Command in $json.PowerShellCommands) { Write-Host ("Running {0}" -f $Command) -ForegroundColor Yellow Powershell.exe -Executionpolicy Bypass -Command $Command } } #Stop Transcript logging Stop-Transcript
Install_Apps.json
{ "Apps": [ "Microsoft.SQLServerManagementStudio", "7zip.7zip", "Adobe.Acrobat.Reader.64-bit", "AnyConnect", "BraveSoftware.BraveBrowser", "Canonical.Ubuntu", "Cisco.CiscoWebexMeetings", "Citrix.Workspace", "Famatech.AdvancedIPScanner", "Fortinet.FortiClientVPN", "Git.Git", "GitHub.GitHubDesktop", "Google.Chrome", "Graphviz.Graphviz", "Insecure.Nmap", "JAMSoftware.TreeSize.Free", "Microsoft.AzureCLI", "Microsoft.AzureStorageExplorer", "Microsoft.DeploymentToolkit", "Microsoft.dotnetRuntime.3.1", "Microsoft.dotnetRuntime.5", "Microsoft.EdgeWebView2Runtime", "Microsoft.PowerBI", "Microsoft.Powershell", "Microsoft.PowerToys", "Microsoft.RemoteDesktopClient", "Microsoft.VisualStudioCode", "Microsoft.WindowsAdminCenter", "Microsoft.WindowsADK", "Microsoft.WindowsTerminal", "Mozilla.FireFox", "Notepad++.Notepad++", "Obsidian.Obsidian", "Ookla.Speedtest", "OpenVPNTechnologies.OpenVPNConnect", "Oracle.JavaRuntimeEnvironment", "PuTTY.PuTTY", "Python.Python.3", "Rufus.Rufus", "Teamviewer.Teamviewer", "Telerik.Fiddler", "WinSCP.WinSCP", "WiresharkFoundation.Wireshark", "Zoom.Zoom" ], "Features": [ "Containers-DisposableClientVM", "Microsoft-Windows-Subsystem-Linux", "TelnetClient", "TFTP" ], "FilesToClean": [ "*.lnk", "desktop.ini" ], "MicrosftVCRuntime": [ "Microsoft.VC++2008Redist-x86", "Microsoft.VC++2008Redist-x64", "Microsoft.VC++2010Redist-x64", "Microsoft.VC++2010Redist-x86", "Microsoft.VC++2012Redist-x64", "Microsoft.VC++2012Redist-x86", "Microsoft.VC++2013Redist-x64", "Microsoft.VC++2013Redist-x86" ], "ProcessesToKill": [ "advanced_ip_scanner", "OpenVPNConnect", "PowerToys.Settings", "PtSrv", "PtSrv" ], "PowerShellCommands": [ "Update-Help -Confirm:$False -Force:$True -ErrorAction:SilentlyContinue" ], "PowerShellModules": [ "AIPService", "Az", "Azure.AnalysisServices", "Azure.Storage", "AzureAD", "AzureADPreview", "AzureRM", "AZViz", "CredentialManager", "ExchangeOnlineManagement", "IntuneBackupAndRestore", "IntuneDocumentation", "IntuneWin32App", "M365Documentation", "Microsoft.Graph", "Microsoft.Online.SharePoint.PowerShell", "Microsoft.PowerApps.Administration.Powershell", "Microsoft.PowerShell.Crescendo", "Microsoft.PowerShell.SecretManagement", "Microsoft.PowerShell.SecretStore", "Microsoft.RDInfra.RDPowershell", "MicrosoftTeams", "MSAL.PS", "MSCommerce", "MSGraph", "MSGraphFunctions", "MSOnline", "oh-my-posh", "ORCA", "PackageManagement", "PnP.PowerShell", "Posh-Git", "PowerShellGet", "Powershell-yaml", "ps2exe", "PSFramework", "PSGraph", "PSParseHTML", "PSReadLine", "PSSlack", "PSWindowsUpdate", "PSWord", "PSWriteWord", "SharePointPnPPowerShellOnline", "SQLServer", "Terminal-Icons", "VMware.CloudServices", "VMware.DeployAutomation", "VMware.ImageBuilder", "VMware.PowerCLI", "VMware.Sdk.Runtime", "VMware.Sdk.vSphere", "VMware.Vim", "VMware.VumAutomation", "WindowsAutoPilotIntune", "WindowsConsoleFonts" ], "PowerShellProfile": [ "Import-Module Posh-Git", "Set-PSReadlineKeyHandler -Key Tab -Function Complete", "Set-PSReadLineOption -PredictionSource History", "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12" ], "RSATTools": [ "Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0", "Rsat.BitLocker.Recovery.Tools~~~~0.0.1.0", "Rsat.CertificateServices.Tools~~~~0.0.1.0", "Rsat.DHCP.Tools~~~~0.0.1.0", "Rsat.Dns.Tools~~~~0.0.1.0", "Rsat.FailoverCluster.Management.Tools~~~~0.0.1.0", "Rsat.FileServices.Tools~~~~0.0.1.0", "Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0", "Rsat.IPAM.Client.Tools~~~~0.0.1.0", "Rsat.LLDP.Tools~~~~0.0.1.0", "Rsat.NetworkController.Tools~~~~0.0.1.0", "Rsat.NetworkLoadBalancing.Tools~~~~0.0.1.0", "Rsat.RemoteAccess.Management.Tools~~~~0.0.1.0", "Rsat.RemoteDesktop.Services.Tools~~~~0.0.1.0", "Rsat.ServerManager.Tools~~~~0.0.1.0", "Rsat.Shielded.VM.Tools~~~~0.0.1.0", "Rsat.StorageMigrationService.Management.Tools~~~~0.0.1.0", "Rsat.StorageReplica.Tools~~~~0.0.1.0", "Rsat.SystemInsights.Management.Tools~~~~0.0.1.0", "Rsat.VolumeActivation.Tools~~~~0.0.1.0", "Rsat.WSUS.Tools~~~~0.0.1.0" ] }
The output
When running the script, it will report progress like this:

Download the script(s) from GitHub here
Pingback: PowerShell is fun :) Using Run-in-Sandbox for testing scripts and Intune packages