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 (  My script checks for the latest one and pulls it down.
  • Cloud-init: The Ubuntu cloud images come preloaded with the Cloud-init client ( – 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 –

# 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:

$qemuImgPath = “C:\Working Space\qemu-img\qemu-img.exe”


# Update this to the release of Ubuntu that you want

$ubuntuPath = “”


$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 = @”


password: $($GuestAdminPassword)


 – [ 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`””