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 (http://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: http://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="http://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
FunctioncleanupFile ([string]$file) {if (test-path$file) {Remove-Item$file}}
# Delete the VM if it is around
If ((Get-VM|?name-eq$VMName).Count -gt0)
{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") -EncodingByte
sc"$($tempPath)\Bits\user-data" ([byte[]][char[]]"$userdata") -EncodingByte
# Convert cloud image to VHDX
&$qemuImgPathconvert-fqcow2"$($imageCachePath)\ubuntu-$($stamp).img"-Ovhdx-osubformat=dynamic$vhdx
Resize-VHD-Path$vhdx-SizeBytes50GB
# 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-MemoryStartupBytes2048mb-VHDPath$vhdx-Generation1 `
-SwitchName$virtualSwitchName-Path$vmPath|Out-Null
set-vm-Name$VMName-ProcessorCount2
Set-VMDvdDrive-VMName$VMName-Path$metaDataIso
Start-VM$VMName
# Open up VMConnect
Invoke-Expression"vmconnect.exe localhost `"$VMName`""
Cheers,
Ben