Windows 11
Before you start
Run everything in an elevated terminal: Win+X → Terminal (Admin).
Allow scripts and modules to run (needed for the Windows Update module below). Bypass turns off script checks for every user on this machine; it applies to the PowerShell you run it in, so repeat it in PowerShell 7 if you use that too:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope LocalMachine -Force
Get-ExecutionPolicy -List
LocalMachine should show Bypass. If a MachinePolicy or UserPolicy row is set, Group Policy is overriding it.
Time zone
Set the time zone to US Eastern:
Set-TimeZone -Id "Eastern Standard Time"
Make sure the Windows time service runs at startup, then sync the clock now:
Set-Service -Name w32time -StartupType Automatic
Start-Service w32time
w32tm /resync /force
Check the zone and the last sync:
Get-TimeZone
w32tm /query /status
Update
Install the PSWindowsUpdate module (once per machine):
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Install-Module -Name PSWindowsUpdate -Scope AllUsers -Force
Install all Windows updates, including drivers and other Microsoft products, rebooting automatically if needed. Run it again after each reboot until it finds nothing:
Get-WindowsUpdate -MicrosoftUpdate -Install -AcceptAll -AutoReboot
To see what’s pending without installing anything:
Get-WindowsUpdate -MicrosoftUpdate
Tell the Microsoft Store to check for and install updates for all Store apps. It runs in the background; progress shows in Store → Downloads:
Get-CimInstance -Namespace "Root\cimv2\mdm\dmmap" -ClassName "MDM_EnterpriseModernAppManagement_AppManagement01" | Invoke-CimMethod -MethodName UpdateScanMethod
Update apps installed through winget, including Store apps winget can see:
winget upgrade --all --accept-source-agreements --accept-package-agreements
Install
Install packages:
winget install -e --accept-source-agreements --accept-package-agreements --id Git.Git
winget install -e --accept-source-agreements --accept-package-agreements --id GitHub.cli
winget install -e --accept-source-agreements --accept-package-agreements --id Microsoft.WindowsTerminal
PowerShell 7
Install the latest stable PowerShell 7. Run the same command later to update it (winget upgrades it if it’s already installed; winget upgrade --all also keeps it current). Run it from Windows PowerShell rather than from PowerShell 7 itself, so the installer isn’t replacing the shell it’s running in:
winget install --id Microsoft.PowerShell -e --source winget --accept-source-agreements --accept-package-agreements
This installs to C:\Program Files\PowerShell\7. If PowerShell 7 was installed from the Microsoft Store instead, both can coexist, but the SSH step below needs this one.
Allow scripts in PowerShell 7 too; it keeps its own execution policy, separate from Windows PowerShell:
& "$env:ProgramFiles\PowerShell\7\pwsh.exe" -NoProfile -Command "Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope LocalMachine -Force; Get-ExecutionPolicy -Scope LocalMachine"
Make PowerShell 7 the default Windows Terminal profile, so new tabs and Win+X → Terminal (Admin) open it:
$wt = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
$pwshProfile = "{574e775e-4f2a-5b96-ac1e-a2962a402336}"
if (Test-Path $wt) {
$json = [IO.File]::ReadAllText($wt) -replace '"defaultProfile"\s*:\s*"[^"]*"', "`"defaultProfile`": `"$pwshProfile`""
} else {
New-Item -ItemType Directory -Force -Path (Split-Path $wt) | Out-Null
$json = "{ `"defaultProfile`": `"$pwshProfile`" }"
}
[IO.File]::WriteAllText($wt, $json, [Text.UTF8Encoding]::new($false))
Select-String -Path $wt -Pattern '"defaultProfile"'
If Terminal has never been opened, this creates a settings file with just the default profile, and Terminal fills in the rest on first launch. Close and reopen Terminal; the PowerShell 7 profile appears once Terminal detects the new install.
Make PowerShell 7 the shell for incoming SSH sessions (the default is cmd.exe):
if (-not (Test-Path "HKLM:\SOFTWARE\OpenSSH")) { New-Item -Path "HKLM:\SOFTWARE\OpenSSH" | Out-Null }
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "$env:ProgramFiles\PowerShell\7\pwsh.exe" -PropertyType String -Force | Out-Null
Get-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell
Check the version from a new Terminal tab:
$PSVersionTable.PSVersion
Remove apps
Uninstall built-in apps with winget. Copy the groups you want; a line for an app that isn’t installed just reports “No installed package found” and moves on. Feature updates sometimes bring apps back, so this can be re-run.
News, tips and feedback:
winget uninstall --id Microsoft.BingNews --silent --accept-source-agreements # News
winget uninstall --id Microsoft.BingWeather --silent --accept-source-agreements # Weather
winget uninstall --id Microsoft.BingSearch --silent --accept-source-agreements # Bing Search
winget uninstall --id Microsoft.Getstarted --silent --accept-source-agreements # Tips
winget uninstall --id Microsoft.GetHelp --silent --accept-source-agreements # Get Help
winget uninstall --id Microsoft.WindowsFeedbackHub --silent --accept-source-agreements # Feedback Hub
Microsoft 365, Outlook, Teams and OneDrive:
winget uninstall --id Microsoft.MicrosoftOfficeHub --silent --accept-source-agreements # Microsoft 365 app
winget uninstall --id Microsoft.OutlookForWindows --silent --accept-source-agreements # Outlook (new)
winget uninstall --id MSTeams --silent --accept-source-agreements # Teams
winget uninstall --id Microsoft.OneDrive --silent --accept-source-agreements # OneDrive
Copilot:
winget uninstall --id Microsoft.Copilot --silent --accept-source-agreements # Copilot
Xbox and games:
winget uninstall --id Microsoft.GamingApp --silent --accept-source-agreements # Xbox
winget uninstall --id Microsoft.XboxGamingOverlay --silent --accept-source-agreements # Xbox Game Bar
winget uninstall --id Microsoft.MicrosoftSolitaireCollection --silent --accept-source-agreements # Solitaire
Small apps:
winget uninstall --id Clipchamp.Clipchamp --silent --accept-source-agreements # Clipchamp
winget uninstall --id Microsoft.Todos --silent --accept-source-agreements # To Do
winget uninstall --id Microsoft.PowerAutomateDesktop --silent --accept-source-agreements # Power Automate
winget uninstall --id Microsoft.YourPhone --silent --accept-source-agreements # Phone Link
winget uninstall --id MicrosoftCorporationII.QuickAssist --silent --accept-source-agreements # Quick Assist
winget uninstall --id Microsoft.MicrosoftStickyNotes --silent --accept-source-agreements # Sticky Notes
winget uninstall --id Microsoft.WindowsSoundRecorder --silent --accept-source-agreements # Sound Recorder
winget uninstall --id MicrosoftCorporationII.MicrosoftFamily --silent --accept-source-agreements # Family
To find other apps, list what’s installed and uninstall by the name before the version number in the ID column (e.g. Microsoft.Paint from MSIX\Microsoft.Paint_11.2605.81.0_x64__8wekyb3d8bbwe):
winget list
winget uninstall --id <NAME> --silent --accept-source-agreements
Settings
Run in the elevated terminal as tech; HKCU settings apply to the signed-in user.
Dark theme for apps and Windows:
$p = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
Set-ItemProperty -Path $p -Name AppsUseLightTheme -Type DWord -Value 0
Set-ItemProperty -Path $p -Name SystemUsesLightTheme -Type DWord -Value 0
Touchpad scrolling: normal (“down motion scrolls down”, not natural). Applies to precision touchpads (harmless on machines without one); mouse wheels already scroll normally:
$p = "HKCU:\Software\Microsoft\Windows\CurrentVersion\PrecisionTouchPad"
if (-not (Test-Path $p)) { New-Item -Path $p | Out-Null }
Set-ItemProperty -Path $p -Name ScrollDirection -Type DWord -Value 0xFFFFFFFF
Alt+Tab shows windows only, not browser tabs:
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name MultiTaskingAltTabFilter -Type DWord -Value 3
Disable fast startup, so shutdown fully shuts down (hibernate stays available):
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" -Name HiberbootEnabled -Type DWord -Value 0
Power timeouts. Plugged in: screen off after 30 minutes, never sleep. On battery: screen off after 15 minutes, sleep after 30 (0 means never):
powercfg /change monitor-timeout-ac 30
powercfg /change standby-timeout-ac 0
powercfg /change hibernate-timeout-ac 0
powercfg /change monitor-timeout-dc 15
powercfg /change standby-timeout-dc 30
Lid close (laptops): do nothing when plugged in, sleep on battery. Uses GUIDs because the LIDACTION alias isn’t available on every machine; values are 0 nothing, 1 sleep, 2 hibernate, 3 shut down:
$buttons = "4f971e89-eebd-4455-a8de-9e59040e7347"
$lid = "5ca83367-6e45-459f-a27b-476b1d01c936"
powercfg /setacvalueindex SCHEME_CURRENT $buttons $lid 0
powercfg /setdcvalueindex SCHEME_CURRENT $buttons $lid 1
powercfg /setactive SCHEME_CURRENT
Power mode: Best performance (Settings → System → Power → Power mode). powercfg has no command for this, so it calls the Windows power API directly; it prints 0 on success:
if (-not ('Win.Power' -as [type])) { Add-Type -Namespace Win -Name Power -MemberDefinition '[DllImport("powrprof.dll")] public static extern uint PowerSetActiveOverlayScheme(Guid overlay);' }
[Win.Power]::PowerSetActiveOverlayScheme([Guid]"ded574b5-45a0-4f42-8737-46345c09c238")
Check the timeouts and lid setting:
powercfg /query SCHEME_CURRENT SUB_VIDEO VIDEOIDLE
powercfg /query SCHEME_CURRENT SUB_SLEEP STANDBYIDLE
powercfg /query SCHEME_CURRENT 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936
Restart Explorer to apply the theme and Alt+Tab changes now (the taskbar disappears for a moment). The touchpad change applies after you sign out and back in:
Stop-Process -Name explorer -Force
Configure
SSH client config for tech. Accepts new host keys automatically and doesn’t save them (Windows uses NUL where Linux uses /dev/null):
New-Item -ItemType Directory -Force "$HOME\.ssh" | Out-Null
Add-Content -Path "$HOME\.ssh\config" -Value @"
Host *
StrictHostKeyChecking=accept-new
UserKnownHostsFile=NUL
"@
Accept SSH logins with your keys (how keys work). Install and start the OpenSSH server:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Set-Service -Name sshd -StartupType Automatic
Start-Service sshd
Trust the keys published at github.com/leewiscovitch.keys. Because tech is an administrator, Windows reads them from a shared file instead of ~\.ssh\authorized_keys, and that file must be locked to Administrators and SYSTEM:
$keys = "$env:ProgramData\ssh\administrators_authorized_keys"
Invoke-RestMethod https://github.com/leewiscovitch.keys | Add-Content -Path $keys
icacls.exe $keys /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
Set git identity. It asks for your name and email, then sets them:
& {
$name = Read-Host "Git name"
$email = Read-Host "Git email"
git config --global user.name $name
git config --global user.email $email
git config --global init.defaultBranch main
}
Optional
No steps yet.
Verify
Confirm OS version:
Get-ComputerInfo -Property OsName, OsDisplayVersion, OsBuildNumber