I recently came to learn about a really cool project called Tiny 11 which is a stripped down version of Windows 11 Pro 22H2 that can run with just 2GB of memory and 8GB of storage. While you would probably not use this for production workloads, it could be interesting for those with homelabs and can even for demo purposes.
It's finally here!
Based off of Windows 11 Pro 22H2, tiny11 has everything you need for a comfortable computing experience without the bloat and clutter of a standard Windows installation.
— NTDEV @*protected email* (@NTDEV_) February 2, 2023
Disclaimer: Tiny 11 is not an official project from Microsoft, use at your own risk. If you are interested in creating an optimized Windows 10 or 11 image, you can also check out these VMware resources here and here to produce a similiar size image using official Microsoft tools and images.
UPDATE (02/16/23) - There is now an Arm version for Tiny 11, which is great for anyone using the ESXi-Arm Fling and the technique in this blog post would also apply.
🙌 Updated Tiny 11 Arm64 (tiny11a64 r1.iso) now works perfectly with @esxi_arm 💪
Thanks @NTDEV_ for the Arm version & quick fix!#ESXionARM pic.twitter.com/03TV69wMIq
— William Lam (@*protected email*) (@lamw) February 16, 2023
After performing a manual installation of Tiny 11, I thought it would be neat if I could build a Virtual Appliance (OVA) for Tiny 11 and also add custom OVF properties, which would allow anyone to customize the OS further without having to rely on any external tooling.
In fact, I had demonstrated this concept back in 2019 using Windows Server 2016 in this blog post and I figure it should be easy enough to also do the same for Tiny 11 or even standard Windows 11! With a bit of trial/error, I was indeed able to create a simple Tiny 11 OVA that includes the following OVF properties as shown in the screenshot below.
My initial goal was to fully automate the building of a Tiny 11 OVA with custom OVF properties using Packer. However, after many attempts, I was not able to figure out the correct autoattended.xml configuration and decided on a semi-manual approach which is detailed in the instructions below. If anyone is able to figure out how to get Tiny 11 installed via Packer, then I may revisit this topic and automate the remainder of the setup.
Note: The instructions below are not specific to Tiny 11 in any way and is also applicable to standard Windows 10 or 11 image.
Step 1 - Download the latest Tiny 11 ISO from Internet Archive, which now requires a login OR you can use standard Windows 11 ISO image.
Step 2 - Create a standard Windows 11 VM in vCenter Server (2 vCPU, 2GB memory and 8GB storage minimum) and install Tiny 11.
Step 3 - Install PowerShell Core, which is needed to run our example guest customization PowerShell script. You can also install any additional packages that you want included within the appliance.
Step 4 - Download the customize-windows-desktop-guest.ps1 script and place it on the desktop or another desired location, but make a note of the path as we will need that in the next step.
Step 5 - Create a new schedule task that will run our customization script during startup. The following PowerShell snippet can be used to automate the creation of the schedule task and you will need to updated with the values based on your own setup. Since we need the script to run without an interactive login, we will need to provide the administrative credentials to be able to register our schedule task.
In my example, the administrative user is called VMware and I have placed the customization script in C:\Users\VMware\Desktop and if you change the location from Step 4, just make sure you update the correct path.
$action = New-ScheduledTaskAction -Execute 'C:\Program Files\PowerShell\7\pwsh.exe' -Argument '-ExecutionPolicy Bypass C:\Users\VMware\Desktop\customize-guest.ps1' $trigger = New-ScheduledTaskTrigger -AtStartup $principal = New-ScheduledTaskPrincipal -UserId 'VMware' -RunLevel Highest -LogonType Password $setting = New-ScheduledTaskSettingsSet $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $setting -Description "Guest Customization via OVF Properties" Register-ScheduledTask -TaskName "OVF Customize" -InputObject $task -TaskPath "My-Custom-Tasks" -User VMware -Password VMware1!
If the operation completed successfully, we can view our custom schedule task under the "My-Custom-Tasks" folder as shown in the screenshot below.
At this point, we are now ready to shutdown our Tiny 11 VM and export to an OVF.
Step 6 - While you can use the vSphere UI to export the VM to OVF, I highly recommend using OVFTool as it is easier and more efficient, especially not having to wait for each file to finish downloading before the next can proceed and avoiding any timeouts.
Here is an example command for exporting our VM named Tiny11 from vSphere Datacenter called Primp-Datacenter and vSphere Cluster called Supermicro-Cluster. You will need to adjust the values based on how your environment is setup including the use of vSphere Resource Pools if they are applicable.
ovftool 'vi://administrator%*protected email*:443/Primp-Datacenter/host/Supermicro-Cluster/Resources/Tiny11' Tiny11.ovf
Step 7 - Based on our customization script which expects certain OVF keys to be defined, we will make a small update to the OVF configuration file. Add the following snippet immediately after </vmw:StorageSection> section towards the bottom of the file
<ProductSection> <Info>Information about the installed software</Info> <Product>Windows Desktop Tiny 11 Template</Product> <Vendor>WilliamLam</Vendor> <VendorUrl>https://www.williamlam.com</VendorUrl> <Category>Networking</Category> <Property ovf:userConfigurable="true" ovf:type="string" ovf:key="guestinfo.hostname"> <Label>Computer Name</Label> <Description>Name of Windows Computer</Description> </Property> <Property ovf:userConfigurable="true" ovf:type="string" ovf:key="guestinfo.ipaddress"> <Label>IP Address</Label> <Description>IP Address of eth0 (DHCP if left blank)</Description> </Property> <Property ovf:userConfigurable="true" ovf:type="string" ovf:key="guestinfo.netmask"> <Label>Prefix</Label> <Description>Prefix of eth0 (DHCP if left blank)</Description> </Property> <Property ovf:userConfigurable="true" ovf:type="string" ovf:key="guestinfo.gateway"> <Label>Gateway</Label> <Description>Gateway of eth0 (DHCP if left blank)</Description> </Property> <Property ovf:userConfigurable="true" ovf:type="string" ovf:key="guestinfo.dns"> <Label>DNS Server</Label> <Description>DNS Server (DHCP if left blank)</Description> </Property> </ProductSection>
Your final OVF should look like the WindowsTiny11-Template.ovf example.
Step 8 - To make it easy to distribute image for your own use, we will convert the OVF to an OVA. This step is completely optional but it is typically easier to manage a single file than a set of files. You will need to delete the manifest file as we have made changes to the original OVF and then run the following OVFTool command to produce the final OVA.
rm Tiny11.mf ovftool Tiny11.ovf Tiny11.ova
Finally, you are now ready to deploy your new Windows Tiny 11 OVA with custom OVF properties! If everything was configured correctly, you should see that the networking and hostname should reflect the input you provided during deployment. The log entry for the customization will be stored under C:\Windows\Temp and the script can certainly be optimized further by removing the scheduled task and the script since customization would no longer be required but I will leave that as an exercise for the reader 🙂
Thanks for the comment!