Last active
November 19, 2022 06:02
-
-
Save hydrz/0097affe6f5a93fd0fa6f06551c61204 to your computer and use it in GitHub Desktop.
Create Linux Cloud Image on Hyper-v use Powershell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -RunAsAdministrator | |
[CmdletBinding()] | |
param( | |
[Alias('Path')] | |
[string]$WorkPath, | |
[ValidateScript({ | |
$existingVm = Get-VM -Name $_ -ErrorAction SilentlyContinue | |
if (-not $existingVm) { | |
return $True | |
} | |
throw "There is already a VM named '$VMName' in this server." | |
})] | |
[Alias('Name')] | |
[string]$VMName, | |
[Alias('Password')] | |
[string]$RootPassword = 'root', | |
[Alias('Rsa')] | |
[string]$RootPublicKey, | |
[Alias('Switch')] | |
[string]$SwitchName, | |
[Alias('Cpu')] | |
[int]$ProcessorCount = 2, | |
[Alias('Memory')] | |
[int64]$MemoryStartupBytes = 4GB, | |
[Alias('Disk')] | |
[uint64]$DiskSizeBytes = 40GB, | |
# Extracted oscdimg From ADK https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install | |
[string]$OscdImgURL = 'https://github.com/hydrz/oscdimg/raw/main/oscdimg-amd64.zip', | |
# Download qemu-img from here: http://www.cloudbase.it/qemu-img-windows/ | |
[string]$QemuImgURL = 'https://cloudbase.it/downloads/qemu-img-win-x64-2_3_0.zip', | |
[Alias('Image')] | |
[string]$DownloadImageURL = "https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cloud-images/jammy/current/jammy-server-cloudimg-amd64.img", | |
# Custom cloud-init config file path | |
[string]$CloudInit | |
) | |
if (-not $WorkPath) { | |
$WorkPath = Read-Host -Prompt 'WorkPath (default: current directory)' | |
if (-not $WorkPath) { | |
$WorkPath = $PWD | |
} | |
} | |
if (-not $VMName) { | |
$VMName = Read-Host -Prompt 'VMName (required)' | |
if (-not $VMName) { | |
throw 'VMName is required.' | |
} | |
} | |
if (-not $RootPublicKey) { | |
if (Test-Path -Path "$($env:USERPROFILE)\.ssh\id_rsa.pub") { | |
$RootPublicKey = Get-Content -Path "$($env:USERPROFILE)\.ssh\id_rsa.pub" | |
} | |
} | |
if (-not $SwitchName) { | |
# Get all virtual switches with number | |
$switches = Get-VMSwitch | Select-Object -Property Name, SwitchType, NetAdapterInterfaceDescription | |
$switches | ForEach-Object { | |
$_ | Add-Member -MemberType NoteProperty -Name 'Number' -Value ($switches.IndexOf($_) + 1) | |
} | |
$switches | Format-Table -AutoSize | |
$SwitchName = Read-Host -Prompt 'SwitchName (default: 1)' | |
if (-not $SwitchName) { | |
$SwitchName = $switches[0].Name | |
} | |
else { | |
$SwitchName = $switches[$SwitchName - 1].Name | |
} | |
} | |
# Set working directories | |
$vmDir = "$WorkPath\vms" | |
$toolsDir = "$WorkPath\tools" | |
$cacheDir = "$WorkPath\cache" | |
# Check Paths | |
if (!(Test-Path $WorkPath)) { mkdir $WorkPath } | |
if (!(Test-Path $vmDir)) { mkdir $vmDir } | |
if (!(Test-Path $toolsDir)) { mkdir $toolsDir } | |
if (!(Test-Path $cacheDir)) { mkdir $cacheDir } | |
# Download oscdimg | |
$oscdImgPath = "$toolsDir\oscdimg\oscdimg.exe" | |
if (!(Test-Path $oscdImgPath)) { | |
Write-Host "Downloading oscdimg from $OscdImgURL" | |
$oscdImgZip = "$cacheDir\oscdimg.zip" | |
Invoke-WebRequest -Uri $OscdImgURL -OutFile $oscdImgZip | |
Expand-Archive -Path $oscdImgZip -DestinationPath $toolsDir\oscdimg | |
} | |
# Download qemu-img | |
$qemuImgPath = "$toolsDir\qemu\qemu-img.exe" | |
if (!(Test-Path $qemuImgPath)) { | |
Write-Host "Downloading qemu-img..." | |
$qemuImgZipPath = "$cacheDir\qemu.zip" | |
Invoke-WebRequest -Uri $QemuImgURL -OutFile $qemuImgZipPath | |
Expand-Archive -Path $qemuImgZipPath -DestinationPath $toolsDir\qemu | |
Remove-Item -Path $qemuImgZipPath | |
} | |
# Download the Ubuntu image | |
$fileName = $DownloadImageURL.Split("/")[-1].Split(".")[0] | |
$imagePath = "$cacheDir\$fileName.img" | |
if (!(Test-Path $cacheDir)) { mkdir $cacheDir } | |
if (!(Test-Path $imagePath)) { | |
Write-Host "Downloading image from $DownloadImageURL" | |
Invoke-WebRequest $DownloadImageURL -UseBasicParsing -OutFile $imagePath | |
} | |
# Delete the old VM | |
if ((Get-VM | Where-Object name -eq $VMName).Count -gt 0) { Stop-VM $VMName -TurnOff -Confirm:$false -Passthru | Remove-VM -Force } | |
$vmPath = "$($vmDir)\$($VMName)" | |
if (Test-Path $vmPath) { Remove-Item $vmPath -Recurse -Force } | |
mkdir $vmPath | |
# Create the seed ISO | |
$metaData = @" | |
instance-id: $($VMName) | |
local-hostname: $($VMName) | |
"@ | |
$userData = @" | |
#cloud-config | |
{} | |
"@ | |
# get the cloud-init config and replace unix line endings with windows line endings | |
if ($CloudInit) { | |
$userData = (Get-Content -Path $CloudInit -Raw) -replace "`r`n", "`r" | |
} | |
$vendorData = @" | |
#cloud-config | |
users: | |
- name: root | |
ssh_pwauth: False | |
ssh_authorized_keys: | |
- $($RootPublicKey) | |
chpasswd: | |
list: | | |
root:$($RootPassword) | |
expire: False | |
timezone: Asia/Shanghai | |
apt: | |
primary: | |
- arches: [default] | |
uri: https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ | |
"@ | |
mkdir "$vmPath\seed" | |
Set-Content "$vmPath\seed\meta-data" ([byte[]][char[]] "$metaData") -Encoding Byte | |
Set-Content "$vmPath\seed\user-data" ([byte[]][char[]] "$userData") -Encoding Byte | |
Set-Content "$vmPath\seed\vendor-data" ([byte[]][char[]] "$vendorData") -Encoding Byte | |
$seedISO = "$vmPath\seed.iso" | |
& $oscdImgPath $vmPath\seed $seedISO -j2 -lcidata | |
Remove-Item "$vmPath\seed" -Recurse -Force | |
# Convert cloud image to VHDX | |
$vhdx = "$($vmPath)\$($fileName).vhdx" | |
& $qemuImgPath convert -f qcow2 $imagePath -O vhdx -o subformat=dynamic $vhdx | |
Resize-VHD -Path $vhdx -SizeBytes $DiskSizeBytes | |
# Create the VM | |
New-VM -Name $VMName -Generation 1 -MemoryStartupBytes $MemoryStartupBytes -SwitchName $SwitchName -VHDPath $vhdx | |
Set-VM -Name $VMName -ProcessorCount $ProcessorCount | |
Set-VMDvdDrive -VMName $VMName -Path $seedISO | |
Set-VM -Name $VMName -AutomaticStartAction StartIfRunning -AutomaticStopAction TurnOff | |
# Generate the MAC address | |
$vmMacAddress = "88:88:88:" + ((0..2 | ForEach-Object { (Get-Random -Minimum 1 -Maximum 255).ToString("X2") }) -join ':') | |
Set-VMNetworkAdapter -VMName $VMName -StaticMacAddress $vmMacAddress | |
Start-VM $VMName |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Run from gist
iex "& { $(iwr https://gist.githubusercontent.com/hydrz/0097affe6f5a93fd0fa6f06551c61204/raw/vm-build.ps1) }"
Or download then run