Public/Initialize-WsCreatorMachine.ps1
|
function Initialize-WsCreatorMachine { <# .SYNOPSIS Creates a machine folder from a template, with your own user, SSH key, port and subnet. .DESCRIPTION Copies the template into <WS_CREATOR_MACHINES>\<Name>\, rewrites the identity values where ws-creator checks them (user, hostname, SSH port and key, podman subnet, Location), records where the copy comes from (Template, TemplateSource, TemplateVersion, TemplateHash), then validates the result. Nothing is written if validation fails. The machine is yours afterwards: edit it freely, then create the distro with 'ws-creator new <Name> -Apply'. Templates come from WS_CREATOR_TEMPLATES (local folders or git repos), then the module. .PARAMETER Template Template name, as shown by 'ws-creator list'. .PARAMETER Name Name of the new machine: folder, distro and hostname. .PARAMETER User Linux user. Default: the Windows user name in lowercase, reduced to [a-z0-9_-]. .PARAMETER PublicKey Public key file in ~\.ssh. Default: id_ed25519.pub. .PARAMETER Port SSH port, unique per machine. Default: the first free one from 2222. .PARAMETER Subnet Podman subnet, unique per machine. Default: the first free 10.89.N.0/24. .PARAMETER Location Folder of the virtual disk. Default: WS_CREATOR_LOCATION, otherwise C:\WSL. .PARAMETER Apply Write the machine folder. Without it, only shows what would be written. .EXAMPLE ws-creator init dev my-machine Shows the values init would use. .EXAMPLE ws-creator init agent-sandbox sandbox -Port 2250 -Apply .LINK https://github.com/akramgassem/ws-creator/blob/main/docs/design/catalogue.md #> param( [Parameter(Mandatory)][string]$Template, [Parameter(Mandatory)][string]$Name, [string]$User, [string]$PublicKey = 'id_ed25519.pub', [int]$Port, [string]$Subnet, [string]$Location, [switch]$Apply ) $script:Apply = $Apply.IsPresent if ($Name -notmatch '^[a-z0-9][a-z0-9-]{0,62}$') { throw "Machine name '$Name': lowercase letters, digits and '-' only (it is also the hostname)" } $target = Get-MachineDir $Name if (Test-Path -Path $target) { throw "Machine '$Name' already exists: $target" } if (Test-Distro $Name) { throw "A WSL distro named '$Name' already exists" } foreach ($source in Get-TemplateSource) { Update-TemplateCatalog $source } $t = Get-Template | Where-Object { $_.Name -eq $Template -and -not $_.Shadowed } if (-not $t) { throw "Template '$Template' not found (ws-creator list)" } $tm = Import-PowerShellDataFile -Path (Join-Path -Path $t.Dir -ChildPath 'machine.psd1') $pub = Join-Path -Path $script:SshDir -ChildPath $PublicKey if (-not (Test-Path -Path $pub)) { throw "Public key not found: ~\.ssh\$PublicKey (ssh-keygen -t ed25519, or -PublicKey)" } $values = @{ Name = $Name User = if ($User) { $User } else { ConvertTo-LinuxUser $env:USERNAME } PublicKey = $PublicKey Key = ((Get-Content -Path $pub -Raw).Trim() -split '\s+')[0..1] -join ' ' Location = if ($Location) { $Location } elseif ($env:WS_CREATOR_LOCATION) { $env:WS_CREATOR_LOCATION } else { 'C:\WSL' } } if ($values.User -notmatch '^[a-z_][a-z0-9_-]{0,31}$') { throw "User '$($values.User)' is not a valid Linux user name" } if ($tm.Ssh) { $values.Port = if ($Port) { $Port } else { Get-FreeSshPort } } if ($tm.PodmanSubnet) { $values.Subnet = if ($Subnet) { $Subnet } else { Get-FreePodmanSubnet } } $work = Join-Path -Path ([IO.Path]::GetTempPath()) -ChildPath "ws-creator-init-$([guid]::NewGuid().ToString('n'))" $copy = Join-Path -Path $work -ChildPath $Name try { New-Item -ItemType Directory -Force -Path $work | Out-Null Copy-Item -Recurse -Path $t.Dir -Destination $copy Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -Path (Join-Path -Path $copy -ChildPath '.git') $leftovers = @(Set-MachineIdentity $copy $values) Add-MachineProvenance $copy $t $m = Get-Machine $Name -Dir $copy Test-MachineConflict $m Write-Host "Template : $($t.Name) ($($t.Source)$(if ($t.Version) { " @ $($t.Version)" }))" Write-Host "User : $($values.User)" if ($values.Port) { Write-Host "SSH : port $($values.Port), key ~\.ssh\$PublicKey" } if ($values.Subnet) { Write-Host "Podman : $($values.Subnet)" } Write-Host "Disk : $(Join-Path -Path $values.Location -ChildPath $Name)" foreach ($line in $leftovers) { Write-Warning "still holds the template's user, check it: $line" } Invoke-Step "write $target" { New-Item -ItemType Directory -Force -Path $script:MachinesDir | Out-Null Copy-Item -Recurse -Path $copy -Destination $target } if ($Apply) { Write-Host "Next: edit $target if needed, then ws-creator new $Name -Apply" } } finally { Remove-Item -Recurse -Force -ErrorAction SilentlyContinue -Path $work } } |