← All OSes

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