This repository contains an example of using Packer to create a Windows ISO image with slipstreamed updates.
The idea is to use Packer and Vagrant to slipstream updates into an existing ISO. This is done by creating a VirtualBox OS from the ISO, updating it, and then slipstreaming the updates into a new ISO.
Vagrant is used as it allows the loading and updating of the latest / previous ISO. This means that the updates can be applied from the updated Windows OS (i.e. C:\Windows\SoftwareDistribution\Download\
) and a new ISO created with them. Making the update process very meta.
Download the latest ISO (e.g. 'Windows 2016') into folder packer/templates/windows/packer_cache
.
e.g.
packer/templates/windows/packer_cache/en_windows_server_2016_vl_x64_dvd_11636701.iso
Download any extra MSU and CAB files to folder packer/templates/windows/packer_cache/Updates/Windows2016_64
.
Creating an offline installer for "Windows Assessment and Deployment Kit" (ADK) can speed-up build times.
cd packer/templates/windows/
e.g.
packer build --on-error=ask -var headless=false -var "iso_url=packer_cache/en_windows_server_2016_vl_x64_dvd_11636701.iso" -var "iso_checksum=e3779d4b1574bf711b063fe457b3ba63" -var "guest_os_type=Windows2016_64" -var "autounattend=../../files/answer_files/server_2016/without_updates/Autounattend.xml" create_adk_offline_installer.json
Change variables as needed!
e.g.
$env:INSTALLER_TYPE="EXE"
$env:INSTALL_FROM="URL"
$env:INSTALLER_DISPLAYNAME="Windows Assessment and Deployment Kit - Windows 10"
$env:INSTALL_EXE_ARGUMENTS="/quiet /layout C:\Windows\Temp\ADKoffline"
$env:INSTALLER_URI="https://go.microsoft.com/fwlink/?linkid=2026036"
$env:INSTALLER_NAME="adksetup.exe"
$env:FORCE_INSTALL="true"
$env:POST_INSTALL="compress"
$env:POST_INSTALL_COMPRESS_PATH="C:\Windows\Temp\ADKoffline"
$env:POST_INSTALL_COMPRESS_OUTPUT_PATH="C:\Windows\Temp\ADKoffline.zip"
packer/provisioners/powershell/install-from.ps1
This cert can be exported from a Windows machine where "Oracle VM VirtualBox Guest Additions" was previously installed and placed in folder packer/files/certs
.
md5 packer/templates/windows/packer_cache/en_windows_server_2016_vl_x64_dvd_11636701.iso
Get-FileHash .\packer\templates\windows\packer_cache\SW_DVD9_Win_Svr_STD_Core_and_DataCtr_Core_2016_64Bit_English_-3_MLF_X21-30350.ISO -Algorithm MD5
Ensure that packer
and VBoxManage
are in the environment variables $env:Path
.
e.g.
$env:Path = "C:\Program Files\Oracle\VirtualBox"
Environment Variables | Description | Default |
---|---|---|
IMAGE_NAME |
This is a regular expression that is used to select the images inside the WIM. | .* |
INSTALL_LIST_FILE |
Applies updates in the order they are listed within this file. | _Updates.txt |
APPLY_INSTALLED_UPDATES |
Apply MSU and CAB files that are found on the guest OS in path C:\Windows\SoftwareDistribution\Download\ |
|
UPDATES_FOLDER |
Path to installer folder. | |
ISO_OUTPUT_PATH |
ISO output filename. | \\\\VBOXSVR\\vagrant\\WindowsServer2016_Patched_{{isotime \"2006-01-02\"}}.iso |
Each uncommented line of the INSTALL_LIST_FILE
is used to search for file paths containing the line text. Only the first matching update is applied.
An example of this file is as follows: -
# Windows2016_64
# Updates will be applied in the following order.
KB4465659
KB4091664
KB4480977
If this file does not exist in the root of the
UPDATES_FOLDER
all MSU and CAB files in the folder tree will be applied.
Template Variables | Description | Default |
---|---|---|
iso_url |
Path to a Windows ISO | |
iso_checksum |
Windows ISO MD5 checksum | |
guest_os_type |
VirtualBox Guest OS Type | |
updates_folder |
Path to folder containing MSU and CAB installer files | \\\\VBOXSVR\\vagrant\\Updates\\Windows2016_64 |
autounattend |
Path to Autounattend XML file | {{template_dir}}/../../files/answer_files/server_2016/with_updates/Autounattend.xml |
adk_installer_uri |
URI to ADK installer | https://go.microsoft.com/fwlink/?linkid=2026036 |
VBoxManage list ostypes | grep -e '^ID' | sed -E -e "s/^ID:[[:blank:]]+//g" | grep -e 'Windows'
-
Create
packer_cache
offline folderpacker/templates/windows/packer_cache/Offline
. -
Create ADK offline installer, Create ADK offline installer, and move output file
ADKoffline.zip
topacker_cache
offline folder. -
Use offline installer: -
cd packer/templates/windows/
Validate
e.g.
packer validate -var headless=false -var 'iso_url=packer_cache/WindowsServer2016_Patched.iso' -var 'iso_checksum=932d3d7f14a3a938bb8ff73f486d64b9' -var 'guest_os_type=Windows2016_64' -var 'autounattend=../../files/answer_files/server_2016/without_updates/Autounattend.xml' -var "adk_installer_uri=file://\\\\VBOXSVR\\vagrant\\Offline\ADKoffline.zip" windows_slipstream.json
Build
e.g.
time PACKER_LOG=1 PACKER_LOG_PATH="windows_slipstream.log" packer build --on-error=ask -var headless=false -var 'iso_url=packer_cache/WindowsServer2016_Patched.iso' -var 'iso_checksum=932d3d7f14a3a938bb8ff73f486d64b9' -var 'guest_os_type=Windows2016_64' -var 'autounattend=../../files/answer_files/server_2016/without_updates/Autounattend.xml' windows_slipstream.json
- Change variables as needed!
- The above examples uses an
Autounattend.xml
file which doesn't install updates.
cd packer/templates/windows/
Validate
e.g.
packer validate -var headless=false -var 'iso_url=packer_cache/en_windows_server_2016_vl_x64_dvd_11636701.iso' -var 'iso_checksum=e3779d4b1574bf711b063fe457b3ba63' -var 'guest_os_type=Windows2016_64' windows_slipstream.json
Validate - Powershell
e.g.
packer validate -var headless=false -var "iso_url=packer_cache\SW_DVD9_Win_Svr_STD_Core_and_DataCtr_Core_2016_64Bit_English_-3_MLF_X21-30350.ISO" -var "iso_checksum=EEB465C08CF7243DBAAA3BE98F5F9E40" -var "guest_os_type=Windows2016_64" windows_slipstream.json
Build 'ask on error'
e.g.
packer build --on-error=ask -var headless=false -var 'iso_url=packer_cache/en_windows_server_2016_vl_x64_dvd_11636701.iso' -var 'iso_checksum=e3779d4b1574bf711b063fe457b3ba63' -var 'guest_os_type=Windows2016_64' windows_slipstream.json
Build 'ask on error' - Powershell
e.g.
packer build --on-error=ask -var headless=false -var "iso_url=packer_cache\SW_DVD9_Win_Svr_STD_Core_and_DataCtr_Core_2016_64Bit_English_-3_MLF_X21-30350.ISO" -var "iso_checksum=EEB465C08CF7243DBAAA3BE98F5F9E40" -var "guest_os_type=Windows2016_64" windows_slipstream.json
Build 'ask on error' (timed)
e.g.
time PACKER_LOG=1 PACKER_LOG_PATH="windows_slipstream.log" packer build --on-error=ask -var headless=false -var 'iso_url=packer_cache/en_windows_server_2016_vl_x64_dvd_11636701.iso' -var 'iso_checksum=e3779d4b1574bf711b063fe457b3ba63' -var 'guest_os_type=Windows2016_64' windows_slipstream.json
Build 'ask on error' (not timed) - Powershell
e.g.
$env:PACKER_LOG=1
$env:PACKER_LOG_PATH="windows_slipstream.log"
packer build -var headless=false -var "iso_url=packer_cache\SW_DVD9_Win_Svr_STD_Core_and_DataCtr_Core_2016_64Bit_English_-3_MLF_X21-30350.ISO" -var "iso_checksum=EEB465C08CF7243DBAAA3BE98F5F9E40" -var "guest_os_type=Windows2016_64" windows_slipstream.json
Build 'ask on error', logging and timed, without updates
By defining autounattend
as ./../files/answer_files/server_2016/without_updates/Autounattend.xml
.
e.g.
time PACKER_LOG=1 PACKER_LOG_PATH="windows_slipstream.log" packer build --on-error=ask -var headless=false -var 'iso_url=packer_cache/WindowsServer2016_Patched.iso' -var 'iso_checksum=932d3d7f14a3a938bb8ff73f486d64b9' -var 'guest_os_type=Windows2016_64' -var 'autounattend=../../files/answer_files/server_2016/without_updates/Autounattend.xml' --force windows_slipstream.json
Build 'ask on error' and logging, without updates - Powershell
e.g.
$env:PACKER_LOG=1
$env:PACKER_LOG_PATH="windows_slipstream.log"
packer build --on-error=ask -var headless=false -var "iso_url=packer_cache\SW_DVD9_Win_Svr_STD_Core_and_DataCtr_Core_2016_64Bit_English_-3_MLF_X21-30350.ISO" -var "iso_checksum=EEB465C08CF7243DBAAA3BE98F5F9E40" -var "guest_os_type=Windows2016_64" -var "autounattend=../../files/answer_files/server_2016/without_updates/Autounattend.xml" windows_slipstream.json