Skip to content

Instantly share code, notes, and snippets.

@eplord
Forked from LM1LC3N7/Readme.md
Created April 25, 2024 18:39
Show Gist options
  • Save eplord/dc0b6c5c165e6a204d7df01156337c2a to your computer and use it in GitHub Desktop.
Save eplord/dc0b6c5c165e6a204d7df01156337c2a to your computer and use it in GitHub Desktop.
Automating application install on a fresh Windows 10/11 using Microsoft Store and winget

FASIC

Fast and Automatic Software Install and Configuration (on Windows)

Need

Install my common apps on Windows as fast as possible and mostly automatically, after a reimaging or reset of my OS. Also change some settings on Windows, like disabling the bing search in the start menu.

How it works?

This PowerShell script uses Microsoft Store and winget to download and install a list of application, configured at the top of the file.

Microsoft store should be privilegied for apps with a real good desktop version, like PowerToys, VScode, etc. Avoid lights versions specific to the store, like VLC. That way, those apps will be automatically updated in the backend.

For all other apps, find them in the winget repo, for example online using https://winget.run/.

For apps that are not in either of these locations, there is a variable where URL and filenames can be listed, for a download only. You will have the opportunity to install manually these files lately.

Configuration

Update variables at the top of the file.

  • destFolderName: Configure where installers that cannot be automatically installed will be downloaded
  • DownloadOnlyAppList: Filenames and related download URLs to be gathered in the folder configured in "destFolderName"
  • storeAppListID: List of IDs and names of apps in Microsoft Store (tip, use the online web version and URL to get the info)
  • wingetAppList_base: List of winget apps to install
  • wingetAppList_poweruser: Secondary list of winget apps to install. Can be empty.

Winget: Known errors and bugs

# Last Update : 08/03/2024
# LM1LC3NT
# Script version 2.4
#
# Changelog:
# v2.x
# Minor updates
#
# v2.0
# Switching from mainly chocolatey to winget, as this tool is included to Windows 11 and seems more powerfull.
# Finaly, wingetUI allows automatic and background checks and updates. A lot better than the Microsoft Store
# than does not really update apps (or every months, not days).
#
# Execute the script NOT as admin. Elevated prompts will be generated when needed.
# Note: a folder will be created on the same path, see variable "$destFolderName" bellow.
# Command:
# powershell -ExecutionPolicy Bypass -File Setup_app_new_windows.ps1
#
# Microsoft Store vs Standard apps?
# Some apps in the MS Store are the same than their standard installer version.
# In that case, prefer to use the MS Store, as it will constantly and in background update them (in theory at least).
# In other cases, UWP app are just lighter or PWA versions, so way less interesing / performant than
# their standard alternative versions (for example, VLC).
#
#
# Automating software updates using winget, without WingetUI:
# winget update --all
#
# OTHER TOOLS NICE TO HAVE, but can't apply it now in one file script
# Context Menu for Hash Calculation with PS: https://www.tenforums.com/tutorials/78681-add-file-hash-context-menu-windows-8-10-a.html
# -----
# CONFIGURATION
# -----
### MANUAL APP TO INSTALL : Download to folder
$destFolderName = "_Manual_Install_Softwares"
$DownloadOnlyAppList = @(
@("Outline-Client.exe", "https://s3.amazonaws.com/outline-releases/client/windows/stable/Outline-Client.exe") # OutlineVPN Client
)
### APPLICATION INSTALLED FROM MICROSOFT STORE
# Get ID from Microsoft Store Online (check URL). Example: https://apps.microsoft.com/store/detail/powershell/9MZ1SNWT0N5D
$storeAppListID = @(
"9NVN9SZ8KFD7", # LibreWolf
"9N0DX20HK701", # Terminal
"xp8k0hkjfrxgck", # Oh My Posh
"XP89DCGQ3K6VLD", # PowerToys
"9MZ1SNWT0N5D", # PowerShell
"9NBLGGH516XP", # EarTrumpet
"XP9KHM4BK9FZ7Q", # VSCode
"XP8C9QZMS2PC1T", # Brave Browser
"9P781RW2VM6G", # WSL Tray Monitor
"9NDGGX7M2H0V" # WSL Toolbox
)
$wingetAppList_base = @(
"SomePythonThings.WingetUIStore",
"Notepad++.Notepad++",
"mcmilk.7zip-zstd", # (alternative avec plus de lib de compression, sinon " 7zip.7zip")
"NextDNS.NextDNS.Desktop",
"Mobatek.MobaXterm",
"VideoLAN.VLC",
"Foxit.FoxitReader",
"JGraph.Draw",
"undergroundwires.privacy.sexy", # enforce privacy & security best-practices on Windows
"TeamSophia.SophiApp" # open source tweaker for fine-tuning of W10 + W11
)
$wingetAppList_poweruser = @(
"Microsoft.VisualStudioCode",
"ShareX.ShareX", # (ou FastStone.Capture)
"FilesCommunity.Files",
"Git.Git",
"Safing.Portmaster",
"Microsoft.Powershell",
"JanDeDobbeleer.OhMyPosh"
)
# -------------------------------------------------------------
# -----
# FUNCTIONS
# -----
function print.err {
Param(
[Parameter()]
[String]
$Message
)
Write-Host -ForegroundColor Red "[ERROR] $Message"
}
function print.warn {
Param(
[Parameter()]
[String]
$Message
)
Write-Host -ForegroundColor Yellow "$Message"
}
function print.success {
Param(
[Parameter()]
[String]
$Message
)
Write-Host -ForegroundColor Green "$Message"
}
function print.info {
Param(
[Parameter()]
[String]
$Message
)
Write-Host "$Message"
}
function wingetInstallFromListFromMsstore($list, $existingApps){
foreach ($app in $list) {
# Check if app exist or install it
if (!($existingApps | findstr $app)) {
print.info "Installing $app from Microsoft Store..."
try { winget install --exact --id $app --source msstore --silent --accept-package-agreements --accept-package-agreements }
catch { print.err "Error: $_" }
} else {
print.info "$app is already installed"
}
}
print.info
}
function wingetInstallFromListFromWinget($list, $existingApps){
foreach ($app in $list) {
# Check if app exist or install it
if (!($existingApps | findstr $app)) {
print.info "Installing $app from Winget..."
try { winget install --exact --id $app --source winget --silent --accept-source-agreements --accept-package-agreements }
catch { print.err "Error: $_" }
} else {
print.info "$app is already installed"
}
}
print.info
}
# -------------------------------------------------------------
# -----
# CHECK DEPENCIES
# -----
# Check for winget
# https://github.com/microsoft/winget-cli/releases/
$wingetPath = Get-Command -Name winget
if ($wingetPath) {
print.success "[OK] Winget is installed."
} else {
print.err "Winget is not installed."
print.info "Download and install the last release from GitHub or azure:"
print.info "https://github.com/microsoft/winget-cli/releases/ or https://winget.azureedge.net/cache/source.msix"
print.info
Exit-PSSession
}
print.info
# Check for gsudo
# This will be used then to install apps using admin rights when they need to
$sudoPath = Get-Command -Name gsudo
if ($sudoPath) {
print.success "[OK] gsudo is installed."
} else {
print.warn "gsudo is not installed."
print.info "Starting installation using winget..."
winget install -e --id gerardog.gsudo --silent --accept-source-agreements --accept-package-agreements
# Test error
if ($?) {
print.warn "gsudo installed, you must open a new shell and restart the script to continue."
} else {
print.err "gsudo installation error."
}
print.info
Exit-PSSession
}
print.info
# Refreshing installed local packages list
print.success "[INFO] Refreshing list of installed packages."
$existingApps = winget list
print.info
# Ask once to elevate
print.success "[INFO] Temporary caching admin privileges."
gsudo cache on
print.info
# Update winget cache and solve problems
# known issue: https://github.com/microsoft/winget-cli/issues/2686
print.success "[INFO] Updating winget source"
Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.Winget.Source_8wekyb3d8bbwe
winget source reset
winget source update
#winget source update --name winget
# Other solution: https://github.com/microsoft/winget-cli/issues/3652#issuecomment-1956699129
# winget install -s msstore --id 9NBLGGH4NNS1
print.info
# -------------------------------------------------------------
# -----
# Export bitlocker key
# -----
# Check if bitlocker is enabled and export recovery key on the desktop
print.success "[INFO] Checking bitlocker for C:"
$blinfo = sudo { Get-BitlockerVolume -MountPoint "C:" }
$BLexportPath = "$env:USERPROFILE\Desktop"
if($blinfo.ProtectionStatus -eq 1){
print.info "Exporting Bitlocker key for disk C: to $BLexportPath\bitlocker_recovery.txt"
(sudo { Get-BitLockerVolume -MountPoint C}).KeyProtector.KeyProtectorId >> $BLexportPath\bitlocker_recovery.txt
(sudo { Get-BitLockerVolume -MountPoint C}).KeyProtector.recoverypassword >> $BLexportPath\bitlocker_recovery.txt
# Test error
if ($?) {
print.success "[OK] Bitlocker recovery key for disk C: exported to $BLexportPath\bitlocker_recovery.txt."
print.info
} else {
print.err "Error while exporting the bitlocker recovery key for disk C: to $BLexportPath\bitlocker_recovery.txt."
print.info
}
} else {
print.warn "Bitlocker not detected for disk C:"
print.info
}
# -------------------------------------------------------------
# -----
# Disable Cortana + Bing in start menu
# -----
# Source: https://theohbrothers.com/posts/disable-bing-search-and-cortana-on-windows-11/
print.success "[INFO] Disabling Bing search in start menu and disabling cortana globally."
#######################
# Disable Bing Search #
#######################
if (!(sudo Get-Item -Path 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer' -ErrorAction SilentlyContinue)) {
sudo New-Item -Path 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer'
}
if (!(sudo Get-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer' -ErrorAction SilentlyContinue)) {
sudo New-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer'
}
sudo New-ItemProperty -Path 'HKCU:\Software\Policies\Microsoft\Windows\Explorer' -Name 'DisableSearchBoxSuggestions' -Value 1 -PropertyType DWORD -Force
sudo New-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer' -Name 'DisableSearchBoxSuggestions' -Value 1 -PropertyType DWORD -Force
# 1903 and later, applies to Microsoft and local user accounts
# Apply to current MS account
if (!(sudo Get-Item -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -ErrorAction SilentlyContinue)) {
sudo New-Item Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search'
}
sudo New-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'BingSearchEnabled' -Value 0 -PropertyType DWORD -Force
# Apply to all MS accounts (Does not work)
if (!(sudo Get-Item -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -ErrorAction SilentlyContinue)) {
sudo New-Item -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search'
}
sudo New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search' -Name 'BingSearchEnabled' -Value 0 -PropertyType DWORD -Force
print.info
# ----------------------------------------------------------
# --------------Disable Cortana in start menu---------------
# ----------------------------------------------------------
# Source: Privacy.sexy
echo --- Disable Cortana in start menu
sudo { reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Search" /v "CortanaEnabled" /t "REG_DWORD" /d "0" /f }
sudo { reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" /v "CortanaEnabled" /t "REG_DWORD" /d "0" /f }
# ----------------------------------------------------------
# ----------------------------------------------------------
# ----------Disable web results in Windows Search-----------
# ----------------------------------------------------------
echo --- Disable web results in Windows Search
sudo { reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v "ConnectedSearchUseWeb" /t "REG_DWORD" /d "0" /f }
sudo { reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\Windows Search" /v "ConnectedSearchUseWebOverMeteredConnections" /t "REG_DWORD" /d "0" /f }
# ----------------------------------------------------------
# -------------------------------------------------------------
# -----
# Downloading apps manually
# -----
print.success "[INFO] Downloading apps that can't be automatically installed using winget or Microsoft Store."
$destinationFolder = $PSScriptRoot + "\" + $destFolderName + "\"
$validUrlPattern = "^(https?|ftp)://[^\s/$.?#].[^\s]*$"
# Folder creation
print.info "Creating folder to download installers: $destinationFolder"
New-Item -Path $destinationFolder -ItemType Directory -Force -ErrorAction SilentlyContinue > $null
# DL files
foreach ($row in $DownloadOnlyAppList) {
$url = $row[1]
$filename = $row[0]
$destinationPath = $destinationFolder + $filename
if ($url -match $validUrlPattern) {
print.info "Downloading $filename from $url ..."
try { (New-Object System.Net.WebClient).DownloadFile($url, $destinationPath) }
catch { print.err "Error: $_" }
} else {
print.err "Invalid URL: $url"
}
}
print.info
# -------------------------------------------------------------
# -----
# Installing & Activating Hyper-V
# -----
print.success "[INFO] Installing and activating optional feature: Microsoft-Hyper-V"
$featureInstalled = sudo { Get-WmiObject -query "select * from win32_optionalfeature where installstate= 1 and name = 'Microsoft-Hyper-V'" }
If (-not $featureInstalled) {
# Feature not installed, installing
# Hyper-V
sudo dism /online /enable-feature /all /featurename:Microsoft-Hyper-V /norestart
# Test error
if (!$?) {
print.err "Error: $_"
print.info
}
sudo dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
# Test error
if ($?) {
print.success "[OK] Hyper-V installed"
} else {
print.err "Error: $_"
}
} else {
# Feature already installed
print.info "Feature is already installed."
}
print.info
# -------------------------------------------------------------
# -----
# Updating and installing Microsoft Store apps
# -----
# Updating Microsoft Store apps
# Source: https://github.com/microsoft/winget-cli/issues/2854#issuecomment-1435369342
print.success "[INFO] Updating apps from Microsoft Store (using winget)"
sudo { Get-CimInstance -Namespace "Root\cimv2\mdm\dmmap" -ClassName "MDM_EnterpriseModernAppManagement_AppManagement01" | Invoke-CimMethod -MethodName UpdateScanMethod }
# Installing Microsoft Store apps
print.success "[INFO] Installing apps from Microsoft Store (using winget)"
wingetInstallFromListFromMsstore $storeAppListID $existingApps
# -------------------------------------------------------------
# -----
# Installing apps with winget
# -----
print.success "[INFO] Installing winget apps using list: wingetAppList_base"
wingetInstallFromListFromWinget $wingetAppList_base $existingApps
print.info
print.success "[INFO] Installing winget apps using list: wingetAppList_poweruser"
wingetInstallFromListFromWinget $wingetAppList_poweruser $existingApps
print.info
# Update all
# Source: https://github.com/microsoft/winget-cli/issues/2854#issuecomment-1386272357
print.success "[INFO] Updating all local packages using winget."
try { winget upgrade --all --include-unknown }
catch { print.err "Error: $_" }
print.info
# -------------------------------------------------------------
# -----
# Installing Sandbox
# -----
print.success "[INFO] Installing and activating Windows Sandbox."
dism /online /enable-feature /featurename:"Containers-DisposableClientVM" /all /norestart
print.info
# -------------------------------------------------------------
# -----
# Installing WSL
# -----
print.success "[INFO] Installing and activating WSL 2."
# Activate WSL
## * https://docs.microsoft.com/fr-fr/windows/wsl/install-win10)
## * https://support.rstudio.com/hc/en-us/articles/360049776974-Using-RStudio-Server-in-Windows-WSL2
# Manual installation of WSL 2.
# Sometimes this does not work with an elevated CMD, it will prompt again a password admin
# Which is a problem sometimes, when using "BeyondTrust" for example
# wsl --install --no-launch
# alternative that works well:
## WSL 2
sudo dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
# Test error
if (!$?) {
print.err "Error: $_"
}
print.info
## restart
## update kernel : https://docs.microsoft.com/fr-fr/windows/wsl/wsl2-kernel
## Install ubuntu from winstore and run it to install
## Source: https://learn.microsoft.com/en-us/windows/wsl/install-manual
print.success "[INFO] Installing wsl 2 with default Linux distribution"
wsl --set-default-version 2
# Test error
if (!$?) {
print.err "Error: $_"
print.info
}
print.info
print.success "[INFO] Downloading and installing Ubuntu 22.04 LTS"
wsl --install --distribution Ubuntu-22.04
# Test error
if (!$?) {
print.warn "[ERROR] A reboot is probably needed. After a reboot, run the following command to install a WSL distro."
print.info "wsl --install --distribution Ubuntu-22.04"
print.err "Error: $_"
print.info
}
print.info
# -------------------------------------------------------------
# -----
# OTHER: Configuring long path, intializing git, installing oh my posh and fonts,
# configuring explorer to show extensions by default
# -----
print.success "[INFO] Configuring long path & intialising git "
# Long path
sudo Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1
print.info
# Git init
print.success "[INFO] Setting up git config globaly"
$name = Read-Host "What is your name :"
$email = Read-Host "What is your email :"
sudo git config --global user.name $name
sudo git config --global user.email $email
print.info
# Installing oh-my-posh
# Source: https://ohmyposh.dev/docs/installation/windows
print.success "[INFO] Insatalling Oh-my-posh and setting it up as default Powershell config."
Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://ohmyposh.dev/install.ps1'))
# Add prompt on powershell startup for current user and admin
Add-Content -Path $PROFILE "`noh-my-posh init pwsh --config ""$env:POSH_THEMES_PATH\jandedobbeleer.omp.json"" | Invoke-Expression"
sudo Add-Content -Path $PROFILE "`noh-my-posh init pwsh --config ""$env:POSH_THEMES_PATH\jandedobbeleer.omp.json"" | Invoke-Expression"
print.info
# Installing fonts using oh-my-posh in a new powershell session (to reload env and use oh-my-posh)
print.success "[INFO] Insatalling fonts globally using Oh-my-posh."
print.info "Installing font 1/4"
start powershell { sudo oh-my-posh font install RobotoMono }
print.info "Installing font 2/4"
start powershell { sudo oh-my-posh font install Hack }
print.info "Installing font 3/4"
start powershell { sudo oh-my-posh font install UbuntuMono }
print.info "Installing font 4/4"
start powershell { sudo oh-my-posh font install Meslo }
# Configuring explorer to show file extensions by default
$key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced'
Set-ItemProperty $key Hidden 1
Set-ItemProperty $key HideFileExt 0
Set-ItemProperty $key ShowSuperHidden 1
Stop-Process -processname explorer
# -------------------------------------------------------------
# stop sudo cache
sudo cache off
print.info
print.success "Script end!"
exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment