Tag Archives: Windows PE

Unified Windows PE 4.0 builder for Windows ADK

This script will build Windows PE 4.0 (for x86, or AMD64 or both) including scripts and drivers of your choosing, it will create ISO images with both BIOS and UEFI support, and will also upload the resulting WIM boot images to your WDS server automatically (and freshen them if they have been re-created). This reduces the tiresome task of boot image maintenance to just a couple of clicks.

It uses only the standard Microsoft Windows ADK tools, which is the new name for WAIK. Just save the code below as Build_WinPE.cmd and right-click on it to Run as Administrator. Notice the defined variables at the start, particularly the %SOURCE% folder. It supports using either the 32bit or the 64bit ADK, and only the Windows PE and Deployment Tools ADK components are required. The script expects the following folders:

  • %SOURCE%\scripts\WinPE – any additional scripts (e.g. OS build scripts)
  • %SOURCE%\drivers\WinPE-x86\CURRENT – drivers
  • %SOURCE%\drivers\WinPE-AMD64\CURRENT
  • %SOURCE%\tools\WinPE-x86 – optional tools such as GImageX, or apps from portableapps.com
  • %SOURCE%\tools\WinPE-AMD64

Notice the optional components section at lines 90-95. Modify this if you need your image to contain additional items, for instance PowerShell or .NET Framework 4.

One further observation is that Macs don’t seem to be able to boot this version of Windows PE. I’m not sure whether this is a GOP display driver issue, or whether only true UEFI firmwares are required (Macs are EFI which is an earlier specification). To carry out an unattended Windows 8 install on a Mac via BootCamp you will need to build a Windows PE 3.0 ISO since Macs can’t PXE boot.

There’s some more info about UEFI booting on 32bit architectures here – apparently UEFI 2.3.1 compliance is a requirement. My VAIO’s Insyde H2O UEFI firmware certainly seems to ignore EFI loaders.

:: Build_WinPE.cmd
::
:: patters 2012
::
:: This script will build x86 and AMD64 Windows PE 4.0, automatically
:: collecting drivers from the relevant folders within the
:: unattended installation, building WIM and ISO images, and
:: will also upload the WIM images to the deployment server(s).
::
:: DO NOT cancel this script in progress as you can end up with
:: orphaned locks on files inside mounted WIM images which
:: usually require a reboot of the server to clear.
::

@echo off
setlocal ENABLEDELAYEDEXPANSION

::variables
     set SOURCE=\\WDSSERVER\unattended
     set PE_TEMP=C:\temp
     ::WinPE feature pack locale
     set PL=en-US
     ::commma separated list for WDS_SERVERS
     set WDS_SERVERS=WDSSERVER1,WDSSERVER2
::end variables

if "%PROCESSOR_ARCHITECTURE%"=="x86" set PRGFILES32=%PROGRAMFILES%
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" set PRGFILES32=%PROGRAMFILES(X86)%

if not exist "%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\*.*" (
     echo This script requires the Windows Assessment and Deployment Kit to be installed
     echo Download it from http://www.microsoft.com/en-us/download/details.aspx?id=30652
     echo.
     pause
     goto :eof
)
if "%1"=="relaunch" (
     call :BUILD_WINPE %2 %3 %4
     goto :eof
)
if "%1"=="unmount" (
     :: use this if you have a problem with the script and there are WIMs still mounted
     dism /Unmount-Wim /MountDir:"%PE_TEMP%\WinPE-x86\mount" /discard
     dism /Unmount-Wim /MountDir:"%PE_TEMP%\WinPE-AMD64\mount" /discard
     goto :eof
)
:prompt
cls
set /P SELECTION=Build WinPE for which CPU architecture (AMD64, x86, both)? [AMD64]: 
if "%SELECTION%"=="" set SELECTION=AMD64
if "%SELECTION%"=="amd64" set SELECTION=AMD64
if "%SELECTION%"=="X86" set SELECTION=x86
if "%SELECTION%"=="b" set SELECTION=both
if "%SELECTION%"=="BOTH" set SELECTION=both
if "%SELECTION%"=="AMD64" (
     start "Building Windows PE for AMD64 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch AMD64
     goto :eof
)
if "%SELECTION%"=="x86" (
     start "Building Windows PE for x86 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch x86
     goto :eof
)
if "%SELECTION%"=="both" (
     ::opening both instances of this script simultaneously seems to cause race conditions with dism.exe
     start /wait "Building Windows PE for x86 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch x86 nopause
     start "Building Windows PE for AMD64 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch AMD64
     goto :eof
)
goto :prompt

:BUILD_WINPE
set PE_ARCH=%1
set OSCDImgRoot=%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PROCESSOR_ARCHITECTURE%\Oscdimg
set WinPERoot=%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Windows Preinstallation Environment
set DandIRoot=%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools
set DISMRoot=%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PROCESSOR_ARCHITECTURE%\DISM
set PATH=%PATH%;%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PROCESSOR_ARCHITECTURE%\Oscdimg
set PATH=%PATH%;%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PROCESSOR_ARCHITECTURE%\BCDBoot
set PATH=%PATH%;%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PROCESSOR_ARCHITECTURE%\DISM
set PATH=%PATH%;%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Windows Preinstallation Environment
echo on
rd /s /q %PE_TEMP%\WinPE-%PE_ARCH%
call copype.cmd %PE_ARCH% %PE_TEMP%\WinPE-%PE_ARCH%
::package path
set PP=%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Windows Preinstallation Environment\%PE_ARCH%\WinPE_OCs
::image path
set IP=%PE_TEMP%\WinPE-%PE_ARCH%\mount
echo on
dism /Mount-Wim /WimFile:"%PE_TEMP%\WinPE-%PE_ARCH%\media\sources\boot.wim" /Index:1 /MountDir:"%IP%"
dism /image:"%IP%" /Add-Package /PackagePath:"%PP%\WinPE-Scripting.cab"^
 /PackagePath:"%PP%\%PL%\WinPE-Scripting_%PL%.cab" /PackagePath:"%PP%\WinPE-WMI.cab"^
 /PackagePath:"%PP%\%PL%\WinPE-WMI_%PL%.cab" /PackagePath:"%PP%\WinPE-MDAC.cab"^
 /PackagePath:"%PP%\%PL%\WinPE-MDAC_%PL%.cab" /PackagePath:"%PP%\WinPE-HTA.cab"^
 /PackagePath:"%PP%\%PL%\WinPE-HTA_%PL%.cab" /PackagePath:"%PP%\WinPE-Dot3Svc.cab"^
 /PackagePath:"%PP%\%PL%\WinPE-Dot3Svc_%PL%.cab"
dism /image:"%IP%" /Add-Driver /driver:"%SOURCE%\drivers\WinPE-%PE_ARCH%\CURRENT" /Recurse
copy "%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PE_ARCH%\BCDBoot\bootsect.exe" "%IP%\Windows"
copy /y "%SOURCE%\scripts\WinPE\*.*" "%IP%\Windows\System32"
copy "%SOURCE%\tools\WinPE-%PE_ARCH%\*.*" "%IP%\Windows\System32"
copy /y "%PRGFILES32%\Windows Kits\8.0\Assessment and Deployment Kit\Deployment Tools\%PE_ARCH%\DISM\imagex.exe" "%IP%\Windows\System32"
dism /Unmount-Wim /MountDir:"%IP%" /commit

::Mac OS BootCamp will look for autorun.inf in order to validate this disk as a Windows Installer CD
::adding this allows us to start unattended installs using WinPE
date /T > "%PE_TEMP%\WinPE-%PE_ARCH%\media\autorun.inf"

::bootable ISO includes both BIOS & EFI boot loaders
oscdimg -m -o -u2 -udfver102 -bootdata:2#p0,e,b"%PE_TEMP%\WinPE-%PE_ARCH%\fwfiles\etfsboot.com"#pEF,e,b"%PE_TEMP%\WinPE-%PE_ARCH%\fwfiles\efisys.bin" "%PE_TEMP%\WinPE-%PE_ARCH%\media" "%PE_TEMP%\WinPE-%PE_ARCH%\WinPE-40-%PE_ARCH%.iso"
@echo off

::rename the WIM file to avoid having multiple image files on the WDS server with the same filename
ren "%PE_TEMP%\WinPE-%PE_ARCH%\media\sources\boot.wim" boot_%PE_ARCH%.wim

if "%PE_ARCH%"=="x86" set WDS_ARCH=%PE_ARCH%
if "%PE_ARCH%"=="AMD64" set WDS_ARCH=X64
for %%i in (%WDS_SERVERS%) do (
     echo.
     echo Adding/updating boot image on WDS server: %%i
     :: try to add the image first, if that fails then replace existing
     wdsutil /Verbose /Progress /Add-Image /ImageFile:"%PE_TEMP%\WinPE-%PE_ARCH%\media\sources\boot-40-%PE_ARCH%.wim"^
      /Server:%%i /ImageType:Boot /Name:"Microsoft Windows PE 4.0 (%PE_ARCH%)" || wdsutil /Verbose /Progress /Replace-Image^
      /Image:"Microsoft Windows PE 4.0 (%PE_ARCH%)" /ImageType:Boot /Architecture:%WDS_ARCH% /ReplacementImage^
      /Name:"Microsoft Windows PE 4.0 (%PE_ARCH%)" /ImageFile:"%PE_TEMP%\WinPE-%PE_ARCH%\media\sources\boot-40-%PE_ARCH%.wim"^
      /Server:%%i
     echo.
)
::rename the WIM back again so bootable USB devices can be created
ren "%PE_TEMP%\WinPE-%PE_ARCH%\media\sources\boot-40-%PE_ARCH%.wim" boot.wim
echo *******************************************************************
echo WDS boot image(s) updated
echo.
echo A bootable ISO of this image has been created at:
echo   %PE_TEMP%\WinPE-%PE_ARCH%\WinPE-40-%PE_ARCH%.iso
echo.
echo To create a bootable USB key, use diskpart.exe to create a FAT32 partition
echo and mark it active, then copy the contents of this folder to its root:
echo   %PE_TEMP%\WinPE-%PE_ARCH%\media
echo.
echo FAT32 is required for EFI support.
echo.
if "%2"=="nopause" goto :eof
pause
goto :eof
Advertisements

Unified Windows PE 3.1 builder script for WAIK, with wifi and EFI support

WinPE-with-WLAN

It’s useful to have wifi support with WPA/WPA2 in Windows PE for occasions where you may want to perform an OS install or a salvage on a machine without ethernet built-in: for instance a MacBook Air running Windows or a Sony Vaio P when you don’t have the breakout dongle to hand.

When I started researching wifi support I found that the people who had clearly got it working seemed to be unwilling to share their work. One tool for the job, Holger’s PE Network Manager did not work for me at all, and judging from the dates of the files it had been created for older versions of Windows PE than the current one. There are many third party Windows PE ‘builder’ apps out there that completely replace the official Microsoft WAIK tools. However these seem more aimed at hobbyists than pros and tend to over complicate by adding features that a deployment pro does not really need. Since they’re not-so-trustworthy compiled binaries I don’t feel comfortable using them for work purposes. I decided to carefully work out for myself which registry entries and which additional files are required. There are surprisingly few.

The following unified script will use the standard WAIK tools to create both x86 and x64 builds, or either one individually, and is designed to be run by double-clicking it. Notice the defined variables at the start, particularly the %SOURCE% folder. The script expects the following folders:

  • %SOURCE%\scripts\WinPE – OS build scripts, wifi config XML, optional CA certificates
  • %SOURCE%\drivers\WinPE-x86\CURRENT – drivers
  • %SOURCE%\drivers\WinPE-x64\CURRENT
  • %SOURCE%\tools\WinPE-x86 – optional tools such as GImageX, or apps from portableapps.com
  • %SOURCE%\tools\WinPE-x64
  • %SOURCE%\tools\WinPE-x86\WLAN – the WLAN system files see highlighted section in main script below
  • %SOURCE%\tools\WinPE-x64\WLAN
 

It also requires your WAIK installation to be updated to Windows PE 3.1 using the WAIK supplement for Windows 7 SP1.

I had originally wanted to connect WinPE clients to a WPA-Enterprise network but although Windows PE now supports 802.1x, it appears to work only for wired connections (see my comment on that post).

There is no GUI to manage the WLAN service, but I did find the necessary registry mod to enable the hooks already present in netsh.exe. You will need to use a working Windows 7 PC which is configured for the WLAN of your choice, then export its profile. You cannot connect to a wifi network in Windows PE without having done this. The PSK must not be encrypted since it will be imported onto a different machine (you need to run this with Administrator privileges or else the key parameter will be ignored):

netsh wlan show profiles
netsh wlan export profile name="YOURWLANSSIDHERE" folder="C:\temp" key=clear

Copy this XML file into the folder %SOURCE%\scripts\WinPE in the build tree. When Windows PE boots, you simply type wifi. This wifi.cmd script which is built by the main script will automatically import any XML profiles, list the available wifi networks, and display some example netsh syntax:

netsh wlan connect name=YOURWLANSSIDHERE ssid=YOURWLANSSIDHERE
 

The script

Save as %SOURCE%\scripts\Build_WinPE.cmd

:: Build_WinPE.cmd
::
:: This script will build x86 and x64 Windows PE, automatically
:: collecting drivers from the relevant folders within the
:: unattended installation, building WIM and ISO images, and
:: will also upload the WIM images to the deployment server(s).
::
:: DO NOT cancel this script in progress as you can end up with
:: orphaned locks on files inside mounted WIM images which
:: usually require a reboot of the server to clear.
::

@echo off
setlocal ENABLEDELAYEDEXPANSION

::variables
     set SOURCE=G:\unattended
     set PE_TEMP=C:\temp
     ::WinPE feature pack locale
     set PL=en-US
     ::commma separated list for WDS_SERVERS
     set WDS_SERVERS=FILESERVER1,FILESERVER2
::end variables

if not exist "C:\Program Files\Windows AIK\Tools\PETools\*.*" (
     echo This script requires the Windows Automatic Install Kit to be installed
     echo Download it from http://www.microsoft.com/download/en/details.aspx?id=5753
     echo.
     pause
     goto :eof
)
if "%1"=="relaunch" (
     call :BUILD_WINPE %2 %3 %4
     goto :eof
)
if "%1"=="unmount" (
     :: use this if you have a problem with the script and there are WIMs still mounted
     dism /Unmount-Wim /MountDir:"%PE_TEMP%\WinPE-x86\mount" /discard
     dism /Unmount-Wim /MountDir:"%PE_TEMP%\WinPE-x64\mount" /discard
     goto :eof
)
:prompt
cls
set /P SELECTION=Build WinPE for which CPU architecture (x64, x86, both)? [x64]: 
if "%SELECTION%"=="" set SELECTION=x64
if "%SELECTION%"=="x64" (
     start "Building Windows PE for x64 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch x64 amd64
     goto :eof
)
if "%SELECTION%"=="x86" (
     start "Building Windows PE for x86 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch x86 i386
     goto :eof
)
if "%SELECTION%"=="b" set SELECTION=both
if "%SELECTION%"=="both" (
     ::opening both instances of this script simultaneously seems to cause race conditions with dism.exe
     start /wait "Building Windows PE for x86 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch x86 i386 nopause
     start "Building Windows PE for x64 - NEVER CANCEL THIS SCRIPT IN PROGRESS" cmd /c "%0" relaunch x64 amd64
     goto :eof
)
goto :prompt

:BUILD_WINPE
set PE_ARCH=%1
set PE_ARCH_LONG=%2
echo on
set PATH=%PATH%;C:\Program Files\Windows AIK\Tools\PETools\;C:\Program Files\Windows AIK\Tools\%PROCESSOR_ARCHITECTURE%
set PATH=%PATH%;C:\Program Files\Windows AIK\Tools\Servicing
rd /s /q %PE_TEMP%\WinPE-%PE_ARCH%

::Microsoft haven't used consistent naming in WAIK
if "%PE_ARCH%"=="x86" set WAIK_ARCH=%PE_ARCH%
if "%PE_ARCH%"=="x64" set WAIK_ARCH=%PE_ARCH_LONG%

call copype.cmd %WAIK_ARCH% %PE_TEMP%\WinPE-%PE_ARCH%
::package path
set PP=%ProgramFiles%\Windows AIK\Tools\PETools\%WAIK_ARCH%\WinPE_FPs
::image path
set IP=%PE_TEMP%\WinPE-%PE_ARCH%\mount
echo on
dism /Mount-Wim /WimFile:"%PE_TEMP%\WinPE-%PE_ARCH%\winpe.wim" /Index:1 /MountDir:"%IP%"
dism /image:"%IP%" /Add-Package /PackagePath:"%PP%\winpe-scripting.cab" /PackagePath:"%PP%\%PL%\winpe-scripting_%PL%.cab" /PackagePath:"%PP%\winpe-wmi.cab" /PackagePath:"%PP%\%PL%\winpe-wmi_%PL%.cab" /PackagePath:"%PP%\winpe-mdac.cab" /PackagePath:"%PP%\%PL%\winpe-mdac_%PL%.cab" /PackagePath:"%PP%\WinPE-HTA.cab" /PackagePath:"%PP%\%PL%\WinPE-HTA_%PL%.cab" /PackagePath:"%PP%\WINPE-DOT3SVC.CAB" /PackagePath:"%PP%\%PL%\WINPE-DOT3SVC_%PL%.CAB"
dism /image:"%IP%" /Add-Driver /driver:"%SOURCE%\drivers\WinPE-%PE_ARCH%\CURRENT" /Recurse
copy "%ProgramFiles%\Windows AIK\Tools\PETools\%WAIK_ARCH%\bootsect.exe" "%IP%\Windows"
copy /y "%SOURCE%\scripts\WinPE\*.*" "%IP%\Windows\System32"
copy "%SOURCE%\tools\WinPE-%PE_ARCH%\*.*" "%IP%\Windows\System32"
copy "%ProgramFiles%\Windows AIK\Tools\%WAIK_ARCH%\*.*" "%IP%\Windows\System32"


::add WLAN components
reg load HKLM\PE-BUILD-SYSTEM "%IP%\Windows\System32\config\SYSTEM"
reg load HKLM\PE-BUILD-SOFTWARE "%IP%\Windows\System32\config\SOFTWARE"
set KEY=HKLM\PE-BUILD-SYSTEM\ControlSet001\Enum\Root\LEGACY_WLANSVC
reg add %KEY% /v NextInstance /t REG_DWORD /d 1 /f
reg add %KEY%\0000 /v Service /t REG_SZ /d Wlansvc /f
reg add %KEY%\0000 /v Legacy /t REG_DWORD /d 1 /f
reg add %KEY%\0000 /v ConfigFlags /t REG_DWORD /d 0 /f
reg add %KEY%\0000 /v Class /t REG_SZ /d LegacyDriver /f
reg add %KEY%\0000 /v ClassGUID /t REG_SZ /d {8ECC055D-047F-11D1-A537-0000F8753ED1} /f
reg add %KEY%\0000 /v DeviceDesc /t REG_SZ /d "@%%SystemRoot%%\System32\wlansvc.dll,-257" /f
set KEY=HKLM\PE-BUILD-SYSTEM\ControlSet001\services\wlansvc
reg add %KEY% /v DisplayName /t REG_SZ /d "@%%SystemRoot%%\System32\wlansvc.dll,-257" /f
reg add %KEY% /v ErrorControl /t REG_DWORD /d 1 /f
reg add %KEY% /v Group /t REG_SZ /d TDI /f
reg add %KEY% /v ImagePath /t REG_EXPAND_SZ /d "%%SystemRoot%%\system32\svchost.exe -k LocalSystemNetworkRestricted" /f
reg add %KEY% /v Start /t REG_DWORD /d 2 /f
reg add %KEY% /v Type /t REG_DWORD /d 32 /f
reg add %KEY% /v Description /t REG_SZ /d "@%%SystemRoot%%\System32\wlansvc.dll,-258" /f
reg add %KEY% /v DependOnService /t REG_MULTI_SZ /d "nativewifip\0RpcSs\0Ndisuio\0Eaphost" /f
reg add %KEY% /v ObjectName /t REG_SZ /d LocalSystem /f
reg add %KEY% /v ServiceSidType /t REG_DWORD /d 1 /f
reg add %KEY% /v RequiredPrivileges /t REG_MULTI_SZ /d "SeChangeNotifyPrivilege\0SeImpersonatePrivilege\0SeAuditPrivilege\0SeTcbPrivilege\0SeDebugPrivilege" /f
reg add %KEY% /v FailureActions /t REG_BINARY /d 2c0100000000000000000000030000001400000001000000c0d4010001000000e09304000000000000000000 /f
reg add %KEY%\Enum /v 0 /t REG_SZ /d "Root\LEGACY_WLANSVC\0000" /f
reg add %KEY%\Enum /v Count /t REG_DWORD /d 1 /f
reg add %KEY%\Enum /v NextInstance /t REG_DWORD /d 1 /f
reg add %KEY%\Parameters /v ServiceDll /t REG_EXPAND_SZ /d "%%SystemRoot%%\System32\wlansvc.dll" /f
reg add %KEY%\Parameters /v ServiceDllUnloadOnStop /t REG_DWORD /d 1 /f
reg add %KEY%\Parameters /v ServiceMain /t REG_SZ /d WlanSvcMain /f
reg add HKLM\PE-BUILD-SOFTWARE\Microsoft\NetSh /v wlancfg /t REG_SZ /d wlancfg.dll /f
call :REG_MULTI_SZ-add "HKLM\PE-BUILD-SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost" LocalSystemNetworkRestricted wlansvc
xcopy /s "%SOURCE%\tools\WinPE-%PE_ARCH%\WLAN\*.*" "%IP%\Windows"
@echo off
:: These are the files that are required for WLAN (take them from Windows 7 machines of both CPU archs)
:: Put them in %SOURCE%\tools\WinPE-%PE_ARCH%\WLAN and create the same relative folder structure from C:\Windows onwards
:: so the file C:\Windows\inf\netnwifi.inf is copied to %SOURCE%\tools\WinPE-%PE_ARCH%\WLAN\inf\netnwifi.inf
::     C:\Windows\inf\netnwifi.inf
::     C:\Windows\inf\netvwififlt.inf
::     C:\Windows\inf\netvwifimp.inf
::     C:\Windows\l2schemas\wlan_policy_v1.xsd
::     C:\Windows\l2schemas\wlan_profile_v1.xsd
::     C:\Windows\l2schemas\wlanap_profile_v1.xsd
::     C:\Windows\schemas\availablenetwork\availablenetworkinfo.xsd
::     C:\Windows\system32\certutil.exe
::     C:\Windows\system32\wlanapi.dll
::     C:\Windows\system32\wlancfg.dll
::     C:\Windows\system32\wlanhlp.dll
::     C:\Windows\system32\wlanmsm.dll
::     C:\Windows\system32\wlansec.dll
::     C:\Windows\system32\wlansvc.dll
::     C:\Windows\system32\wlanui.dll
::     C:\Windows\system32\wlgpclnt.dll
::     C:\Windows\system32\drivers\nwifi.sys
::     C:\Windows\system32\drivers\vwififlt.sys
::     C:\Windows\system32\drivers\vwifimp.sys
::     C:\Windows\system32\en-US\certutil.exe.mui (or your locale's equivalent)
::     C:\Windows\system32\en-us\wlanapi.dll.mui
::     C:\Windows\system32\en-us\wlancfg.dll.mui
::     C:\Windows\system32\en-us\wlansvc.dll.mui
::     C:\Windows\system32\en-us\wlanui.dll.mui
::     C:\Windows\system32\en-us\wlgpclnt.dll.mui
echo on
reg unload HKLM\PE-BUILD-SYSTEM
reg unload HKLM\PE-BUILD-SOFTWARE
:: build wifi.cmd
(
     echo @echo off
     echo drvload X:\WINDOWS\Inf\netvwifimp.inf
     echo drvload X:\WINDOWS\Inf\netvwififlt.inf
     echo drvload X:\WINDOWS\Inf\netnwifi.inf
     echo netcfg -c s -i ms_nativewifip
     echo echo Importing detected CA certificate^(s^).
     echo for %%%%i in ^(*.cer^) do certutil -addstore root %%%%i
     echo echo.
     echo net start dot3svc
     echo net start wlansvc
     echo for %%%%i in ^("Wireless Network Connection*.xml"^) do netsh wlan add profile filename="%%%%i"
     echo netsh wlan show networks
     echo echo.
     echo echo use "netsh wlan" to manage the wifi connection like so:
     echo echo   netsh wlan connect name=YOURWLANHERE ssid=YOURWLANHERE
     echo echo   ipconfig /renew
     echo echo.
) > "%IP%\Windows\System32\wifi.cmd"

dism /Unmount-Wim /MountDir:"%IP%" /commit
imagex /export /boot /compress fast "%PE_TEMP%\WinPE-%PE_ARCH%\winpe.wim" 1 "%PE_TEMP%\WinPE-%PE_ARCH%\ISO\sources\boot.wim"
@echo off

::Mac OS BootCamp will look for autorun.inf in order to validate this disk as a Windows Installer CD
::adding this allows us to start unattended installs using WinPE
date /T > "%PE_TEMP%\WinPE-%PE_ARCH%\ISO\autorun.inf"

::x64 bootable ISO includes both BIOS & EFI boot loaders
if "%PE_ARCH%" == "x64" (
     set CD_CMD=oscdimg -m -o -u2 -udfver102 -bootdata:2#p0,e,b"%PE_TEMP%\WinPE-%PE_ARCH%\etfsboot.com"#pEF,e,b"%PE_TEMP%\WinPE-%PE_ARCH%\efisys.bin" "%PE_TEMP%\WinPE-%PE_ARCH%\ISO" "%PE_TEMP%\WinPE-%PE_ARCH%\winpe_%PE_ARCH_LONG%.iso"
) else ( 
     set CD_CMD=oscdimg -n -b"%PE_TEMP%\WinPE-%PE_ARCH%\etfsboot.com" "%PE_TEMP%\WinPE-%PE_ARCH%\ISO" "%PE_TEMP%\WinPE-%PE_ARCH%\winpe_%PE_ARCH_LONG%.iso"
)
echo on
%CD_CMD%
@echo off

::rename the WIM file to avoid having multiple image files on the WDS server with the same filename
ren "%PE_TEMP%\WinPE-%PE_ARCH%\ISO\sources\boot.wim" boot_%PE_ARCH_LONG%.wim
del "%PE_TEMP%\WinPE-%PE_ARCH%\winpe.wim"

for %%i in (%WDS_SERVERS%) do (
     echo.
     echo Adding/updating boot image on WDS server: %%i
     :: try to add the image first, if that fails then replace existing
     wdsutil /Verbose /Progress /Add-Image /ImageFile:"%PE_TEMP%\WinPE-%PE_ARCH%\ISO\sources\boot_%PE_ARCH_LONG%.wim" /Server:%%i /ImageType:Boot /Name:"Microsoft Windows PE (%PE_ARCH%)" || wdsutil /Verbose /Progress /Replace-Image /Image:"Microsoft Windows PE (%PE_ARCH%)" /ImageType:Boot /Architecture:%PE_ARCH% /ReplacementImage /Name:"Microsoft Windows PE (%PE_ARCH%)" /ImageFile:"%PE_TEMP%\WinPE-%PE_ARCH%\ISO\sources\boot_%PE_ARCH_LONG%.wim" /Server:%%i
     echo.
)
::rename the WIM back again so bootable USB devices can be created
ren "%PE_TEMP%\WinPE-%PE_ARCH%\ISO\sources\boot_%PE_ARCH_LONG%.wim" boot.wim
echo *******************************************************************
echo WDS boot image(s) updated
echo.
echo A bootable ISO of this image has been created at:
echo   %PE_TEMP%\WinPE-%PE_ARCH%\winpe_%PE_ARCH_LONG%.iso
echo.
echo To create a bootable USB key, use diskpart.exe to create a FAT32 partition
echo and mark it active, then copy the contents of this folder to its root:
echo   %PE_TEMP%\WinPE-%PE_ARCH%\ISO
echo.
echo FAT32 is required for EFI support.
echo.
if "%3"=="nopause" goto :eof
pause
goto :eof

:REG_MULTI_SZ-add
::subroutine to append a value to a multiple string value Registry entry
setlocal ENABLEEXTENSIONS
set KEY=%1
set VALUE=%2
for /f "tokens=2*" %%a in ('reg query %KEY% /v %VALUE% /t REG_MULTI_SZ ^| FIND "REG_MULTI_SZ"') do set DATA=%%b
set DATA=%DATA%\0%3
reg add %KEY% /v %VALUE% /t REG_MULTI_SZ /d %DATA% /f
 
 

Customizing Windows 7 unattend.xml

Windows 7, like Vista, uses an XML answer file to configure the OS install. What’s neat about this is that even though you use the WAIK‘s WSIM tool to edit and validate it, you can customize it and add your own sections for software packages etc. as you can see from the example below, though these custom sections will need to be inserted after the sections that WSIM validates. This answer file can easily be parsed with VBScript using MSXML DOM, allowing for variables like passwords, driver sets, product keys and so on to be inserted at build time.

Why use an unattended install?

If you’ve always used an unattended install to build your workstations, you’ll know that they can be extremely versatile. If you already have a scripted build for XP with applications then chances are you’ll want to tweak those scripts to work with Windows 7. Sure, Sysprep images are handy too, but unless all your machines are the same, or all your packaged app requirements are identical, then you’ll need to add a load of scripted customization to them anyway. Which begs the question: why not just use an unattended install? That way you eliminate application problems that sometimes surface after image-based deployment. In a previous job I remember Roxio deployment in particular was a nightmare for this reason.

Microsoft have certainly made things considerably easier with the release of the Microsoft Deployment Toolkit 2010 but, though it offers a great introduction into the process of automated system building, it lacks the flexibility of rolling your own build process – in particular if you already have a host database. When I decide to rebuild a machine, it boots Windows PE from WDS, looks up its MAC address in the host database and reads the model type from WMI and will offer default choices in my build script menus based on that. It also works out which is the nearest site file server to use for the install.

The problem with Sysprep deployment if you have a very mixed hardware environment is that you either have to:

  • Create a WIM image for each different hardware type (lots of boring maintenance when changes are required)
  • Add a huge bulk of drivers to a single WIM image with DISM
  • Use the AuditSystem phase to connect to a driver share and re-detect all the hardware

Since Windows Vista and later versions effectively just install a WIM and run a hardware detection phase during their normal install process, Sysprep no longer offers much of a speed improvement over an unattended build.

Device drivers

Additional mass storage and networking drivers that will be essential during setup are detected in the Windows PE instance (which we booted from WDS) Driver Store and ‘reflected’ into the installed OS. As you build your custom Windows PE boot image, add these using DSIM as described in my post on the subject.

Since we can read the PC model name from the BIOS via WMI, we can add a tailored device path to the offlineServicing phase of setup. This allows for easy maintenance of driver bundles, since we can arrange them by model type (which certainly beats having dozens of SoundMAX audio drivers lumped together), and we can limit which drivers are offered to each model – particularly useful when a driver causes problems for some models, as with the GMA950 driver and Adobe Flash on Intel 945G motherboards.

I noticed that this is in fact considerably neater that the method used in MDT2010’s ZTIDrivers.wsf script, which copies drivers to the local system then invokes the AuditSystem phase, running the PnP detection routines a second time, slowing down the install. Useful for an OEM like Dell I suppose, whose PCs are often started from their factory image with no connectivity, but not ideal for corporate LANs.

Sample autounattend.xml with custom sections at the end

Note 1 – I couldn’t find any examples of this online, but I discovered that the values for pre-populating the Internet Explorer 8 Search Providers can be obtained by configuring a workstation, then harvesting the registry settings from HKCU\Software\Microsoft\Internet Explorer\SearchScopes. I have highlighted the relevant lines in the XML below.

Note 2 – Somewhat confusingly, in the offlineServicing phase Microsoft-Windows-PnpCustomizationsNonWinPE will fail to connect to your file server for drivers unless you connect to its FQDN (assuming the unattended launched the OS build from a share referencing just the NetBIOS name). Fail to do this and %systemroot%\panther\setupact.log will reveal that it fails to connect with error 0x4C3 (multiple credentials on connection to the same server). What’s bizarre is that there certainly aren’t multiple credentials in use – I use the same ones throughout. I wrote up this problem and solution in this thread on the MSFN Forums. I suspect this might be caused because I launch setup.exe from a network drive rather than mounting the OS WIM image from a WDS server (I wanted to maintain consistency with my other legacy OS builds). I have highlighted this on line 57.

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="windowsPE">
        <component name="Microsoft-Windows-International-Core-WinPE" 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">
            <SetupUILanguage>
                <UILanguage>en-US</UILanguage>
            </SetupUILanguage>
            <InputLocale>0809:00000809</InputLocale>
            <SystemLocale>en-GB</SystemLocale>
            <UILanguage>en-US</UILanguage>
            <UserLocale>en-GB</UserLocale>
        </component>
        <component name="Microsoft-Windows-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">
            <ComplianceCheck>
                <DisplayReport>Never</DisplayReport>
            </ComplianceCheck>
            <Diagnostics>
                <OptIn>false</OptIn>
            </Diagnostics>
            <DiskConfiguration>
                <WillShowUI>Always</WillShowUI>
            </DiskConfiguration>
            <DynamicUpdate>
                <Enable>true</Enable>
                <WillShowUI>OnError</WillShowUI>
            </DynamicUpdate>
            <ImageInstall>
                <OSImage>
                    <InstallFrom>
                        <MetaData wcm:action="add">
                            <Key>/IMAGE/NAME</Key>
                            <Value>Windows 7 PROFESSIONAL</Value>
                        </MetaData>
                    </InstallFrom>
                </OSImage>
            </ImageInstall>
            <UserData>
                <AcceptEula>true</AcceptEula>
                <FullName>IT</FullName>
                <Organization>My company</Organization>
                <ProductKey>
                    <WillShowUI>OnError</WillShowUI>
                </ProductKey>
            </UserData>
            <EnableNetwork>true</EnableNetwork>
        </component>
    </settings>
    <settings pass="offlineServicing">
        <component name="Microsoft-Windows-PnpCustomizationsNonWinPE" 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">
            <DriverPaths>
                <PathAndCredentials wcm:action="add" wcm:keyValue="common">
                    <Credentials>
                        <Domain>domain.com</Domain>
                        <Password>Password</Password>
                        <Username>unattended</Username>
                    </Credentials>
                    <Path>\\myserver.domain.com\UNATTENDED\drivers\7-x64\common</Path>
                </PathAndCredentials>
                <PathAndCredentials wcm:action="add" wcm:keyValue="build">
                    <Credentials>
                        <Domain>*value to be set by install.vbs*</Domain>
                        <Password>*value to be set by install.vbs*</Password>
                        <Username>*value to be set by install.vbs*</Username>
                    </Credentials>
                    <Path>*value to be set by install.vbs*</Path>
                </PathAndCredentials>
            </DriverPaths>
        </component>
    </settings>
    <settings pass="generalize">
        <component name="Microsoft-Windows-PnpSysprep" 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">
            <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
        </component>
    </settings>
    <settings pass="specialize">
        <component name="Microsoft-Windows-IE-InternetExplorer" 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">
            <Home_Page>http://mycompany.com</Home_Page>
            <CompanyName>My Company</CompanyName>
            <FavoritesOnTop>true</FavoritesOnTop>
            <FilterLevel>High</FilterLevel>
            <Help_Page></Help_Page>
            <DisableFirstRunWizard>true</DisableFirstRunWizard>
            <DisableWelcomePage>true</DisableWelcomePage>
            <PlaySound>true</PlaySound>
            <ShowInformationBar>true</ShowInformationBar>
            <UserAgent></UserAgent>
            <Window_Title_CN></Window_Title_CN>
            <SearchScopes>
                <Scope wcm:action="add">
                    <ScopeDefault>true</ScopeDefault>
                    <ScopeKey>Search1</ScopeKey>
                    <ScopeDisplayName>Google</ScopeDisplayName>
                    <ScopeUrl>http://www.google.com/search?q={searchTerms}&amp;sourceid=ie7&amp;rls=com.microsoft:{language}:{referrer:source}&amp;ie={inputEncoding?}&amp;oe={outputEncoding?}</ScopeUrl>
                    <FaviconURL>http://www.google.com/favicon.ico</FaviconURL>
                    <SuggestionsURL>http://clients5.google.com/complete/search?q={searchTerms}&amp;client=ie8&amp;mw={ie:maxWidth}&amp;sh={ie:sectionHeight}&amp;rh={ie:rowHeight}&amp;inputencoding={inputEncoding}&amp;outputencoding={outputEncoding}</SuggestionsURL>
                </Scope>
                <Scope wcm:action="add">
                    <ScopeKey>Search2</ScopeKey>
                    <ScopeDisplayName>Bing</ScopeDisplayName>
                    <FaviconURL>http://www.bing.com/favicon.ico</FaviconURL>
                    <ScopeUrl>http://www.bing.com/search?q={searchTerms}&amp;form=IE8SRC&amp;src=IE-SearchBox</ScopeUrl>
                    <SuggestionsURL>http://api.bing.com/qsml.aspx?query={searchTerms}&amp;market={Language}&amp;form=IE8SSC&amp;maxwidth={ie:maxWidth}&amp;rowheight={ie:rowHeight}&amp;sectionHeight={ie:sectionHeight}</SuggestionsURL>
                </Scope>
            </SearchScopes>
            <EnableLinksBar>false</EnableLinksBar>
            <PrintBackground>true</PrintBackground>
        </component>
        <component name="Microsoft-Windows-RemoteAssistance-Exe" 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">
            <fAllowFullControl>true</fAllowFullControl>
            <fAllowToGetHelp>true</fAllowToGetHelp>
        </component>
        <component name="Microsoft-Windows-Security-SPP-UX" 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">
            <SkipAutoActivation>true</SkipAutoActivation>
        </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">
            <ComputerName>*</ComputerName>
            <ProductKey>XXXXX-YYYYY-ZZZZZ-YYYYY-XXXXX</ProductKey>
            <RegisteredOrganization>My Company</RegisteredOrganization>
            <RegisteredOwner>IT</RegisteredOwner>
        </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-UnattendedJoin" 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">
            <Identification>
                <Credentials>
                    <Domain>*value to be set by install.vbs*</Domain>
                    <Password>*value to be set by install.vbs*</Password>
                    <Username>*value to be set by install.vbs*</Username>
                </Credentials>
                <JoinDomain>domain.com</JoinDomain>
                <MachineObjectOU>OU=Windows 7,OU=Workstations,DC=domain,DC=com</MachineObjectOU>
            </Identification>
        </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">
            <DomainProfile_EnableFirewall>false</DomainProfile_EnableFirewall>
        </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>1</UserAuthentication>
        </component>
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-International-Core" 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">
            <SystemLocale>en-GB</SystemLocale>
            <UILanguage>en-GB</UILanguage>
            <UserLocale>0809:00000809</UserLocale>
        </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">
            <RegisteredOrganization>My Company</RegisteredOrganization>
            <RegisteredOwner>IT</RegisteredOwner>
            <TimeZone>GMT Standard Time</TimeZone>
            <OEMInformation>
                <Manufacturer>*</Manufacturer>
                <Model>*</Model>
            </OEMInformation>
            <OOBE>
                <HideEULAPage>true</HideEULAPage>
                <NetworkLocation>Work</NetworkLocation>
                <ProtectYourPC>1</ProtectYourPC>
            </OOBE>
            <UserAccounts>
                <AdministratorPassword>
                    <Value>MwBifhgftytredjghAcwB0AHIAYQ584jkhkgtEAcwBzAHcAbwByAGQA</Value>
                    <PlainText>false</PlainText>
                </AdministratorPassword>
                <LocalAccounts>
                    <LocalAccount wcm:action="add">
                        <Password>
                            <Value>QQBTAEUANABoAGchgfhgfd357wrysAHMAcwB3AG8AcgBkAA==</Value>
                            <PlainText>false</PlainText>
                        </Password>
                        <Name>cust_localuser</Name>
                        <DisplayName>cust_localuser</DisplayName>
                        <Description>Dummy user required for unattended - delete later</Description>
                        <Group>users</Group>
                    </LocalAccount>
                </LocalAccounts>
            </UserAccounts>
            <VisualEffects>
                <FontSmoothing>ClearType</FontSmoothing>
            </VisualEffects>
            <FirstLogonCommands>
                <SynchronousCommand wcm:action="add">
                    <Order>1</Order>
                    <Description>Connect to unattended share</Description>
                    <CommandLine>net use *value to be set by install.vbs*</CommandLine>
                </SynchronousCommand>
                <SynchronousCommand wcm:action="add">
                    <Order>2</Order>
                    <Description>Launch package installer</Description>
                    <CommandLine>cscript *value to be set by install.vbs*</CommandLine>
                    <RequiresUserInput>true</RequiresUserInput>
                </SynchronousCommand>
            </FirstLogonCommands>
            <AutoLogon>
                <Password>
                    <Value>MwBifhgftytredjghAcwB0AHIAYQ584jkhkgtEAcwBzAHcAbwByAGQA</Value>
                    <PlainText>false</PlainText>
                </Password>
                <Username>Administrator</Username>
                <LogonCount>1</LogonCount>
                <Enabled>true</Enabled>
            </AutoLogon>
        </component>
    </settings>
    <cpi:offlineImage cpi:source="wim://myserver/unattended/os/7-x64/sources/install.wim#Windows 7 PROFESSIONAL" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
    <!-- MY COMPANY CUSTOMIZATIONS BELOW-->
    <mycompany:custom xmlns:mycompany="urn:schemas-domain-com:mycompany">
        <!-- build description -->
        <cust_description>standard workstation</cust_description>
        <!-- list of packages to install in order. Descriptions are read from U:\scripts\packages.csv -->
        <cust_packages>office2k7.cmd,adobe.cmd,flash.cmd,java.cmd,fonts.cmd</cust_packages>
        <!-- start of the model name string, as reported by WMI, comma separated - allows auto-selection of build. -->
        <cust_models>OptiPlex 745,OptiPlex GX620,VMware Virtual Platform</cust_models>
        <!-- do we want packages.vbs to activate Windows? -->
        <cust_activate>false</cust_activate>
        <!-- do we want a hibernation file wasting disk space? If set to false packages.vbs will disable hibernation support -->
        <cust_hibernate>false</cust_hibernate>
    </mycompany:custom>
</unattend>
 

Sample VBScript code for parsing the XML using MSXML DOM

This is not a complete script – it’s intended purely to illustrate the concept.

'New OS - 7/Vista/2008/2008R2

'read the answer file
Set objXML = CreateObject("Microsoft.XMLDOM")
objXML.Async = "False"
objXML.Load(strMedia & "\sifs\" & strOS & "\" & strBuild & ".xml")

'Additional mass storage and Networking drivers that will be essential for the rest of the build are detected in the
'running Windows PE instance's Driver Store and reflected into the installed OS

'insert the non-WinPE PnP driver discovery paths and credentials for the offlineServicing pass
strXPath = "/unattend/settings[@pass='offlineServicing']/component[@name='Microsoft-Windows-PnpCustomizationsNonWinPE']/DriverPaths/PathAndCredentials/Credentials"
'we need to iterate since there may be several sets of credentials (one for each driver path)
Set colNodes=objXML.selectNodes(strXPath & "/Domain")
For Each objNode In colNodes
  objNode.Text = strDomainName
Next
Set colNodes=objXML.selectNodes(strXPath & "/Password")
For Each objNode In colNodes
  objNode.Text = strPass
Next
Set colNodes=objXML.selectNodes(strXPath & "/Username")
For Each objNode In colNodes
  objNode.Text = strUser  
Next

'Insert hostname
strXPath = "/unattend/settings[@pass='specialize']/component[@name='Microsoft-Windows-Shell-Setup']/ComputerName"
Set objNode=objXML.selectSingleNode(strXPath)
objNode.Text = strComputerName

'insert credentials for Domain join
On Error Resume Next
strXPath = "/unattend/settings[@pass='specialize']/component[@name='Microsoft-Windows-UnattendedJoin']/Identification/Credentials"
Set objNode=objXML.selectSingleNode(strXPath & "/Domain")
If Err.Number = 0 Then
  'Microsoft-Windows-UnattendedJoin exists - carry on modifying (workgroup builds won't have this section in the XML)
  objNode.Text = strDomainName
  Set objNode=objXML.selectSingleNode(strXPath & "/Password")
  objNode.Text = strPass
  Set objNode=objXML.selectSingleNode(strXPath & "/Username") 
  objNode.Text = strUser
End If
On Error Goto 0

'insert manufacturer and model into OEMinfo
strXPath = "/unattend/settings[@pass='oobeSystem']/component[@name='Microsoft-Windows-Shell-Setup']/OEMInformation/Manufacturer"
Set objNode=objXML.selectSingleNode(strXPath)
objNode.Text = strManufacturer
strXPath = "/unattend/settings[@pass='oobeSystem']/component[@name='Microsoft-Windows-Shell-Setup']/OEMInformation/Model"
Set objNode=objXML.selectSingleNode(strXPath)
objNode.Text = strModel & " - Your Company"

strXPath = "/unattend/settings[@pass='oobeSystem']/component[@name='Microsoft-Windows-Shell-Setup']/FirstLogonCommands/SynchronousCommand/CommandLine"
Set colNodes=objXML.selectNodes(strXPath)
For Each objNode In ColNodes
  'Insert the unattended share info and credentials
  If InStr (objNode.Text,"net use *value to be set by install.vbs*") Then
    objNode.Text = "net use " & strMedia & " \\" & strInstallServer & "\" & strShare & " /user:" & strUser & "@" & strDomainName & " " & strPass & " /persistent:no"
  End If
  'Record our build file selection for the package installer later
  If InStr (objNode.Text,"cscript *value to be set by install.vbs*") Then
    objNode.Text = "cscript //nologo " & strMedia & "\scripts\packages.vbs " & strMedia & " " & strOS & " " & strBuild & ".xml"
  End If
Next

Set objNode = Nothing
'write out the answer file
objXML.Save "x:\autounattend.xml" 
Set objXML = Nothing

Maintaining a consistent Windows PE build

UPDATE – new much improved script here.

Having a WDS server with a bootable Windows PE instance is extremely useful. Got a server into a reboot loop by deleting a driver you were sure was inactive? Run out of space on C: and need to image and repartition? OS won’t boot but you need to salvage files? You get the picture. They key though, is making sure you’ve integrated all the network and mass storage controller drivers your hardware will need.

One of the most useful tools that’s not included in the image by default is ImageX – the tool for reading and writing WIM images. Pair this up with the GUI wrapper GImageX and you’ve got yourself a free replacement for Ghost.

Once you’ve downloaded WAIK, making a Windows PE build is a pain – there’s so much messy syntax to get right. Since you’re likely to keep needing to rebuild it to add more drivers, it’s a good idea to write a script to rebuild it from scratch each time. Here’s my build_pe_amd64.cmd:

set SOURCE=C:\WINPE_DRIVERS
set PATH=%PATH%;C:\Program Files\Windows AIK\Tools\PETools\;C:\Program Files\Windows AIK\Tools\%PROCESSOR_ARCHITECTURE%
set PATH=%PATH%;C:\Program Files\Windows AIK\Tools\Servicing
del c:\temp\pe_amd64\ISO\sources\boot.wim
del c:\temp\pe_amd64\winpe_amd64.iso
rd /s /q c:\temp\pe_amd64
call copype.cmd amd64 c:\temp\pe_amd64
dism /Mount-Wim /WimFile:c:\temp\pe_amd64\winpe.wim /Index:1 /MountDir:c:\temp\pe_amd64\mount
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\winpe-scripting.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\en-us\winpe-scripting_en-us.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\winpe-wmi.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\en-us\winpe-wmi_en-us.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\winpe-mdac.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\en-us\winpe-mdac_en-us.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\WinPE-HTA.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Package /PackagePath:"%ProgramFiles%\Windows AIK\Tools\PETools\amd64\WinPE_FPs\en-us\WinPE-HTA_en-us.cab"
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\common\vmxnet3\vmxnet3ndis5.inf
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\pvscsi\pvscsi.inf
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\perc4esi\oemsetup.inf
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\lsi_sas\lsi_sas.inf /forceunsigned
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\nc373i_ris\b06nd.inf
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\sa_5x6x\hpcisss.inf
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\sa_p400\hpcissx2.inf
dism /image:c:\temp\pe_amd64\mount /Add-Driver /driver:%SOURCE%\x64\yk62\yk62x64.inf
copy "%ProgramFiles%\Windows AIK\Tools\PETools\amd64\bootsect.exe" c:\temp\pe_amd64\mount\Windows
copy /y %SOURCE%\common\*.cmd c:\temp\pe_amd64\mount\Windows\System32
copy "%SOURCE%\x64\Tools\*.*" c:\temp\pe_amd64\mount\Windows\System32
copy "%ProgramFiles%\Windows AIK\Tools\amd64\*.*" c:\temp\pe_amd64\mount\Windows\System32
md c:\temp\pe_amd64\mount\scripts
::script for launching my unattended OS installer
copy \\myserver\unattended\scripts\init.vbs c:\temp\pe_amd64\mount\scripts
dism /Unmount-Wim /MountDir:c:\temp\pe_amd64\mount /commit
imagex /export c:\temp\pe_amd64\winpe.wim 1 c:\temp\pe_amd64\ISO\sources\boot.wim
::uncomment the following line to build a bootable ISO image
::oscdimg -n -bc:\temp\pe_amd64\etfsboot.com c:\temp\pe_amd64\ISO c:\temp\pe_amd64\winpe_amd64.iso
ren c:\temp\pe_amd64\ISO\sources\boot.wim boot_amd64.wim
WDSUTIL /Verbose /Progress /Replace-Image /Image:"Microsoft Windows PE 3.0 (x64)" /ImageType:Boot /Architecture:x64 /ReplacementImage /Name:"Microsoft Windows PE 3.0 (x64)" /ImageFile:"c:\temp\pe_amd64\ISO\sources\boot_amd64.wim"
ren c:\temp\pe_amd64\ISO\sources\boot_amd64.wim boot.wim
cd\scripts