Skip to content

Instantly share code, notes, and snippets.

@airtonix
Last active July 29, 2025 06:09
Show Gist options
  • Select an option

  • Save airtonix/ec97ec8a93d73405367857b5dab451d0 to your computer and use it in GitHub Desktop.

Select an option

Save airtonix/ec97ec8a93d73405367857b5dab451d0 to your computer and use it in GitHub Desktop.
Quickly provision new wsl instances
#!/usr/bin/env pwsh
function List-AvailableDistros {
# List available WSL distros and only include first column
$distroList = wsl --list --online | Where-Object { $_ -ne "" } | Select-Object -Skip 3 | ForEach-Object { $_.Split(" ")[0] } | Where-Object { $_ -ne "" }
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to list available WSL distros."
exit 1
}
return $distroList
}
# 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 (
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]
$name = "ubuntu-$((Get-Date).ToString('yyyy-MM-dd'))",
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]
$user = ($env:USERNAME -replace '[^a-zA-Z0-9]', '-').ToLower(),
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
$distro = "Ubuntu",
[string]
$repo = ""
)
#
# Loggers
#
function Write-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 Write-Success {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Write-Log $Text -Color "Green"
}
function Write-Error {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Write-Log $Text -Color "Red"
}
function Write-Warning {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Write-Log $Text -Color "Yellow"
}
function Write-Info {
param (
[Parameter(Mandatory=$true)]
[string]$Text
)
Write-Log $Text -Color "Cyan"
}
#
# Core Functions
#
function Get-IsDistroInstalled() {
# 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-IsDistroInstalled() {
if (-not (Get-IsDistroInstalled)) {
Write-Error "Container '$name' is not installed. Please install it first."
exit 1
}
}
function Install-Distro {
$exists = Get-IsDistroInstalled -name "$name"
if ($exists) {
return
}
Write-Info "Creating WSL2 Container: '$name'"
wsl --install "$distro" --name "$name"
Write-Success "Installation complete."
return "$name"
}
function Update-Ubuntu {
Assert-IsDistroInstalled
Write-Info "Updating Container '$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) {
Write-Error "Failed to update Container '$name'"
exit 1
}
Write-Success "Container '$name' updated successfully"
}
function Stop-Container() {
Assert-IsDistroInstalled
Write-Info "Shutting down Container '$name'"
wsl --shutdown
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to shut down Container '$name'"
exit 1
}
Write-Success "Container '$name' shut down successfully"
}
function Update-User() {
Assert-IsDistroInstalled
# 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-Info "User '$user' does not exist. Creating user."
} else {
Write-Info "User '$user' already exists. Skipping user creation."
return
}
Write-Info "Creating user '$user' on Container '$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 Write-IntoShellProfile() {
param (
[string]$profilePath = "/home/$user/.bashrc",
[string]$label = "Custom Insertion",
[string]$insertion = ""
)
Assert-IsDistroInstalled
# if no insertion is provided, return
if (-not $insertion) {
return
}
Write-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) {
Write-Error "Failed to add $label to shell profile '$profilePath' on Container '$name' as '$user'"
exit 1
}
Write-Success "$label added to shell profile '$profilePath' on Container '$name' as '$user'"
}
# https://gist.github.com/onomatopellan/90024008a0d8c8a2ed6fa57e8b64df54
function Add-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)) {
Write-Error "Alpine executable not found at $alpineExe"
exit 1
}
Write-Info "Running Alpine executable to create shared drive"
Start-Process -FilePath $alpineExe -ArgumentList "--install" -Wait
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to run Alpine executable to create shared drive"
exit 1
}
Write-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
Write-Success "Shared drive created successfully using Alpine Linux on WSL2"
# modify our ~/.profile to automount the shared drive
Write-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 }"
Close-Container
}
#
# Tasks
#
# Installs mise on Ubuntu WSL2
function Install-Mise() {
Assert-IsDistroInstalled
Write-Info "Installing mise on Container '$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"
wsl -d "$name" `
--user "$user" `
--exec bash -c "mise generate config > ~/.mise/config.toml"
wsl -d "$name" `
--user "$user" `
--exec bash -c "mise trust --all"
$activationString = 'eval "$(mise activate bash --quiet)"'
Write-IntoShellProfile `
-profilePath "/home/$user/.bashrc" `
-label "mise activation" `
-insertion $activationString
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to install mise on Container '$name' as '$user'"
exit 1
}
Write-Success "mise installed successfully on Container '$name' as '$user'"
}
$FILE_MiseHuskyEnv = @"
#!/usr/bin/env bash
eval "\$\(mise activate bash --quiet)"
"@
# we need a ~/.huskyrc file that activates mise
function Install-MiseHuskyConfig() {
Assert-IsDistroInstalled
Write-Info "Creating Husky mise environment on Container '$name' for user '$user'"
$huskyrcPath = "/home/$user/.huskyrc"
Insert-IntoShellProfile `
-profilePath $huskyrcPath `
-label "Husky mise environment" `
-insertion $FILE_MiseHuskyEnv
Write-Success "Husky mise environment created successfully at $huskyrcPath on Container '$name' as '$user'"
}
function Install-Comtrya(){
Assert-IsDistroInstalled
Write-Info "Installing Comtrya"
# if no repo is provided, prompt for it
if (-not $repo) {
$repo = Read-Host "Enter the Comtrya repository URL (or leave empty to skip)"
if (-not $repo) {
Write-Host "No repository URL provided. Skipping Comtrya installation."
return
}
}
wsl -d "$name" `
--user "$user" `
--exec bash -c "mise use ubi:comtrya/comtrya"
wsl -d "$name" `
--user "$user" `
--exec bash -c "comtrya -d $repo apply"
}
function Install-SshKey() {
Assert-IsDistroInstalled
Write-Info "Installing SSH key for user '$user' on Container '$name'"
# Check if the SSH key already exists
$sshKeyPath = "/home/$user/.ssh/id_rsa"
if (Test-Path -Path $sshKeyPath) {
Write-Info "SSH key already exists at $sshKeyPath. Skipping key generation."
return
}
# Generate a new SSH key
wsl -d "$name" `
--user "$user" `
--exec bash -c "ssh-keygen -t rsa -b 4096 -f $sshKeyPath -N ''"
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to generate SSH key for user '$user' on Container '$name'"
exit 1
}
Write-Success "SSH key generated successfully for user '$user' on Container '$name'"
}
function Run-Comtrya() {
Assert-IsDistroInstalled
Write-Info "Running Comtrya on Container '$name' for user '$user'"
# if no repo is provided, prompt for it
if (-not $repo) {
$repo = Read-Host "Enter the Comtrya repository URL (or leave empty to skip)"
if (-not $repo) {
Write-Host "No repository URL provided. Skipping Comtrya run."
return
}
}
wsl -d "$name" `
--user "$user" `
--exec bash -c "comtrya -d $repo apply"
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to run Comtrya on Container '$name' for user '$user'"
exit 1
}
Write-Success "Comtrya run completed successfully on Container '$name' for user '$user'"
}
#
# Main script execution
#
if (-not (Get-Command wsl -ErrorAction SilentlyContinue)) {
Write-Host "WSL is not installed. Please install WSL first."
exit 1
}
function Provision(){
write-host "Provisioning WSL2 Ubuntu '$name' for user '$user'"
write-host "Using repo '$repo'"
Install-Distro
Update-Ubuntu
Update-User
Install-Mise
Install-MiseHuskyConfig
Install-Comtrya
}
# Process command line positional arguments
$command = $args[0]
Write-Info "Provisioning Container '$name' for user '$user' with repo '$repo'"
Write-Info "Command: $command"
# if ($command) {
# switch ($command) {
# "install" {
# Install-Distro
# }
# "update" {
# Update-Ubuntu
# }
# "user" {
# Update-User
# }
# "provision" {
# Provision
# }
# "mise" {
# Install-Mise
# Install-MiseHuskyConfig
# }
# "comtrya" {
# if (-not $repo) {
# $repo = Read-Host "Enter the Comtrya repository URL (or leave empty to skip)"
# }
# Install-Comtrya
# }
# default {
# Write-Info "Unknown command: $command"
# Write-Host "Available commands: install, update, user, provision, mise, comtrya"
# }
# }
# } else {
# # Default action when no command is provided
# Provision
# }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment