Skip to content

Instantly share code, notes, and snippets.

@garyo
Last active April 17, 2025 18:42
Show Gist options
  • Save garyo/3c39e294a65d666a4ba0b1f538137989 to your computer and use it in GitHub Desktop.
Save garyo/3c39e294a65d666a4ba0b1f538137989 to your computer and use it in GitHub Desktop.
Script to set up a Windows dev machine
# Script to set up a Windows 11 machine for software development
# Author: Gary Oberbrunner, [email protected]
# License: MIT
# Requires elevation
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "This script requires admin privileges. Please run PowerShell as Administrator and try again."
Exit 1
}
# Enable Developer Mode
Write-Host "Enabling Developer Mode..."
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (-NOT(Test-Path -Path $RegistryKeyPath)) {
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
Set-ItemProperty -Path $RegistryKeyPath -Name "AllowDevelopmentWithoutDevLicense" -Value 1
Set-ItemProperty -Path $RegistryKeyPath -Name "AllowAllTrustedApps" -Value 1
# Enable symlink creation without admin privileges
Write-Host "Enabling symlink creation for non-admin users..."
$symlinkKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
Set-ItemProperty -Path $symlinkKeyPath -Name "EnableLinksAcrossUserAccounts" -Value 1
# Set HOME environment variable
Write-Host "Setting HOME environment variable..."
[System.Environment]::SetEnvironmentVariable('HOME', $env:USERPROFILE, [System.EnvironmentVariableTarget]::User)
$env:HOME = $env:USERPROFILE
########################################################################
# Unlink standard folders from OneDrive, store them locally
########################################################################
# Stop OneDrive process before unlinking folders
Stop-Process -Name "OneDrive" -Force -ErrorAction SilentlyContinue
# Registry paths for Known Folders
$RegistryPaths = @(
"HKCU:\Software\Microsoft\OneDrive\Accounts\Business1\Tenants",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{018D5C66-4533-4307-9B53-224DE2ED1FE6}",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\OneDrive",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
)
# Default folder locations
$DefaultFolders = @{
"Desktop" = [Environment]::GetFolderPath("Desktop")
"Documents" = [Environment]::GetFolderPath("MyDocuments")
"Pictures" = [Environment]::GetFolderPath("MyPictures")
}
# Update Shell Folders registry keys to default locations
foreach ($Path in $RegistryPaths) {
if (Test-Path $Path) {
foreach ($Folder in $DefaultFolders.Keys) {
$DefaultLocation = $DefaultFolders[$Folder]
try {
Set-ItemProperty -Path $Path -Name $Folder -Value $DefaultLocation -ErrorAction SilentlyContinue
Write-Host "Updated $Folder to $DefaultLocation"
}
catch {
Write-Host "Could not update $Folder location: $_"
}
}
}
}
# Remove OneDrive sync relationships
$OneDrivePath = "$env:USERPROFILE\OneDrive"
if (Test-Path $OneDrivePath) {
Remove-Item -Path "$OneDrivePath\Desktop" -Force -Recurse -ErrorAction SilentlyContinue
Remove-Item -Path "$OneDrivePath\Documents" -Force -Recurse -ErrorAction SilentlyContinue
Remove-Item -Path "$OneDrivePath\Pictures" -Force -Recurse -ErrorAction SilentlyContinue
Write-Host "Removed OneDrive sync folders"
}
# Restart Explorer to apply changes
Stop-Process -Name "explorer" -Force -ErrorAction SilentlyContinue
Start-Process "explorer"
Write-Host "OneDrive folders have been unlinked. Please verify your files are in the correct locations."
########################################################################
# Install apps and utilities
########################################################################
# Helper function to run commands as the original non-admin user
# Helper function to run commands as the original non-admin user
function Invoke-AsUser {
param([string]$command)
$wrappedCommand = @"
`$ErrorActionPreference = 'Stop'
try {
$command
if (`$LASTEXITCODE) { exit `$LASTEXITCODE }
} catch {
Write-Host "Error: `$_" -ForegroundColor Red
Write-Host "Stack Trace: `$(`$_.ScriptStackTrace)" -ForegroundColor Red
pause
exit 1
}
Write-Host "Command completed successfully"
exit 0
"@
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
$processInfo.FileName = "powershell.exe"
$processInfo.Arguments = "-Command $wrappedCommand"
$processInfo.UseShellExecute = $true
$processInfo.Verb = "open"
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $processInfo
$process.Start()
$process.WaitForExit()
if ($process.ExitCode -ne 0) {
throw "Process exited with code $($process.ExitCode)"
}
return $true
}
# Install Scoop and packages as non-admin user
Write-Host "Installing Scoop and packages as regular user..."
$scoopInstallScript = @'
# Install Scoop if not already installed
if (!(Get-Command scoop -ErrorAction SilentlyContinue)) {
Write-Host "Installing Scoop..."
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
# Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
Invoke-RestMethod get.scoop.sh | Invoke-Expression
}
# Install aria2 for parallel downloads first
if (!(Get-Command aria2c -ErrorAction SilentlyContinue)) {
Write-Host "Installing aria2 for faster downloads..."
scoop install aria2
scoop config aria2-enabled true
scoop config aria2-warning-enabled false
}
# Add scoop buckets (need git first)
Write-Host "Adding Scoop buckets with git and 7zip..."
scoop install 7zip git
$buckets = @('extras', 'sysinternals', 'nerd-fonts', 'nonportable')
foreach ($bucket in $buckets) {
Write-Host "Adding $bucket bucket..."
scoop bucket add $bucket
}
# Install all packages in parallel
Write-Host "Installing all packages..."
scoop install `
googlechrome vscode vim rapidee `
cppcheck gh diffutils kdiff3 uv `
everything-np debugview procmon sysmon dependencies `
fd ripgrep eza fzf msys2 `
logseq synctrayzor
'@
Invoke-AsUser $scoopInstallScript
# Define MSYS2 paths for Scoop installation
$msys2Root = "$env:USERPROFILE\scoop\apps\msys2\current"
$msys2Shell = "$msys2Root\msys2.exe"
# Update nsswitch.conf using bash
Write-Host "Configuring nsswitch.conf..."
$bashCommand = 'sed -i "/^db_home:/c\db_home: windows" /etc/nsswitch.conf'
Start-Process -FilePath $msys2Shell -ArgumentList "-defterm", "-no-start", "-here", "-c", $bashCommand -Wait
# Configure Git line endings
Write-Host "Configuring Git line endings..."
git config --global core.autocrlf false
# Post-installation configurations
Write-Host "Applying post-installation configurations..."
# VSCode extensions and settings
Write-Host "Installing VSCode extensions..."
$vsCodeExtensions = @(
'ms-vscode.cpptools',
'eamodio.gitlens',
'GitHub.copilot',
'ms-python.python',
'rust-lang.rust-analyzer',
'vadimcn.vscode-lldb'
)
foreach ($extension in $vsCodeExtensions) {
code --install-extension $extension
}
# Configure Everything
Write-Host "Configuring Everything search..."
# Start Everything service
Start-Process "$env:USERPROFILE\scoop\shims\everything.exe" -ArgumentList "-startup"
# Wait for service to start
Start-Sleep -Seconds 2
# Configure Sysmon
Write-Host "Configuring Sysmon..."
$sysmonConfigUrl = "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml"
$sysmonConfigPath = "$env:TEMP\sysmonconfig.xml"
Invoke-WebRequest -Uri $sysmonConfigUrl -OutFile $sysmonConfigPath
Start-Process "sysmon.exe" -ArgumentList "-accepteula -i $sysmonConfigPath" -Wait
# Configure fzf
Write-Host "Configuring fzf..."
$profileContent = @"
# fzf configuration
`$env:FZF_DEFAULT_COMMAND = 'fd --type f --hidden --follow --exclude .git'
`$env:FZF_DEFAULT_OPTS = '--height 40% --layout=reverse --border'
# Set eza as default list command
Set-Alias -Name ls -Value eza -Option AllScope
Set-Alias -Name ll -Value 'eza -l' -Option AllScope
Set-Alias -Name la -Value 'eza -la' -Option AllScope
Set-Alias -Name lt -Value 'eza --tree' -Option AllScope
"@
# Create PowerShell profile directory if it doesn't exist
$profileDir = Split-Path $PROFILE -Parent
if (!(Test-Path $profileDir)) {
New-Item -Path $profileDir -ItemType Directory -Force
}
Add-Content $PROFILE $profileContent
# Install commonly needed MSYS2 packages
Write-Host "Installing common MSYS2 packages..."
Start-Process -FilePath $msys2Shell -ArgumentList "-defterm", "-no-start", "-here", "-c", "pacman -S --noconfirm mingw-w64-x86_64-toolchain base-devel" -Wait
Write-Host "Setting ssh-agent service to start manually"
Get-Service -Name ssh-agent | Set-Service -StartupType Manual
Write-Host "Setup complete! Additional notes:
1. VSCode: Log in to GitHub to activate Copilot
2. Everything: The service is now running in the background
3. Sysmon: Configured with SwiftOnSecurity's config
4. PowerShell: New aliases and fzf configuration added to profile
5. MSYS2: Basic development tools installed
Please log out and back in for all changes to take effect.
GitHub CLI: Run 'gh auth login' to authenticate."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment