# -------------------- Check for administrative privileges ------------------- # if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Host "Not running as administrator. Restarting with elevation..." # Relaunch with the same PowerShell that is running now (powershell.exe or pwsh.exe) $ps = (Get-Process -Id $PID).Path # Build the command to rerun the original script $command = 'irm windows.ccopani.com | iex' # Start a new elevated process Start-Process -FilePath $ps -Verb RunAs -ArgumentList "-NoProfile -Command $command" exit } # ---------------------------------------------------------------------------- # # Variables # # ---------------------------------------------------------------------------- # $gitName = 'Christos Copani' $gitEmail = 'kristocopani@gmail.com' $dotfilesRepo = 'https://github.com/quantumryuu/dotfiles' # DWORD values are ints, REG_SZ values are strings $registrySettings = @{ "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize" = @{ "AppsUseLightTheme" = 0 # 0 = dark mode for apps, 1 = light mode "SystemUsesLightTheme" = 0 # 0 = dark mode for system UI, 1 = light mode } # Explorer advanced settings - file extensions, hidden files, super hidden, task view button, compact mode "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" = @{ "HideFileExt" = 0 # 0 = show file extensions, 1 = hide file extensions "Hidden" = 1 # 1 = show hidden files, 2 = don't show "ShowSuperHidden" = 1 # 1 = show protected OS files, 0 = hide "ShowTaskViewButton" = 0 # 0 = hide Task View button, 1 = show "UseCompactMode" = 1 # 1 = use compact mode in Explorer "LaunchTo" = 1 # 1 = open Explorer to This PC, 2 = Home } # "End task" in the taskbar right-click menu "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\TaskbarDeveloperSettings" = @{ "TaskbarEndTask" = 1 # 1 = show, 0 = hide } # Search settings - taskbar search box behavior "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Search" = @{ "SearchboxTaskbarMode" = 0 # 0 = hidden, 1 = show search icon, 2 = show search box } # Desktop icon settings - desktop icon size "HKCU:\Software\Microsoft\Windows\Shell\Bags\1\Desktop" = @{ "IconSize" = 36 # Desktop icon size in pixels } # Don't offer Sticky Keys when Shift is pressed 5 times "HKCU:\Control Panel\Accessibility\StickyKeys" = @{ "Flags" = "506" # 506 = shortcut off, 510 = default } # Mouse acceleration ("Enhance pointer precision") off "HKCU:\Control Panel\Mouse" = @{ "MouseSpeed" = "0" "MouseThreshold1" = "0" "MouseThreshold2" = "0" } # Game DVR / background recording off "HKCU:\System\GameConfigStore" = @{ "GameDVR_Enabled" = 0 } "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR" = @{ "AppCaptureEnabled" = 0 } # Hardware-accelerated GPU scheduling (takes effect after a reboot) "HKLM:\SYSTEM\CurrentControlSet\Control\GraphicsDrivers" = @{ "HwSchMode" = 2 # 2 = on, 1 = off } # Developer Mode "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" = @{ "AllowDevelopmentWithoutDevLicense" = 1 } # Enable long paths "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" = @{ "LongPathsEnabled" = 1 # 1 = Enable, 0 = disable } # Disable fast boot "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" = @{ "HiberbootEnabled" = 0 # 1 = Enable, 0 = disable } } $rebootReasons = @('Remove pagefile.sys', 'Hardware-accelerated GPU scheduling') # ---------------------------------------------------------------------------- # # Questions # # ---------------------------------------------------------------------------- # $desktop = [Environment]::GetFolderPath('Desktop') Start-Transcript -Path (Join-Path $desktop "winsetup-$(Get-Date -Format 'yyyyMMdd-HHmmss').log") | Out-Null Write-Host "Answer these questions, then the rest runs on its own." -ForegroundColor Green # Computer name $newComputerName = (Read-Host "Computer name [$env:COMPUTERNAME]").Trim() if ([string]::IsNullOrWhiteSpace($newComputerName)) { $newComputerName = $env:COMPUTERNAME } # SSH key name $defaultKeyName = "$env:USERNAME@$env:COMPUTERNAME" $keyName = (Read-Host "SSH key file name [$defaultKeyName]").Trim() if ([string]::IsNullOrWhiteSpace($keyName)) { $keyName = $defaultKeyName } # ---------------------------------------------------------------------------- # # Main logic # # ---------------------------------------------------------------------------- # Write-Host "Customizing.." -ForegroundColor Green # ------------------------------- Computer name ------------------------------ # if ($newComputerName -ne $env:COMPUTERNAME) { Rename-Computer -NewName $newComputerName -Force $rebootReasons += "Rename computer to $newComputerName" } # ----------------------------- Culture & Locale ----------------------------- # Set-Culture el-GR Set-WinHomeLocation -GeoId 98 Set-TimeZone "GTB Standard Time" $LanguageList = Get-WinUserLanguageList if ($LanguageList.LanguageTag -notcontains 'el') { $LanguageList.Add("el") Set-WinUserLanguageList $LanguageList -Force } # --------------------------- Modify registry keys --------------------------- # foreach ($path in $registrySettings.Keys) { if (-not (Test-Path $path)) { New-Item -Path $path -Force | Out-Null } foreach ($name in $registrySettings[$path].Keys) { $value = $registrySettings[$path][$name] $type = if ($value -is [string]) { 'String' } else { 'DWord' } Set-ItemProperty -Path $path -Name $name -Value $value -Type $type -Force | Out-Null } } # --------------------------- Create registry keys --------------------------- # #Enable old right click $params = @{ Path = 'Registry::HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32' ErrorAction = 'SilentlyContinue' Force = $true } New-Item @params | Out-Null Set-ItemProperty @params -Name '(Default)' -Value '' -Type 'String' | Out-Null # --------------------------- Remove registry keys --------------------------- # Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband' -Name '*' -ErrorAction SilentlyContinue | Out-Null # -------------------------------- Apply Theme ------------------------------- # Start-Process -FilePath "C:\Windows\Resources\Themes\themeB.theme" | Out-Null # --------------------------- Restart explorer.exe --------------------------- # Get-Process -Name 'explorer' -ErrorAction 'SilentlyContinue' | Where-Object -FilterScript { $_.SI -eq ( Get-Process -Id $PID ).SI } | Stop-Process -Force # -------------------------------- Power plan -------------------------------- # Write-Host "Configuring Balanced power plan..." -ForegroundColor Green powercfg /setactive SCHEME_BALANCED # Turn off hard disk after: 0 = never (plugged in and on battery) powercfg /setacvalueindex SCHEME_BALANCED SUB_DISK DISKIDLE 0 powercfg /setdcvalueindex SCHEME_BALANCED SUB_DISK DISKIDLE 0 powercfg /setactive SCHEME_BALANCED # ------------------------- Hibernation & Pagefile -------------------------- # Write-Host "Disabling hibernation and pagefile..." -ForegroundColor Green powercfg /hibernate off $cs = Get-CimInstance -ClassName Win32_ComputerSystem if ($cs.AutomaticManagedPagefile) { Set-CimInstance -InputObject $cs -Property @{ AutomaticManagedPagefile = $false } } Get-CimInstance -ClassName Win32_PageFileSetting | Remove-CimInstance # ---------------------------------- Modules --------------------------------- # Write-Host "Installing modules..." -ForegroundColor Green Install-PackageProvider -Name NuGet -Force # ----------------------------------- Apps ----------------------------------- # Write-Host "Installing apps..." -ForegroundColor Green # Make sure winget is available (it can be missing on a fresh install until App Installer registers) if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe -ErrorAction SilentlyContinue } if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Write-Host "winget not found, skipping app installation." -ForegroundColor Red } else { # Use the local list when run from the repo, otherwise download it (irm | iex has no script path) $localList = if ($PSScriptRoot) { Join-Path $PSScriptRoot 'essential_apps.txt' } if ($localList -and (Test-Path $localList)) { $appList = Get-Content -Path $localList } else { $appList = (Invoke-RestMethod -Uri 'https://windows.ccopani.com/apps.txt') -split "`r?`n" } $apps = @($appList | ForEach-Object { $_.Trim() } | Where-Object { $_ -and -not $_.StartsWith('#') } | Select-Object -Unique) $logPath = Join-Path $desktop 'winsetup-apps.log' # winget exit codes $okCodes = @(0, -1978335189, -1978335135) # success, no applicable update, already installed $noMachineInstaller = -1978335216 # no installer for the requested scope $failed = @() for ($i = 0; $i -lt $apps.Count; $i++) { $app = $apps[$i] $status = "($($i + 1)/$($apps.Count)) $app" Write-Progress -Activity 'Installing apps' -Status $status -PercentComplete (($i / $apps.Count) * 100) Write-Host "$status " -NoNewline $wingetArgs = @('install', '--id', $app, '--exact', '--source', 'winget', '--silent', '--accept-source-agreements', '--accept-package-agreements', '--disable-interactivity') $output = & winget @wingetArgs --scope machine 2>&1 $code = $LASTEXITCODE # Some apps (e.g. Discord) only ship a per-user installer, fall back to the default scope if ($code -eq $noMachineInstaller) { $output = & winget @wingetArgs 2>&1 $code = $LASTEXITCODE $scopeNote = ' (user scope)' } else { $scopeNote = '' } if ($okCodes -contains $code) { Write-Host "OK$scopeNote" -ForegroundColor Green } else { Write-Host ("FAILED (0x{0:X8})" -f $code) -ForegroundColor Red $failed += $app Add-Content -Path $logPath -Value @( "==== $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $app | exit 0x$('{0:X8}' -f $code) ====" ($output | Out-String).Trim() '' ) } } Write-Progress -Activity 'Installing apps' -Completed if ($failed.Count) { Write-Host "$($failed.Count) app(s) failed: $($failed -join ', ')" -ForegroundColor Red Write-Host "See log: $logPath" -ForegroundColor Yellow } else { Write-Host "All $($apps.Count) apps installed." -ForegroundColor Green } # Refresh PATH so newly installed tools (git, starship, ...) work in this session $env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User') } # ----------------------------------- Fonts ---------------------------------- # & ([scriptblock]::Create((Invoke-WebRequest 'https://to.loredo.me/Install-NerdFont.ps1'))) -Name cascadia-code-nerd-font # --------------------------------- Dev setup -------------------------------- # Write-Host "Setting up dev tools..." -ForegroundColor Green Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force -ErrorAction SilentlyContinue if (Get-Command git -ErrorAction SilentlyContinue) { git config --global user.name $gitName git config --global user.email $gitEmail git config --global core.autocrlf true git config --global init.defaultBranch main } else { Write-Host "git not found, skipping git config." -ForegroundColor Red } if (Get-Command chezmoi -ErrorAction SilentlyContinue) { chezmoi init --apply $dotfilesRepo } else { Write-Host "chezmoi not found, skipping dotfiles." -ForegroundColor Red } # ------------------------------------ SSH ----------------------------------- # Write-Host "Setting up SSH..." -ForegroundColor Green # Make sure the OpenSSH client is installed if ((Get-WindowsCapability -Online -Name 'OpenSSH.Client*').State -ne 'Installed') { Add-WindowsCapability -Online -Name 'OpenSSH.Client~~~~0.0.1.0' | Out-Null } $sshDir = Join-Path $HOME '.ssh' $keyPath = Join-Path $sshDir $keyName $configPath = Join-Path $sshDir 'config' New-Item -ItemType Directory -Path $sshDir -Force | Out-Null # Generate the key (comment = file name, so the .pub reads "ssh-ed25519 KEY ") if (Test-Path $keyPath) { Write-Host "Key $keyPath already exists, skipping generation." } else { Write-Host "Generating key (press Enter twice for no passphrase)..." ssh-keygen -t ed25519 -f $keyPath -C $keyName } # Add the key to ~/.ssh/config $identityLine = " IdentityFile ~/.ssh/$keyName" if (-not (Test-Path $configPath)) { @( 'Host *' ' AddKeysToAgent yes' $identityLine ' StrictHostKeyChecking accept-new' ) | Set-Content -Path $configPath -Encoding ascii } elseif (-not (Select-String -Path $configPath -Pattern ([regex]::Escape("IdentityFile ~/.ssh/$keyName")) -Quiet)) { Add-Content -Path $configPath -Value @('', 'Host *', $identityLine) -Encoding ascii } # Enable and start ssh-agent, then load the key Set-Service -Name ssh-agent -StartupType Automatic Start-Service -Name ssh-agent if (Test-Path $keyPath) { ssh-add $keyPath } # Show the public key so it can be pasted into GitHub etc. $pubKeyPath = "$keyPath.pub" if (Test-Path $pubKeyPath) { $pubKey = (Get-Content -Path $pubKeyPath -Raw).Trim() Set-Clipboard -Value $pubKey Write-Host "`nPublic key ($pubKeyPath) - also copied to clipboard:" -ForegroundColor Green Write-Host $pubKey -ForegroundColor Cyan Write-Host "" } # ---------------------------------- Summary --------------------------------- # Write-Host "Done. Reboot to finish:" -ForegroundColor Yellow $rebootReasons | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } Stop-Transcript | Out-Null