diff --git a/README.md b/README.md index 002adc31..b56ebab3 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,9 @@ Read more about how [custom images](https://maas.io/docs/how-to-customise-images | Windows 2016 | Beta | >= 3.3 | | Windows 2019 | Beta | >= 3.3 | | Windows 2022 | Beta | >= 3.3 | +| Windows 2025 | Beta | >= 3.3 | | Windows 10 | Beta | >= 3.3 | +| Windows 11 | Beta | >= 3.3 | ### Maturity level diff --git a/windows/Makefile b/windows/Makefile index 125583ff..57c7d0ac 100644 --- a/windows/Makefile +++ b/windows/Makefile @@ -6,10 +6,12 @@ PACKER ?= packer PACKER_LOG ?= 0 export PACKER_LOG -ISO ?= +ISO ?= +VHDX ?= VERSION ?= 2022 -BOOT ?= uefi HEADLESS ?= false +FORMAT ?= ISO +TIMEOUT ?= 1h ifeq ($(strip $(VERSION)),10) TYPE = Windows @@ -17,11 +19,19 @@ ifeq ($(strip $(VERSION)),10) else ifeq ($(strip $(VERSION)),11) TYPE = Windows EDIT ?= PRO + USE_TPM ?= yes +else ifeq ($(strip $(VERSION)),2025) + TYPE = Windows Server + EDIT ?= SERVERSTANDARD else TYPE = Windows Server EDIT ?= SERVERSTANDARD endif +ifneq ($(strip $(VHDX)),) + FORMAT ?= VHDX +endif + ifeq ($(wildcard /usr/share/OVMF/OVMF_CODE.fd),) OVMF_SFX ?= _4M else @@ -32,12 +42,12 @@ endif all: windows -$(eval $(call check_packages_deps,cloud-image-utils ovmf,cloud-image-utils ovmf)) +$(eval $(call check_packages_deps,cloud-image-utils,genisoimage,swtpm ovmf,cloud-image-utils ovmf,genisoimage,swtpm)) -OVMF_VARS.fd: /usr/share/OVMF/OVMF_VARS*.fd +OVMF_VARS.fd: /usr/share/OVMF/OVMF_VARS*.ms.fd cp -v $< $@ -http/Autounattend.xml: http/Autounattend.xml.${BOOT}.template +http/Autounattend.xml: http/Autounattend.xml.${FORMAT}.template sed s#@VERSION@#"${TYPE} ${VERSION} ${EDIT}"#g $< > $@ ifneq ($(strip $(PKEY)),) sed -i s#@PKEY@#${PKEY}#g $@ @@ -55,11 +65,41 @@ extra_drivers: .INTERMEDIATE: extra_drivers http/Autounattend.xml windows: check-deps clean extra_drivers http/Autounattend.xml OVMF_VARS.fd +ifeq ($(strip $(VERSION)),11) + mkdir -p /tmp/swtpm; \ + swtpm socket --daemon --tpm2 --tpmstate dir=/tmp/swtpm --ctrl type=unixio,path=/tmp/swtpm/swtpm-sock --log level=10 +endif +ifneq ($(strip $(VHDX)),) + qemu-img convert -p -O raw ${VHDX} windows_builder.img; \ + scripts/setup-nbd windows_builder.img; \ + TMP_DIR=$$(mktemp -d /tmp/packer-maas-XXXX); \ + nbd=$$(cat /tmp/nbd.lock); \ + pnum=$$(lsblk -l $${nbd} | grep -c part); \ + echo 'Adding unattend.xml to image...'; \ + mount -t ntfs $${nbd}p$${pnum} $${TMP_DIR}; \ + mkdir -pv $${TMP_DIR}/Windows/Panther/; \ + cp -v ./http/Autounattend.xml $${TMP_DIR}/Windows/Panther/unattend.xml; \ + sync -f $${TMP_DIR}/Windows/Panther/; \ + umount $${TMP_DIR}; \ + qemu-nbd -d $${nbd}; \ + rmdir $${TMP_DIR}; + ${PACKER} init . && ${PACKER} build -var iso_path=windows_builder.img \ + -var headless=${HEADLESS} \ + -var ovmf_suffix=${OVMF_SFX} \ + -var timeout=${TIMEOUT} \ + -var is_vhdx=true \ + -var disk_size=64G \ + windows.pkr.hcl +else ${PACKER} init . && ${PACKER} build -var iso_path=${ISO} \ -var headless=${HEADLESS} \ -var ovmf_suffix=${OVMF_SFX} \ + -var is_vhdx=false \ + -var timeout=${TIMEOUT} \ + -var use_tpm=${USE_TPM} \ windows.pkr.hcl +endif clean: - ${RM} -rf output-* windows.tar.gz http/Autounattend.xml \ - drivers.iso OVMF_VARS.fd + ${RM} -rf output-* windows.dd.gz http/Autounattend.xml \ + drivers.iso windows_builder.img OVMF_VARS.fd diff --git a/windows/README.md b/windows/README.md index 1db40bf3..3d40ac18 100644 --- a/windows/README.md +++ b/windows/README.md @@ -13,6 +13,7 @@ The Packer templates in this directory creates Windows Server images for use wit * ovmf * cloud-image-utils * [Packer](https://www.packer.io/intro/getting-started/install.html), v1.7.0 or newer +* Ubuntu 22.04+ is required to build Windows 11 images (swtpm package) ## Requirements (to deploy the image) @@ -26,30 +27,35 @@ The Packer templates in this directory creates Windows Server images for use wit This process has been build and deployment tested with the following versions of Microsoft Windows: +* Windows Server 2025 * Windows Server 2022 * Windows Server 2019 * Windows Server 2016 -* Windows 10 Pro 22H2 +* Windows 10 PRO+ +* Windows 11 PRO+ -## Known Issues +## Known Limitations * The current process builds UEFI compatible images only. ## windows.pkr.hcl Template -This template builds a dd.tgz MAAS image from an official Microsoft Windows ISO. +This template builds a dd.tgz MAAS image from an official Microsoft Windows ISO/VHDX. This process also installs the latest VirtIO drivers as well as Cloudbase-init. ## Obtaining Microsoft Windows ISO images -You can obtains Microsoft Windows Evaluation ISO images from the following links: +You can obtains Microsoft Windows Evaluation ISO/VHDX images from the following links: +* [Windows Server 2025](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2025) * [Windows Server 2022](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022) * [Windows Server 2019](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019) * [Windows Server 2016](https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016) +* [Windows 10 Enterprise](https://www.microsoft.com/en-us/evalcenter/download-windows-10-enterprise) +* [Windows 11 Enterprise](https://www.microsoft.com/en-us/evalcenter/download-windows-11-enterprise) ### Building the image @@ -61,6 +67,12 @@ customization: sudo make windows ISO= VERSION= ``` +Example: + +```shell +sudo make ISO=/mnt/iso/Windows_Server_2025_SERVER_EVAL_x64FRE_en-us.iso VERSION=2025 +``` + ### Makefile Parameters #### BOOT @@ -80,7 +92,7 @@ Whether VNC viewer should not be launched. Default is set to false. #### ISO -Path to Microsoft Windows ISO used to build the image. +Path to Microsoft Windows ISO image used to build the image. #### PACKER_LOG @@ -98,10 +110,18 @@ the build time depending on the type of ISO being used. Evaluation series ISO images usually do not require a product key to proceed, however this is not true with Enterprise and Retail ISO images. +#### TIMEOUT + +Defaults to 1h. Supports variables in h (hour) and m (Minutes). + +#### VHDX + +Path to Microsoft Windows VHDX image used to build the image. + #### VERSION -Specify the Microsoft Windows Version. Example inputs include: 2022, 2019, 2016 -and 10. +Specify the Microsoft Windows Version. Example inputs include: 2025, 2022, 2019, 2016, 10 +and 11. ## Uploading images to MAAS diff --git a/windows/TODO.md b/windows/TODO.md index 5965f5fd..d9c52d12 100644 --- a/windows/TODO.md +++ b/windows/TODO.md @@ -1,7 +1,4 @@ ## To be implemented (TODO List) -* Complete the support for build-time driver injection. -* Add support for BIOS based deployments. -* Add support for Windows 11 which requires TPM 2.0. * Migrate scripts/setup-nbd to use fuse. diff --git a/windows/http/Autounattend.xml.uefi.template b/windows/http/Autounattend.xml.ISO.template similarity index 95% rename from windows/http/Autounattend.xml.uefi.template rename to windows/http/Autounattend.xml.ISO.template index 60269cd9..769d395b 100644 --- a/windows/http/Autounattend.xml.uefi.template +++ b/windows/http/Autounattend.xml.ISO.template @@ -43,10 +43,8 @@ - 3 0 - false OnError @@ -66,7 +64,7 @@ true - + en-US @@ -92,11 +90,6 @@ 3 Work true - - diff --git a/windows/http/Autounattend.xml.VHDX.template b/windows/http/Autounattend.xml.VHDX.template new file mode 100644 index 00000000..f815c48d --- /dev/null +++ b/windows/http/Autounattend.xml.VHDX.template @@ -0,0 +1,109 @@ + + + + + + + true + + + + + en-US + + en-US + en-US + en-US + en-US + + + + + en-US + en-US + en-US + en-US + + + + ClearType + + + true + 3 + Work + true + + + + %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell -NoLogo -NonInteractive -ExecutionPolicy RemoteSigned -Command A:\logon.ps1 + 1 + + + + + Passw0rd + true</PlainText> + </AdministratorPassword> + <!--<LocalAccounts> + <LocalAccount wcm:action="add"> + <Password> + <Value>Passw0rd</Value> + <PlainText>true</PlainText> + </Password> + <Description>Packer Administrator</Description> + <DisplayName>defaultuser</DisplayName> + <Group>Administrators</Group> + <Name>defaultuser</Name> + </LocalAccount> + </LocalAccounts>--> + </UserAccounts> + <AutoLogon> + <Password> + <Value>Passw0rd</Value> + <PlainText>true</PlainText> + </Password> + <Enabled>true</Enabled> + <LogonCount>50</LogonCount> + <Username>Administrator</Username> + </AutoLogon> + <ComputerName>*</ComputerName> + </component> + </settings> + <settings pass="specialize"> + <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> + <RunAsynchronous> + <RunAsynchronousCommand> + <Order>1</Order> + <Path>reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OOBE" /v SetupDisplayedProductKey /t REG_DWORD /d 1 /f</Path> + <Description>Disable Product Key Pop-up During Setup</Description> + </RunAsynchronousCommand> + </RunAsynchronous> + </component> + <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <fDenyTSConnections>false</fDenyTSConnections> + </component> + <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <UserAuthentication>0</UserAuthentication> + </component> + <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <FirewallGroups> + <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop"> + <Active>true</Active> + <Profile>all</Profile> + <Group>@FirewallAPI.dll,-28752</Group> + </FirewallGroup> + </FirewallGroups> + </component> + <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <TimeZone>UTC</TimeZone> + <ComputerName>*</ComputerName> + </component> + <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="NonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <CEIPEnabled>0</CEIPEnabled> + </component> + </settings> +</unattend> diff --git a/windows/http/logon.ps1 b/windows/http/logon.ps1 index 02e5a7ec..cb506416 100644 --- a/windows/http/logon.ps1 +++ b/windows/http/logon.ps1 @@ -99,7 +99,7 @@ try # Install virtio drivers $Host.UI.RawUI.WindowTitle = "Installing Virtio Drivers..." - certutil -addstore "TrustedPublisher" A:/rh.cer + certutil -addstore "TrustedPublisher" A:\rh.cer [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Invoke-WebRequest "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win-gt-x64.msi" -Outfile "c:\virtio.msi" Invoke-WebRequest "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win-guest-tools.exe" -Outfile "c:\virtio.exe" diff --git a/windows/scripts/setup-nbd b/windows/scripts/setup-nbd index 5c653a49..96c9b4d5 100755 --- a/windows/scripts/setup-nbd +++ b/windows/scripts/setup-nbd @@ -4,7 +4,7 @@ # # Author: Lee Trager <lee.trager@canonical.com> # -# Copyright (C) 2020 Canonical +# Copyright (C) 2024 Canonical # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -24,7 +24,16 @@ if [ $UID -ne 0 ]; then exit 1 fi -if [ ! -f output-windows_builder/packer-windows_builder ]; then +if [ -z ${1} ]; then + disk_path="output-windows_builder/packer-windows_builder" +else + disk_path="${1}" + IMG_FMT="raw" +fi + +echo ${disk_path} + +if [ ! -f ${disk_path} ]; then echo "ERROR: Not in the same path as template!" >&2 exit fi @@ -34,23 +43,24 @@ shopt -s extglob modprobe nbd for nbd in /sys/class/block/nbd+([0-9]); do if [ "$(cat ${nbd}/size)" -eq 0 ]; then - nbd="/dev/$(basename $nbd)" - echo "Using $nbd" - break + nbd="/dev/$(basename ${nbd})" + echo ${nbd} > /tmp/nbd.lock + echo "Using ${nbd}" + break fi done -if [ -z "${nbd}" ] || ! echo $nbd | grep -q "/dev"; then +if [ -z "${nbd}" ] || ! echo ${nbd} | grep -q "/dev"; then echo "ERROR: Unable to find nbd device to mount image!" >&2 exit 1 fi -echo "Binding image to $nbd..." -qemu-nbd -d $nbd +echo "Binding image to ${nbd}..." +qemu-nbd -d ${nbd} if [ -n "$IMG_FMT" ]; then - qemu-nbd -c $nbd -f "$IMG_FMT" -n output-windows_builder/packer-windows_builder + qemu-nbd -c ${nbd} -f "$IMG_FMT" -n ${disk_path} else - qemu-nbd -c $nbd -n output-windows_builder/packer-windows_builder + qemu-nbd -c ${nbd} -n ${disk_path} fi echo 'Waiting for partitions to be created...' tries=0 diff --git a/windows/windows.pkr.hcl b/windows/windows.pkr.hcl index fa4ee003..ec8656f2 100644 --- a/windows/windows.pkr.hcl +++ b/windows/windows.pkr.hcl @@ -14,6 +14,11 @@ variable "headless" { description = "Whether VNC viewer should not be launched." } +variable "disk_size" { + type = string + default = "32G" +} + variable "iso_path" { type = string default = "" @@ -30,13 +35,49 @@ variable "filename" { description = "The filename of the tarball to produce" } +variable "is_vhdx" { + type = bool + default = false + description = "Whether we are building using a VHDX disk." +} + +variable "use_tpm" { + type = string + default = "" + description = "Whether we are building using a virtual tpm device." +} + +variable "timeout" { + type = string + default = "1h" +} + +locals { + baseargs = [ + ["-cpu", "host"], + ["-serial", "stdio"], + ["-drive", "if=pflash,format=raw,id=ovmf_code,readonly=on,file=/usr/share/OVMF/OVMF_CODE${var.ovmf_suffix}.ms.fd"], + ["-drive", "if=pflash,format=raw,id=ovmf_vars,file=OVMF_VARS.fd"], + ["-drive", "file=output-windows_builder/packer-windows_builder,format=raw"], + ["-cdrom", "${var.iso_path}"], + ["-drive", "file=drivers.iso,media=cdrom,index=3"], + ["-boot", "d"] + ] + tpmargs = [ + ["-chardev", "socket,id=chrtpm,path=/tmp/swtpm/swtpm-sock"], + ["-tpmdev", "emulator,id=tpm0,chardev=chrtpm"], + ["-device", "tpm-tis,tpmdev=tpm0"] + ] +} + source "qemu" "windows_builder" { accelerator = "kvm" - boot_command = ["<enter>"] - boot_wait = "1s" + boot_command = ["<return>"] + boot_wait = "2s" communicator = "none" - disk_interface = "ide" - disk_size = "20G" + disk_interface = "sata" + disk_image = "${var.is_vhdx}" + disk_size = "${var.disk_size}" floppy_files = ["./http/Autounattend.xml", "./http/logon.ps1", "./http/rh.cer"] floppy_label = "flop" format = "raw" @@ -48,17 +89,8 @@ source "qemu" "windows_builder" { memory = "4096" cpus = "2" net_device = "e1000" - qemuargs = [ - ["-cpu", "host"], - ["-serial", "stdio"], - ["-drive", "if=pflash,format=raw,id=ovmf_code,readonly=on,file=/usr/share/OVMF/OVMF_CODE${var.ovmf_suffix}.fd"], - ["-drive", "if=pflash,format=raw,id=ovmf_vars,file=OVMF_VARS.fd"], - ["-drive", "file=output-windows_builder/packer-windows_builder,format=raw"], - ["-cdrom", "${var.iso_path}"], - ["-drive", "file=drivers.iso,media=cdrom,index=3"], - ["-boot", "d"] - ] - shutdown_timeout = "45m" + qemuargs = concat(local.baseargs, (var.use_tpm == "yes" ? local.tpmargs : [])) + shutdown_timeout = "${var.timeout}" vnc_bind_address = "0.0.0.0" } @@ -83,6 +115,7 @@ build { ] inline_shebang = "/bin/bash -e" } + post-processor "compress" { output = "${var.filename}" }