Skip to content

Instantly share code, notes, and snippets.

@airtonix
Created June 13, 2025 06:59
Show Gist options
  • Save airtonix/ec97ec8a93d73405367857b5dab451d0 to your computer and use it in GitHub Desktop.
Save airtonix/ec97ec8a93d73405367857b5dab451d0 to your computer and use it in GitHub Desktop.
Quickly provision new wsl instances
#!/usr/bin/env pwsh
# get named args from command line
# -- name <name> --user <user> --repo <repo>
# leave repo empty to prompt for it
# if name is not provided, use "ubuntu-<date>"
param (
[string]$name = "ubuntu-$((Get-Date).ToString('yyyy-MM-dd'))",
[string]$user = ($env:USERNAME -replace '[^a-zA-Z0-9]', '-').ToLower(),
)
#
# Loggers
#
function Log {
param (
[Parameter(Mandatory=$true)]
[string]$Text,
[ValidateSet("Black", "DarkBlue", "DarkGreen", "DarkCyan", "DarkRed", "DarkMagenta", "DarkYellow", "Gray", "DarkGray", "Blue", "Green", "Cyan", "Red", "Magenta", "Yellow", "White")]
[string]$Color = "White"
)
# Writer multiline text to the console with color
Write-Host " "
Write-Host $Text -ForegroundColor $Color
Write-Host " "
}
function Success {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Log $Text -Color "Green"
}
function Error {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Log $Text -Color "Red"
}
function Warning {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Log $Text -Color "Yellow"
}
function Info {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Log $Text -Color "Cyan"
}
#
# Core Functions
#
function Is-Ubuntu-Installed() {
# Run wsl --list and capture output
$wslOutput = wsl --list --quiet
# Define the exact line to match (e.g., a specific distribution name)
$exactLine = "$name"
# remove (Default) from each line
$wslOutput = $wslOutput |
ForEach-Object { $_ -replace '\(Default\)', '' } | # Remove (Default) from each line
Where-Object { $_ -ne '' }
foreach ($line in $wslOutput) {
if ($line -eq $exactLine) {
return $true
}
}
return $false
}
function Assert-Is-Ubuntu-Installed() {
if (-not (Is-Ubuntu-Installed)) {
Error "Ubuntu WSL2 '$name' is not installed. Please install it first."
exit 1
}
}
function Install-Ubuntu {
$exists = Is-Ubuntu-Installed -name "$name"
if ($exists) {
return
}
Info "Ubuntu WSL2 '$name' Installing."
Info "Installing Ubuntu WSL2 '$name'"
wsl --install "Ubuntu" --name "$name"
Success "Installation complete."
return "$name"
}
function Update-Ubuntu {
Assert-Is-Ubuntu-Installed
Info "Updating Ubuntu WSL2 '$name'"
wsl -d "$name" `
--user root `
--exec bash -c 'apt update && apt upgrade -y'
wsl -d "$name" `
--user root `
--exec bash -c 'apt install -y git curl wget zsh build-essential libssl-dev libffi-dev libbz2-dev libreadline-dev libreadline6-dev bzip2 zlib1g-dev'
if ($LASTEXITCODE -ne 0) {
Error "Failed to update Ubuntu WSL2 '$name'"
exit 1
}
Success "Ubuntu WSL2 '$name' updated successfully"
}
function Shutdown-Ubuntu() {
Assert-Is-Ubuntu-Installed
Info "Shutting down Ubuntu WSL2 '$name'"
wsl --shutdown
if ($LASTEXITCODE -ne 0) {
Error "Failed to shut down Ubuntu WSL2 '$name'"
exit 1
}
Success "Ubuntu WSL2 '$name' shut down successfully"
}
function Update-User() {
Assert-Is-Ubuntu-Installed
# does user exist? true or false
$exists = wsl -d "$name" `
--user root `
--exec bash -c "id $user" 2>&1
if ($exists -match "no such user") {
Write-Host "User '$user' does not exist. Creating user."
} else {
Write-Host "User '$user' already exists. Skipping user creation."
return
}
write-host "Creating user '$user' on Ubuntu WSL2 '$name'"
wsl -d "$name" `
--user root `
--exec bash -c '
useradd \
--create-home \
--shell /usr/bin/bash \
--groups adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev \
--password $(read -sp Password: pw; echo $pw | openssl passwd -1 -stdin) \
$user
'
wsl -d "$name" `
--user root `
--exec bash -c "echo '$user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
wsl -d "$name" `
--user root `
--exec bash -c "id $user && echo 'User $user created successfully' || echo 'Failed to create user $user'"
wsl -d "$name" `
--user root `
--exec bash -c "cat << EOF > /etc/wsl.conf
[user]
default=$user
EOF"
wsl --terminate "$name"
}
function Insert-IntoShellProfile() {
param (
[string]$profilePath = "/home/$user/.bashrc",
[string]$label = "Custom Insertion",
[string]$insertion = ""
)
Assert-Is-Ubuntu-Installed
# if no insertion is provided, return
if (-not $insertion) {
return
}
Info "Adding $label to shell profile '$profilePath'"
# Check if the insertion already exists in the profile
wsl -d "$name" `
--user "$user" `
--exec bash -c "grep -q '$insertion' $profilePath 2>/dev/null || echo '$insertion' >> $profilePath"
if ($LASTEXITCODE -ne 0) {
Error "Failed to add $label to shell profile '$profilePath' on Ubuntu WSL2 '$name' as '$user'"
exit 1
}
Success "$label added to shell profile '$profilePath' on Ubuntu WSL2 '$name' as '$user'"
}
# https://gist.github.com/onomatopellan/90024008a0d8c8a2ed6fa57e8b64df54
function Create-SharedDrive() {
# download alpine linux image
url="https://github.com/yuk7/AlpineWSL/releases/download/3.11.5-1/Alpine.zip"
# download the file
$zipFile = "Alpine.zip"
if (-not (Test-Path $zipFile)) {
Info "Downloading Alpine Linux image from $url"
Invoke-WebRequest -Uri $url -OutFile $zipFile
} else {
Info "Alpine Linux image already downloaded."
}
# unzip it
Expand-Archive -Path $zipFile -DestinationPath "Alpine" -Force
# run Alpine.exe
$alpineExe = "Alpine\Alpine.exe"
if (-not (Test-Path $alpineExe)) {
Error "Alpine executable not found at $alpineExe"
exit 1
}
Info "Running Alpine executable to create shared drive"
Start-Process -FilePath $alpineExe -ArgumentList "--install" -Wait
if ($LASTEXITCODE -ne 0) {
Error "Failed to run Alpine executable to create shared drive"
exit 1
}
Info "Create your shared drive using Alpine Linux on WSL2"
Write-Host "You're about to create a new user in the Alpine Distro."
Write-Host "Please use the same username '$user':"
Write-Host "Press Enter to continue..."
Read-Host
Start-Process -FilePath $alpineExe -ArgumentList "--set-default" -Wait
Success "Shared drive created successfully using Alpine Linux on WSL2"
# modify our ~/.profile to automount the shared drive
Insert-IntoShellProfile `
-profilePath "/home/$user/.profile" `
-label "Alpine Shared Drive Mount" `
-insertion "[ ! /mnt/Store ] && { mkdir -p /mnt/Store && wsl.exe -d Alpine mount --bind /home/$user /mnt/Store }"
Shutdown-Ubuntu
}
#
# Tasks
#
# Installs mise on Ubuntu WSL2
function Task-InstallMise() {
Assert-Is-Ubuntu-Installed
Info "Installing mise on Ubuntu WSL2 '$name' as '$user'"
wsl -d "$name" `
--user "$user" `
--exec bash -c "curl https://mise.run | sh"
wsl -d "$name" `
--user "$user" `
--exec bash -c "mise settings experimental=true"
$activationString = 'eval "$(mise activate)"'
Insert-IntoShellProfile `
-profilePath "/home/$user/.bashrc" `
-label "mise activation" `
-insertion $activationString
if ($LASTEXITCODE -ne 0) {
Error "Failed to install mise on Ubuntu WSL2 '$name' as '$user'"
exit 1
}
Success "mise installed successfully on Ubuntu WSL2 '$name' as '$user'"
}
# Just needs to run tasks in tmp, but in recent wsl2 ubuntu versions
# there's an issue where tmp dir is not writable by the user
# so we point it to somewhere else
function Task-FixXdgJustfile() {
# add tmpdir to the .bashrc file
Assert-Is-Ubuntu-Installed
Info "Fixing xdg justfile on Ubuntu WSL2 '$name' for user '$user'"
$bashrcPath = "/home/$user/.bashrc"
$exportString = "export XDG_RUNTIME_DIR=`$(mktemp -d /tmp/xdg-runtime-XXXXXX)"
Insert-IntoShellProfile `
-profilePath $bashrcPath `
-label "XDG Runtime Dir Fix" `
-insertion $exportString
}
$FILE_MiseHuskyEnv = @"
#!/usr/bin/env bash
eval "$(mise activate --quiet)"
"@
# we need a ~/.huskyrc file that activates mise
function Task-HuskyMiseEnv() {
Assert-Is-Ubuntu-Installed
Info "Creating Husky mise environment on Ubuntu WSL2 '$name' for user '$user'"
$huskyrcPath = "/home/$user/.huskyrc"
Insert-IntoShellProfile `
-profilePath $huskyrcPath `
-label "Husky mise environment" `
-insertion $FILE_MiseHuskyEnv
Success "Husky mise environment created successfully at $huskyrcPath on Ubuntu WSL2 '$name' as '$user'"
}
$FILE_StarshipUpdateCheck = @"
# Check for updates and notify if available
function check_updates() {
# if not apt is installed, return
if ! command -v apt &> /dev/null; then
return
fi
# Check for updates
updates=`$(apt list --upgradable 2>/dev/null | wc -l)
if [ "$updates" -gt 1 ]; then
echo "Updates available: `$((updates - 1))"
return 0
fi
echo "No updates available."
}
function starship_precmd_user_func() {
check_updates
}
starship_precmd_user_func="starship_precmd_user_func"
"@
# Creates a script that runs in the prompt
# each time this runs it tests if apt
function Task-CreateAptUpdatePrompt() {
Assert-Is-Ubuntu-Installed
# we're going to install starship and define
# a user prompt that checks for outstanding updates
Info "Creating update prompt for Ubuntu WSL2 '$name' as '$user'"
$bashrcPath = "/home/$user/.bashrc"
Insert-IntoShellProfile `
-profilePath $bashrcPath `
-label "Update Prompt" `
-insertion $FILE_StarshipUpdateCheck
if ($LASTEXITCODE -ne 0) {
Error "Failed to create update prompt in shell profile '$bashrcPath' on Ubuntu WSL2 '$name' as '$user'"
exit 1
}
Success "Update prompt created in shell profile '$bashrcPath' on Ubuntu WSL2 '$name' as '$user'"
}
#
# Main script execution
#
if (-not (Get-Command wsl -ErrorAction SilentlyContinue)) {
Write-Host "WSL is not installed. Please install WSL first."
exit 1
}
write-host "Provisioning WSL2 Ubuntu '$name' for user '$user'"
write-host "Using repo '$repo'"
Install-Ubuntu
Update-Ubuntu
Update-User
Task-InstallMise
Task-FixXdgJustfile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment