Script for installing WSL on Windows 10 AME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 lines
21 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. # https://stackoverflow.com/a/34559554/
  2. function New-TemporaryDirectory {
  3. $parent = [System.IO.Path]::GetTempPath()
  4. $name = [System.IO.Path]::GetRandomFileName()
  5. New-Item -ItemType Directory -Path (Join-Path $parent $name)
  6. }
  7. Workflow Install-WSL {
  8. [CmdletBinding(DefaultParameterSetName='Installation')]
  9. param(
  10. [Parameter(Mandatory=$True,ParameterSetName='Installation',Position=0)]
  11. [ValidateSet(
  12. 'wslubuntu2004',
  13. 'wslubuntu2004arm',
  14. 'wsl-ubuntu-1804',
  15. 'wsl-ubuntu-1804-arm',
  16. 'wsl-ubuntu-1604',
  17. 'wsl-debian-gnulinux',
  18. 'wsl-kali-linux-new',
  19. 'wsl-opensuse-42',
  20. 'wsl-sles-12'
  21. )]
  22. [string]$LinuxDistribution,
  23. [Parameter(Mandatory=$False,ParameterSetName='Installation')]
  24. [switch]$FeatureInstalled,
  25. [Parameter(Mandatory=$True,ParameterSetName='Cancelation')]
  26. [switch]$Cancel
  27. )
  28. # The task scheduler is unreliable in AME
  29. #Write-Output 'Scheduling task'
  30. #
  31. #$action = New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -Argument "-NonInteractive -WindowStyle Normal -NoLogo -NoProfile -Command `"& { Write-Output \`"Don```'t close this PowerShell window! This is your WSL installer! Just give it a minute...\`"; Get-Job -Command `'Install-WSL`' | Resume-Job | Receive-Job -Wait; pause; exit }`""
  32. #$logon = New-ScheduledTaskTrigger -AtLogOn
  33. #$task = Register-ScheduledTask -TaskName 'InstallWSL' -Action $action -Trigger $logon -RunLevel Highest
  34. ## Where ShortcutPath is placed is honestly an implementation detail. It will
  35. ## be run as administrator by the elevator, which is what gets run at startup
  36. #$ShortcutPath = Join-Path $env:ProgramData 'Microsoft\Windows\Install WSL.lnk'
  37. #$ElevatorPath = Join-Path $env:ProgramData 'Microsoft\Windows\Start Menu\Programs\Startup\Install WSL.lnk'
  38. $ShortcutPath = Join-Path $env:AppData 'Microsoft\Windows\Start Menu\Programs\Startup\Install WSL.lnk'
  39. if ($Cancel) {
  40. $Removed = Remove-Item -LiteralPath $ShortcutPath -ErrorAction SilentlyContinue
  41. $Removed = Get-Job -Command 'Install-WSL' | Where-Object {$_.State -eq 'Suspended'} | Remove-Job -Force
  42. return Write-Output 'All pending WSL installations have been canceled.'
  43. }
  44. # establish directory for WSL installations
  45. $AppDataFolder = Join-Path $env:LocalAppData 'WSL'
  46. $DistrosFolder = New-Item -ItemType Directory -Force -Path $AppDataFolder
  47. $DistroFolder = Join-Path $DistrosFolder $LinuxDistribution
  48. if (Test-Path -Path $DistroFolder -PathType Container) {
  49. return Write-Error 'Cannot install a distro twice! This will waste your internet data. Uninstall the existing version first.' -Category ResourceExists
  50. }
  51. Write-Output 'Creating startup item'
  52. InlineScript {
  53. $shell = New-Object -ComObject ('WScript.Shell')
  54. $shortcut = $shell.CreateShortcut($Using:ShortcutPath)
  55. $shortcut.TargetPath = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'
  56. $shortcut.Arguments = "-WindowStyle Normal -NoLogo -NoProfile -Command `"& { Write-Output \`"Resuming installation...\`"; Get-Job -Command `'Install-WSL`' | Resume-Job | Receive-Job -Wait; pause; exit }`""
  57. $shortcut.Save()
  58. #$elevator = $shell.CreateShortcut($Using:ElevatorPath)
  59. #$elevator.TargetPath = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'
  60. #$elevator.Arguments = "-WindowStyle Normal -NoLogo -NoProfile -Command `"Write-Output \`"The WSL installation can now be started. Please accept the UAC prompt to proceed\`"; pause; Start-Process -FilePath '$Using:ShortcutPath' -Verb Runas`""
  61. #$elevator.Save()
  62. }
  63. # This didn't work.
  64. ## This is the exact same shortcut as above, but with 'Run As Administrator' set, encoded in Base64.
  65. ## [Convert]::ToBase64String((Get-Content 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\Install WSL.lnk' -Encoding Byte))
  66. ## This is needed because that flag cannot be checked programmatically.
  67. #$Base64 = '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'
  68. #Set-Content -LiteralPath $ShortcutPath -Value ([Convert]::FromBase64String($Base64)) -Encoding Byte
  69. Write-Output 'There will be a "Windows PowerShell" shortcut in your startup items until this script is complete. Please do not be alarmed, it will remove itself once the installation is complete.'
  70. Write-Output 'Ensuring required features are enabled'
  71. # using a named pipe to communicate between elevated process and not elevated one
  72. if ($FeatureInstalled) {
  73. $RestartNeeded = $False
  74. } else {
  75. try {
  76. # For various reasons this needs to be duplicated twice.
  77. # I hate it as much as you, but for some reason I can't put it in a function
  78. # It just refuses to work when I try to call it in the loop below
  79. $RestartNeeded = InlineScript {
  80. $PipeName = -join (((48..57)+(65..90)+(97..122)) * 80 |Get-Random -Count 12 |%{[char]$_})
  81. $Enabled = Start-Process powershell -ArgumentList "`
  82. `$Enabled = Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart -WarningAction SilentlyContinue`
  83. `$RestartNeeded = `$Enabled.RestartNeeded`
  84. `
  85. `$pipe = New-Object System.IO.Pipes.NamedPipeServerStream `'$PipeName`',`'Out`'`
  86. `$pipe.WaitForConnection()`
  87. `$sw = New-Object System.IO.StreamWriter `$pipe`
  88. `$sw.AutoFlush = `$True`
  89. `$sw.WriteLine([string]`$RestartNeeded)`
  90. `$sw.Dispose()`
  91. `$pipe.Dispose()`
  92. " -Verb RunAs -WindowStyle Hidden -ErrorAction Stop
  93. $pipe = New-Object System.IO.Pipes.NamedPipeClientStream '.',$Using:PipeName,'In'
  94. $pipe.Connect()
  95. $sr = New-Object System.IO.StreamReader $pipe
  96. $data = $sr.ReadLine()
  97. $sr.Dispose()
  98. $pipe.Dispose()
  99. $data -eq [string]$True
  100. } -ErrorAction Stop
  101. } catch {
  102. return Write-Error 'Please accept the UAC prompt so that the WSL feature can be installed, or specify the -FeatureInstalled flag to skip'
  103. }
  104. }
  105. if ($RestartNeeded) {
  106. # TODO detect if we're already waiting for a reboot specifically
  107. # Maybe this can be done by checking for the scheduled task instead?
  108. # This feels messy which is why it's disabled, and it would also detect
  109. # the currently running task
  110. #$Job = Get-Job -Name 'Install-WSL'
  111. #
  112. #if ($Job) {
  113. # Write-Output 'Already waiting for the WSL feature to be enabled'
  114. # return
  115. #}
  116. # Future Logan from the future!: I think the shortcut is more easily
  117. # detected, but there are reasons you might want to run this more than
  118. # once in a row. For example if you are installing multiple distros
  119. # Should work okay...
  120. Write-Output 'Restart your computer in 30 seconds or it will explode'
  121. Suspend-Workflow
  122. # Wait for a logon where the feature is installed. This will be after at
  123. # least 1 reboot, but for various reasons (grumble grumble...) it might
  124. # be later. Every Suspend-Workflow is virtually guaranteed to be resumed
  125. # by a logon, or a manual resume (which is harmless in this case).
  126. $waiting = $True
  127. while ($waiting) {
  128. if ($FeatureInstalled) {
  129. $RestartNeeded = $False
  130. } else {
  131. try {
  132. $RestartNeeded = InlineScript {
  133. $PipeName = -join (((48..57)+(65..90)+(97..122)) * 80 |Get-Random -Count 12 |%{[char]$_})
  134. $Enabled = Start-Process powershell -ArgumentList "`
  135. `$Enabled = Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart -WarningAction SilentlyContinue`
  136. `$RestartNeeded = `$Enabled.RestartNeeded`
  137. `
  138. `$pipe = New-Object System.IO.Pipes.NamedPipeServerStream `'$PipeName`',`'Out`'`
  139. `$pipe.WaitForConnection()`
  140. `$sw = New-Object System.IO.StreamWriter `$pipe`
  141. `$sw.AutoFlush = `$True`
  142. `$sw.WriteLine([string]`$RestartNeeded)`
  143. `$sw.Dispose()`
  144. `$pipe.Dispose()`
  145. " -Verb RunAs -WindowStyle Hidden -ErrorAction Stop
  146. $pipe = New-Object System.IO.Pipes.NamedPipeClientStream '.',$Using:PipeName,'In'
  147. $pipe.Connect()
  148. $sr = New-Object System.IO.StreamReader $pipe
  149. $data = $sr.ReadLine()
  150. $sr.Dispose()
  151. $pipe.Dispose()
  152. $data -eq [string]$True
  153. } -ErrorAction Stop
  154. } catch {
  155. # I decided that this is not always true and it would be
  156. # rude to assume that. So I give the user a choice and allow
  157. # them to continue without UAC
  158. ## The user accepted the UAC prompt the first time, so they
  159. ## can do it again. They cannot specify the -FeatureInstalled
  160. ## flag at this point, unfortunately.
  161. #Write-Output 'Please accept the UAC prompt to continue installation.'
  162. # Try to get input from the user as a fallback
  163. $response = InlineScript {
  164. [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
  165. [System.Windows.Forms.Messagebox]::Show("Admin access is required to check the status of the WSL feature. If you can no longer grant admin access via UAC:`n`nIs the WSL feature installed and enabled?", 'WSL Installer', [System.Windows.Forms.MessageBoxButtons]::YesNo)
  166. }
  167. $RestartNeeded = $response -eq 7 # 7 is DialogResult.No
  168. }
  169. }
  170. if ($RestartNeeded) {
  171. Write-Output 'Looks like the WSL component is still not installed.'
  172. Suspend-Workflow
  173. } else {
  174. $waiting = $False
  175. }
  176. }
  177. }
  178. # This is so that the progress indicator doesn't obstruct the text
  179. Write-Output "`n`n`n`n`n`n`n`n`n"
  180. Write-Output 'Warning: The PowerShell window will display the download process for longer than'
  181. Write-Output 'usual. This is a Windows bug, and is only visual.'
  182. Write-Output ''
  183. $retrying = $True
  184. while ($retrying) {
  185. $tempFile = InlineScript { New-TemporaryFile }
  186. Remove-Item -LiteralPath $tempFile
  187. $tempFile = $tempFile.FullName -replace '$','.zip'
  188. try {
  189. Write-Output "Attempting to download distribution to $tempFile..."
  190. Invoke-WebRequest -Uri "https://aka.ms/$LinuxDistribution" -OutFile $tempFile -ErrorAction Stop -UseBasicParsing
  191. #InlineScript {
  192. # (New-Object System.Net.WebClient).DownloadFile("https://aka.ms/$Using:LinuxDistribution", $tempFile.FullName)
  193. #}
  194. #Start-BitsTransfer -DisplayName 'WSL Package Download' -Source "https://aka.ms/$LinuxDistribution" -Destination $tempFile -ErrorAction Stop
  195. $retrying = $False
  196. Write-Output 'Done!'
  197. } catch {
  198. #Get-BitsTransfer -Name 'WSL Package Download' | Remove-BitsTransfer -ErrorAction SilentlyContinue
  199. Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
  200. # PSItem is contextual and can't be read from the InlineScript
  201. $theError = $PSItem.Message
  202. Write-Output "Error: $theError"
  203. $response = InlineScript {
  204. [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
  205. [System.Windows.Forms.Messagebox]::Show("The WSL package '$Using:LinuxDistribution' could not be downloaded from Microsoft's servers.`n`nError: $Using:theError`n`nYou may abort the install, and restart it at any time using the wizard. Clicking Ignore will cause a retry the next time you log in.", 'Could not download WSL package', [System.Windows.Forms.MessageBoxButtons]::AbortRetryIgnore)
  206. }
  207. if ($response -eq 3) { # Abort
  208. Write-Output 'Aborting'
  209. $retrying = $False
  210. Unregister-ScheduledTask -TaskName 'InstallWSL' -Confirm:$False
  211. return
  212. } elseif ($response -eq 5) { # Ignore
  213. Write-Output 'Ignoring'
  214. Suspend-Workflow # Wait for next logon
  215. }
  216. Write-Output 'Retrying'
  217. # If retry just loop again /shrug
  218. }
  219. }
  220. #Write-Output 'Unscheduling task...'
  221. #
  222. #Unregister-ScheduledTask -TaskName 'InstallWSL' -Confirm:$False
  223. Write-Output 'Removing startup item...'
  224. Remove-Item -LiteralPath $ShortcutPath -ErrorAction SilentlyContinue
  225. #Remove-Item -LiteralPath $ElevatorPath -ErrorAction SilentlyContinue
  226. $tempDir = New-TemporaryDirectory
  227. Expand-Archive -LiteralPath $tempFile -DestinationPath $tempDir
  228. Remove-Item -LiteralPath $tempFile
  229. Write-Output 'Distribution bundle extracted'
  230. # Thought we might need to support ARM64, turns out artrons doesn't want it.
  231. # Leaving this (and the comment) here just in case.
  232. ## This appx package contains inner appx packages for each architecture.
  233. ## This information is encoded in an XML manifest file.
  234. ## I want to use the XML manifest to find the right package, unzip that, and
  235. ## then find the executable inside.
  236. ## This allows compatibility with both x86 and ARM
  237. #$RootManifest = Join-Path $tempDir 'AppxMetadata\AppxBundleManifest.xml'
  238. #$Package = Select-Xml -Path $RootManifest -XPath '/Bundle/Packages/Package' | Where-Object {$_.Node.Type -eq 'Application' -and $_.Node.Architecture -eq 'x64'} | Select-Object -First 1
  239. $theDir = $tempDir
  240. $Executable = Get-ChildItem $tempDir | Where-Object {$_.Name -match '.exe$'} | Select-Object -First 1
  241. if ($Executable -eq $null) {
  242. $Package = Get-ChildItem $tempDir | Where-Object {$_.Name -match '_x64.appx$'} | Select-Object -First 1
  243. if ($Package -eq $null) {
  244. return Write-Error 'Could not find the package containing the installer :(' -Category NotImplemented
  245. }
  246. $Package = Rename-Item -LiteralPath ($Package.FullName) -NewName ($Package.Name -replace '.appx$','.zip') -PassThru
  247. Write-Output "Distribution package: $($Package.Name)"
  248. $InnerPackageTemp = New-TemporaryDirectory
  249. Expand-Archive -LiteralPath $Package -DestinationPath $InnerPackageTemp
  250. Remove-Item -LiteralPath $tempDir -Recurse
  251. $Executable = Get-ChildItem $InnerPackageTemp | Where-Object {$_.Name -match '.exe$'} | Select-Object -First 1
  252. $theDir = $InnerPackageTemp
  253. if ($Executable -eq $null) {
  254. return Write-Error 'Could not find an executable inside the x64 package :(' -Category NotImplemented
  255. }
  256. } else {
  257. Write-Output 'Root package contains the installer'
  258. }
  259. # this is going to have to stick around forever if the wsl install is going to stay intact
  260. $theDir = Move-Item -LiteralPath $theDir -Destination $DistroFolder -PassThru
  261. $Executable = Get-ChildItem $theDir | Where-Object {$_.Name -match '.exe$'} | Select-Object -First 1
  262. Write-Output "Executing installer: $($Executable.Name)"
  263. InlineScript { wsl --set-default-version 1 }
  264. Start-Process -FilePath ($Executable.FullName) -Wait
  265. # ruins the WSL install
  266. #Remove-Item -LiteralPath $theDir -Recurse -ErrorAction SilentlyContinue
  267. Write-Output 'Everything should be in order now. Enjoy!'
  268. # We done
  269. }
  270. function Install-WSLInteractive {
  271. $Distros = @(
  272. [PSCustomObject]@{Slug = 'wslubuntu2004'; Name = 'Ubuntu 20.04'; Arch = 'x64'}
  273. #[PSCustomObject]@{Slug = 'wslubuntu2004arm'; Name = 'Ubuntu 20.04'; Arch = 'ARM64'}
  274. [PSCustomObject]@{Slug = 'wsl-ubuntu-1804'; Name = 'Ubuntu 18.04'; Arch = 'x64'}
  275. #[PSCustomObject]@{Slug = 'wsl-ubuntu-1804-arm'; Name = 'Ubuntu 18.04'; Arch = 'ARM64'}
  276. [PSCustomObject]@{Slug = 'wsl-ubuntu-1604'; Name = 'Ubuntu 16.04'; Arch = 'x64'}
  277. [PSCustomObject]@{Slug = 'wsl-debian-gnulinux'; Name = 'Debian Stable'; Arch = 'x64'}
  278. [PSCustomObject]@{Slug = 'wsl-kali-linux-new'; Name = 'Kali Linux'; Arch = 'x64'}
  279. [PSCustomObject]@{Slug = 'wsl-opensuse-42'; Name = 'OpenSUSE 4.2'; Arch = 'x64'}
  280. [PSCustomObject]@{Slug = 'wsl-sles-12'; Name = 'SLES 12'; Arch = 'x64'}
  281. )
  282. $Menu = 'main'
  283. if ([Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544') {
  284. $Menu = 'admin'
  285. }
  286. while ($Menu -ne 'exit') {
  287. Clear-Host
  288. # 80 chars: ' '
  289. Write-Output ' :: WSL INSTALL SCRIPT FOR WINDOWS 10 AME'
  290. Write-Output ''
  291. Write-Output ' This script will help you install Windows Subsystem for Linux on your'
  292. Write-Output ' ameliorated installation of Windows 10'
  293. Write-Output ''
  294. Write-Output ' :: NOTE: Tested on Windows 10 1909, and Windows 10 AME 20H2'
  295. switch ($menu) {
  296. 'main' {
  297. Write-Output ''
  298. Write-Output ' :: Please enter a number from 1-3 to select an option from the list below'
  299. Write-Output ''
  300. Write-Output ' 1) Install a new WSL distro'
  301. Write-Output ' 2) Cancel a pending WSL installation'
  302. Write-Output ' 3) Exit'
  303. Write-Output ''
  304. Write-Host ' >> ' -NoNewLine
  305. $Input = $Host.UI.ReadLine()
  306. switch ($Input) {
  307. '1' {
  308. $Menu = 'select-distro'
  309. }
  310. '2' {
  311. $Menu = 'cancel'
  312. }
  313. '3' {
  314. $Menu = 'exit'
  315. }
  316. default {
  317. Write-Output ''
  318. Write-Host ' !! Invalid option selected' -ForegroundColor red
  319. Write-Output ''
  320. Write-Host ' Press enter to continue...' -NoNewLine
  321. $Host.UI.ReadLine()
  322. }
  323. }
  324. }
  325. 'select-distro' {
  326. Write-Output ''
  327. Write-Output ' :: Please enter a number from the list to select a distro to install'
  328. Write-Output ''
  329. $Max = 1
  330. $Distros | ForEach-Object {
  331. Add-Member -InputObject $_ -NotePropertyName Option -NotePropertyValue ([string]$Max) -Force
  332. #Write-Output " $Max) $($_.Name) ($($_.Arch))"
  333. Write-Output " $Max) $($_.Name)"
  334. $Max += 1
  335. }
  336. Write-Output " $Max) Return to main menu"
  337. Write-Output ''
  338. Write-Host ' >> ' -NoNewLine
  339. $Input = $Host.UI.ReadLine()
  340. if ($Input -eq ([string]$Max)) {
  341. $Menu = 'main'
  342. } else {
  343. $Distro = $Distros | Where-Object -Property Option -eq -Value $Input
  344. if ($Distro -eq $null) {
  345. Write-Output ''
  346. Write-Host ' !! Invalid option selected' -ForegroundColor Red
  347. Write-Output ''
  348. Write-Host ' Press enter to continue...' -NoNewLine
  349. $Host.UI.ReadLine()
  350. } else {
  351. $Menu = 'install-distro-confirm'
  352. }
  353. }
  354. }
  355. 'install-distro-confirm' {
  356. Write-Output ''
  357. #Write-Host " :: WARNING: Are you sure you want to install $($Distro.Name) ($($Distro.Arch))? (yes/no) " -NoNewLine
  358. Write-Host " :: WARNING: Are you sure you want to install $($Distro.Name)? (yes/no) " -NoNewLine
  359. $Input = $Host.UI.ReadLine()
  360. switch ($Input) {
  361. 'yes' {
  362. $Menu = 'install-distro'
  363. }
  364. 'no' {
  365. $Menu = 'select-distro'
  366. }
  367. default {
  368. Write-Output ''
  369. Write-Host ' !! Invalid input' -ForegroundColor Red
  370. Write-Output ''
  371. Write-Host ' Press enter to continue...' -NoNewLine
  372. $Host.UI.ReadLine()
  373. $Menu = 'select-distro'
  374. }
  375. }
  376. }
  377. 'install-distro' {
  378. Write-Output ''
  379. #Write-Output "Installing $($Distro.Name) ($($Distro.Arch))..."
  380. Write-Output "Installing $($Distro.Name)..."
  381. Install-WSL -LinuxDistribution ($Distro.Slug)
  382. $Menu = 'exit'
  383. }
  384. 'cancel' {
  385. Write-Output ''
  386. Write-Host ' :: WARNING: Are you sure you want to cancel all pending installs? (yes/no) ' -NoNewLine
  387. $Input = $Host.UI.ReadLine()
  388. switch ($Input) {
  389. 'yes' {
  390. Write-Output ''
  391. Install-WSL -Cancel
  392. }
  393. 'no' {
  394. Write-Output ''
  395. Write-Output ' Returning to main menu.'
  396. }
  397. default {
  398. Write-Output ''
  399. Write-Host ' !! Invalid input' -ForegroundColor Red
  400. }
  401. }
  402. Write-Output ''
  403. Write-Host ' Press enter to continue...' -NoNewLine
  404. $Host.UI.ReadLine()
  405. $Menu = 'main'
  406. }
  407. 'admin' {
  408. Write-Output ''
  409. Write-Host ' !! This script should NOT be run as Administrator' -ForegroundColor Red
  410. Write-Host ' !! Please close this window and run the script normally' -ForegroundColor Red
  411. Write-Output ''
  412. Write-Host ' Press enter to continue...' -NoNewLine
  413. $Host.UI.ReadLine()
  414. $Menu = 'exit'
  415. }
  416. default {
  417. Write-Output ''
  418. Write-Host " !! Invalid menu encountered ($Menu). Exiting" -ForegroundColor Red
  419. Write-Host ' !! THIS IS A BUG, PLEASE REPORT IT TO THE AME DEVS' -ForegroundColor Red
  420. $Menu = 'exit'
  421. }
  422. }
  423. }
  424. }