|
# Last Update : 28/08/2024 |
|
# w00dbury inspired by LM1LC3NT |
|
|
|
# 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 = @( |
|
@("nvm-setup.exe", "https://github.com/coreybutler/nvm-windows/releases/download/1.1.12/nvm-setup.exe") |
|
@("go1.23.0.windows-amd64.msi", "https://go.dev/dl/go1.23.0.windows-amd64.msi") |
|
) |
|
|
|
### APPLICATION INSTALLED FROM MICROSOFT STORE |
|
# Get ID from Microsoft Store Online (check URL). Example: https://apps.microsoft.com/store/detail/powershell/9MZ1SNWT0N5D |
|
$storeAppListID = @( |
|
"9N0DX20HK701", # Terminal |
|
"XP89DCGQ3K6VLD", # PowerToys |
|
"9P7KNL5RWT25", # Sysinternals Suite |
|
"XP9KHM4BK9FZ7Q", # VSCode |
|
"xp99c9g0krdz27", # 1Password |
|
"9n2t6f9f5zdn", # Nightingale REST Client |
|
"9np355qt2sqb", # Azure VPN Client |
|
"xpffh613w8v6lv" # OBS Studio |
|
) |
|
|
|
$wingetAppList_base = @( |
|
"7zip.7zip", |
|
"NickeManarin.ScreenToGif" |
|
) |
|
|
|
$wingetAppList_poweruser = @( |
|
"ShareX.ShareX", |
|
"Git.Git", |
|
"GitHub.GitHubDesktop", |
|
"Oracle.VirtualBox", |
|
"Hashicorp.Vagrant", |
|
"suse.RancherDesktop", |
|
"1password-cli" |
|
) |
|
|
|
$vsCodeExtList = @( |
|
"ms-playwright.playwright", |
|
"salesforce.salesforcedx-vscode-apex", |
|
"GraphQL.vscode-graphql-syntax", |
|
"mongodb.mongodb-vscode", |
|
"GitLab.gitlab-workflow", |
|
"pflannery.vscode-versionlens", |
|
"AmazonWebServices.aws-toolkit-vscode", |
|
"GitHub.vscode-github-actions", |
|
"ms-edgedevtools.vscode-edge-devtools", |
|
"usernamehw.errorlens", |
|
"bradlc.vscode-tailwindcss", |
|
"naumovs.color-highlight", |
|
"mhutchie.git-graph", |
|
"EditorConfig.EditorConfig", |
|
"ms-vscode.PowerShell", |
|
"golang.Go", |
|
"redhat.vscode-yaml", |
|
"formulahendry.auto-rename-tag", |
|
"ms-dotnettools.csharp", |
|
"eamodio.gitlens", |
|
"ms-azuretools.vscode-docker", |
|
"dbaeumer.vscode-eslint", |
|
"VisualStudioExptTeam.vscodeintellicode", |
|
"esbenp.prettier-vscode", |
|
"ritwickdey.LiveServer", |
|
"GitHub.copilot", |
|
"dotenv.dotenv-vscode", |
|
"1Password.op-vscode" |
|
) |
|
|
|
|
|
# ------------------------------------------------------------- |
|
|
|
|
|
# ----- |
|
# 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 |
|
} |
|
|
|
function codeInstallFromList($list){ |
|
foreach ($app in $list) { |
|
print.info "Installing $app in VSCode..." |
|
try { code --install-extension $app } |
|
catch { print.err "Error: $_" } |
|
} |
|
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 |
|
# TODO Install winget + restart script |
|
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." |
|
} else { |
|
print.err "gsudo installation error, exiting" |
|
print.info |
|
Exit 1 |
|
} |
|
|
|
} |
|
# Reload Path |
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") |
|
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 |
|
|
|
# ------------------------------------------------------------- |
|
|
|
|
|
# ----- |
|
# 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 |
|
|
|
|
|
# ------------------------------------------------------------- |
|
|
|
# ----- |
|
# 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 VS Code extensions |
|
# ----- |
|
|
|
|
|
print.success "[INFO] Installing VS Code extensions using list: vsCodeExtList" |
|
codeInstallFromList $vsCodeExtList |
|
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 |
|
|
|
# Create the profile file and set the script policy to signed |
|
if (-not (Test-Path -Path $PROFILE)) { |
|
# Create the directory for the profile if it doesn't exist |
|
$profileDir = Split-Path -Path $PROFILE |
|
if (-not (Test-Path -Path $profileDir)) { |
|
New-Item -Path $profileDir -ItemType Directory -Force |
|
} |
|
|
|
# Create an empty profile script file |
|
New-Item -Path $PROFILE -ItemType File -Force |
|
|
|
Write-Host "Profile file created at $PROFILE" |
|
} else { |
|
Write-Host "Profile file already exists at $PROFILE" |
|
} |
|
# Set the execution policy to RemoteSigned for the current user |
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser |
|
|
|
|
|
# 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 |