Building a Daily Ubuntu image for Hyper-V
After getting my Windows Image Factory up and running – the next thing I started to look at was how to create an “always up-to-date Ubuntu image”. To solve this problem I ended up taking a very different route. Some of the tools / services I used are as follows:
- Ubuntu Cloud Images: Canonical provides daily builds of Ubuntu server for cloud deployments (https://cloud-images.ubuntu.com/). My script checks for the latest one and pulls it down.
- Cloud-init: The Ubuntu cloud images come preloaded with the Cloud-init client (https://cloudinit.readthedocs.org/en/latest/) – so I use this to customize the image.
- Qemu-img: As I mentioned yesterday, qemu-img is a great tool that helps you to convert KVM images (like the ones provided by Canonical) into Hyper-V Images.
- OSCDimg: As part of this script I need to create an ISO image on the fly. I do this using OSCDimg – which is part of the deployment tools in the Windows ADK
This script will then:
- Check if we already have the latest Ubuntu cloud image
- If not – download the latest
- Create Cloud-init metadata files
- Convert the Qemu image to Hyper-V
- Create an ISO with the Cloud-init metadata files in it
- Create a virtual machine
- Start the virtual machine, and connect to it
The script is also attached to the end of this blog post.
$tempPath = [System.IO.Path]::GetTempPath() + [System.Guid]::NewGuid().ToString()
# ADK Download - https://www.microsoft.com/en-us/download/confirmation.aspx?id=39982
# You only need to install the deployment tools
$oscdimgPath = "C:\Program Files (x86)\Windows Kits\8.1\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe"
# Download qemu-img from here: https://www.cloudbase.it/qemu-img-windows/
$qemuImgPath = "C:\Working Space\qemu-img\qemu-img.exe"
# Update this to the release of Ubuntu that you want
$ubuntuPath = "https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64"
$GuestOSName = "Hyper-V-VM"
$GuestOSID = "iid-123456"
$GuestAdminPassword = "P@ssw0rd"
$VMName = "Ubuntu Test"
$virtualSwitchName = "Virtual Switch"
$vmPath = "C:\Working Space\VM"
$imageCachePath = "C:\Working Space"
$vhdx = "$($vmPath)\test.vhdx"
$metaDataIso = "$($vmPath)\metadata.iso"
# Get the timestamp of the latest build on the Ubuntu cloud-images site
$stamp = (Invoke-WebRequest "$($ubuntuPath).manifest").BaseResponse.LastModified.ToFileTimeUtc()
$metadata = @"
instance-id: $($GuestOSID)
local-hostname: $($GuestOSName)
"@
$userdata = @"
#cloud-config
password: $($GuestAdminPassword)
runcmd:
- [ useradd, -m, -p, "", ben ]
- [ chage, -d, 0, ben ]
"@
# Check Paths
if (!(test-path $vmPath)) {mkdir $vmPath}
if (!(test-path $imageCachePath)) {mkdir $imageCachePath}
# Helper function for no error file cleanup
Function cleanupFile ([string]$file) {if (test-path $file) {Remove-Item $file}}
# Delete the VM if it is around
If ((Get-VM | ? name -eq $VMName).Count -gt 0)
{stop-vm $VMName -TurnOff -Confirm:$false -Passthru | Remove-VM -Force}
cleanupFile $vhdx
cleanupFile $metaDataIso
# Make temp location
md -Path $tempPath
md -Path "$($tempPath)\Bits"
if (!(test-path "$($imageCachePath)\ubuntu-$($stamp).img")) {
# If we do not have a matching image - delete the old ones and download the new one
Remove-Item "$($imageCachePath)\ubuntu-*.img"
Invoke-WebRequest "$($ubuntuPath)-disk1.img" -UseBasicParsing -OutFile "$($imageCachePath)\ubuntu-$($stamp).img"
}
# Output meta and user data to files
sc "$($tempPath)\Bits\meta-data" ([byte[]][char[]] "$metadata") -Encoding Byte
sc "$($tempPath)\Bits\user-data" ([byte[]][char[]] "$userdata") -Encoding Byte
# Convert cloud image to VHDX
& $qemuImgPath convert -f qcow2 "$($imageCachePath)\ubuntu-$($stamp).img" -O vhdx -o subformat=dynamic $vhdx
Resize-VHD -Path $vhdx -SizeBytes 50GB
# Create meta data ISO image
& $oscdimgPath "$($tempPath)\Bits" $metaDataIso -j2 -lcidata
# Clean up temp directory
rd -Path $tempPath -Recurse -Force
# Create new virtual machine and start it
new-vm $VMName -MemoryStartupBytes 2048mb -VHDPath $vhdx -Generation 1 `
-SwitchName $virtualSwitchName -Path $vmPath | Out-Null
set-vm -Name $VMName -ProcessorCount 2
Set-VMDvdDrive -VMName $VMName -Path $metaDataIso
Start-VM $VMName
# Open up VMConnect
Invoke-Expression "vmconnect.exe localhost `"$VMName`""
Cheers,
Ben