Jump to content

Building a GNU/Linux Based Windows Deployment Server

 Today I wanted to discuss one method of how to setup a GNU/Linux based Windows deployment server. This can be used to perform clean installs from extracted Windows.iso files or complete pre-compiled image files of a Windows instance for large scale deployment over a network. For this demonstration I'll be using Ubuntu Server 20.04.5 LTS but these services are openly available on other distributions including UNIX platforms such as FreeBSD if you rather use your preference. The general process is the same.

 

Index

Spoiler

1. Downloading Packages & Necessities

2. Configuring Network Services

     2.1 - DHCP

          2.1.1 - ISC-DHCP-SERVER

          2.1.2 - DNSMASQ

     2.2 - TFTP

          2.2.1 - Configuring TFTP

          2.2.2 - Compiling iPXE from Source w/ Embedded Script

     2.3 - HTTP

     2.4 - SMB

3. Preparing OS for Deployment

     3.1 - Using Windows Setup Files (easy but only for small scale)

     3.2 - Using an Extracted Windows.wim Image File (hard but good for large scale)

          3.2.1 - Initial Setup

          3.2.2 - Manual Deployment

          3.2.3 - Automated Deployment

 

1. Downloading Packages & Necessities

Spoiler

It's going to be the easiest to explain if we go ahead and download everything we need at one time. I'll go into further explanation about them as we go.

 

sudo apt install dnsmasq isc-dhcp-server tftpd-hpa apache2 samba make gcc binutils perl mtools liblzma-dev git -y

 

Now we need to download both the iPXE open source project files and the openly available wimboot kernel.

git clone https://github.com/ipxe/ipxe.git
wget https://github.com/ipxe/wimboot/releases/latest/download/wimboot

 

From a Windows PC we need to download the Windows ADK & WinPE Add-On Deployment Tools. Once both are downloaded and installed run Deployment and Imaging Tools Environment located under Start -> Windows Kits. From here run the command:

copype amd64 C:\WinPE_amd64

Once complete navigate to C:\WinPE_amd64 and move or copy the contents into a folder called winpe.

 

From a Windows PC download a windows10.iso or windows11.iso either directly or through the Windows Media Creation Tool. What you do with it from here will depend on what direction you want to go in Step 3. Preparing OS for Deployment.

 

2. Configuring Network Services

Spoiler

In this section I'll be going over the preliminary setup of each service in order of operation: DHCP, TFTP, HTTP, & SMB.

 

2.1 - DHCP

Spoiler

This is where the packages isc-dhcp-server and dnsmasq come into play. You can use one or the other. Which one depends on your needs.

  • isc-dhcp-server advertises it's own DHCP service. This is only going to want to be used on a network with no other active DHCP server such as a router.
    • If you don't want or can't use isc-dhcp-server you can remove it with sudo apt autoremove isc-dhcp-server
  • dnsmasq can provide a multitude of services one such being ProxyDHCP. ProxyDHCP can be used on a network with an existing DHCP server such as a router.
    • If you don't want or can't use dnsmasq you can remove it with sudo apt autoremove dnsmasq

2.1.1 - ISC-DHCP-SERVER

Spoiler

Start by editing the servers config file /etc/dhcp/dhcpd.conf. In here define the network you want to advertise, the IP of the TFTP server (next-server), and add an option if statement to serve each client a different iPXE file based on their BIOS being Legacy or UEFI.

subnet 10.0.0.0 netmask 255.255.255.0 {
range 10.0.0.2 10.0.0.254;
next-server 10.0.0.1; }

option client-arch code 93 = unsigned integer 16;
  if option client-arch != 00:00 {
     filename "ipxe.efi";
  } else {
     filename "undionly.kpxe";
  }

 

Save the file and start the service ensuring that it's running:

root@WinPE-Server:~# systemctl start isc-dhcp-server
root@WinPE-Server:~# systemctl status isc-dhcp-server
* isc-dhcp-server.service - ISC DHCP IPv4 server
     Loaded: loaded (/lib/systemd/system/isc-dhcp-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-01-16 03:02:33 UTC; 4s ago
       Docs: man:dhcpd(8)
   Main PID: 8469 (dhcpd)
      Tasks: 4 (limit: 230375)
     Memory: 4.7M
        CPU: 14ms
     CGroup: /system.slice/isc-dhcp-server.service
             `-8469 dhcpd -user dhcpd -group dhcpd -f -4 -pf /run/dhcp-server/dhcpd.pid -cf /etc/dhcp/dhcpd.conf

Jan 16 03:02:33 WinPE-Server dhcpd[8469]: Sending on   LPF/net1/66:28:5e:52:af:75/10.0.0.0/24
Jan 16 03:02:33 WinPE-Server dhcpd[8469]: 
Jan 16 03:02:33 WinPE-Server dhcpd[8469]: No subnet declaration for eth0 (192.168.0.126).
Jan 16 03:02:33 WinPE-Server dhcpd[8469]: ** Ignoring requests on eth0.  If this is not what
Jan 16 03:02:33 WinPE-Server dhcpd[8469]:    you want, please write a subnet declaration
Jan 16 03:02:33 WinPE-Server dhcpd[8469]:    in your dhcpd.conf file for the network segment
Jan 16 03:02:33 WinPE-Server dhcpd[8469]:    to which interface eth0 is attached. **
Jan 16 03:02:33 WinPE-Server dhcpd[8469]: 
Jan 16 03:02:33 WinPE-Server dhcpd[8469]: Sending on   Socket/fallback/fallback-net
Jan 16 03:02:33 WinPE-Server dhcpd[8469]: Server starting service.

 

At this point in time if we connect a client and PXE boot you should get an IP address and possibly see the TFTP server IP with the ipxe.efi or undionly.kpxe filename being specified before receiving an error message. This is normal.

 

2.1.2 - DNSMASQ

Spoiler

Start by editing the server's config file /etc/dnsmasq.conf. In here copy/paste the following parameters substituting the IP's in dhcp-range & dhcp-boot for your server's IP. 

port=0
tftp-root=/services/ipxe    
dhcp-no-override
pxe-prompt="PXE booting in", 10
pxe-service=X86PC, "Boot from network", undionly.kpxe
pxe-service=X86-64_EFI, "Boot from network", ipxe.efi
dhcp-range=10.0.0.1,proxy
dhcp-boot=,,10.0.0.1

 

Save the file and start the service ensuring that it's running:

root@WinPE-Server:~# systemctl start dnsmasq
root@WinPE-Server:~# systemctl status dnsmasq
* dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-01-16 03:25:32 UTC; 4s ago
    Process: 8507 ExecStartPre=/usr/sbin/dnsmasq --test (code=exited, status=0/SUCCESS)
    Process: 8508 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=0/SUCCESS)
    Process: 8517 ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf (code=exited, status=0/SUCCESS)
   Main PID: 8516 (dnsmasq)
      Tasks: 1 (limit: 230375)
     Memory: 536.0K
        CPU: 25ms
     CGroup: /system.slice/dnsmasq.service
             `-8516 /usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new --local-service --trust-anchor=>

Jan 16 03:25:32 WinPE-Server systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
Jan 16 03:25:32 WinPE-Server dnsmasq[8507]: dnsmasq: syntax check OK.
Jan 16 03:25:32 WinPE-Server dnsmasq[8516]: started, version 2.80 DNS disabled
Jan 16 03:25:32 WinPE-Server dnsmasq[8516]: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth nettlehash DNSSE>
Jan 16 03:25:32 WinPE-Server dnsmasq-dhcp[8516]: DHCP, proxy on subnet 10.0.0.1
Jan 16 03:25:32 WinPE-Server systemd[1]: Started dnsmasq - A lightweight DHCP and caching DNS server.

 

At this point in time if we connect a client and PXE boot you should get an IP address from the Router, possibly see the ProxyDHCP server IP, and TFTP server IP with the ipxe.efi or undionly.kpxe filename being specified before receiving an error message. This is normal.

 

 

2.2 - TFTP

Spoiler

This is where the tftpd-hpa package and the iPXE GitHub download are going to come into play. We need to:

  1. Configure the TFTP server.
  2. Compile iPXE from source code with an embedded script.
  3. Write the main.ipxe file for our boot option menu.

2.2.1 - Configuring TFTP

Spoiler

To start we need to create a couple of directories for where all our server files will be located. You may opt for an alternative location but I'm going to drop everything in a folder hierarchy in the root directory:

mkdir /win-deploy /win-deploy/ipxe-boot

 

Now we need to modify the TFTP server configuration file /etc/default/tftpd-hpa to match the following settings:

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/win-deploy/ipxe-boot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"

 

Save the file, restart the service, and make sure it's running:

root@WinPE-Server:/# systemctl restart tftpd-hpa
root@WinPE-Server:/# systemctl status tftpd-hpa
* tftpd-hpa.service - LSB: HPA's tftp server
     Loaded: loaded (/etc/init.d/tftpd-hpa; generated)
     Active: active (running) since Mon 2023-01-16 17:07:05 UTC; 44min ago
       Docs: man:systemd-sysv-generator(8)
    Process: 223 ExecStart=/etc/init.d/tftpd-hpa start (code=exited, status=0/SUCCESS)
      Tasks: 1 (limit: 230375)
     Memory: 244.0K
        CPU: 9ms
     CGroup: /system.slice/tftpd-hpa.service
             `-243 /usr/sbin/in.tftpd --listen --user tftp --address :69 --secure /win-deploy/ipxe-boot

Jan 16 17:07:05 WinPE-Server systemd[1]: Starting LSB: HPA's tftp server...
Jan 16 17:07:05 WinPE-Server tftpd-hpa[223]:  * Starting HPA's tftpd in.tftpd
Jan 16 17:07:05 WinPE-Server tftpd-hpa[223]:    ...done.
Jan 16 17:07:05 WinPE-Server systemd[1]: Started LSB: HPA's tftp server.

 

The server is now prepared for the iPXE files in the coming steps.

 

2.2.2 - Compiling iPXE from Source w/ Embedded Script

Spoiler

Navigate to the folder where you downloaded iPXE using Git and perform the following operations:

cp -r ipxe/ ipxe2
mv ipxe legacy && mv ipxe2 uefi

These operations are not explicitly necessary but will make sense as we progress.

 

In the current folder we want to create a script. This script is a set of instructions iPXE will execute upon startup. Copy/paste the contents below into an empty file called embed.ipxe.

#!ipxe
dhcp && goto netboot || goto dhcperror

:dhcperror
prompt --key s --timeout 10000 DHCP failed, hit 's' for the iPXE shell; reboot in 10 seconds && shell || reboot

:netboot
chain tftp://${next-server}/main.ipxe ||
prompt --key s --timeout 10000 Chainloading failed, hit 's' for the iPXE shell; reboot in 10 seconds && shell || reboot

 

Save, exit, then copy the file into the respective directories:

cp embed.ipxe legacy/src/ && cp embed.ipxe uefi/src/

 

Now to compile both files if being performed from your home folder (note: both compiles will take a short while to complete):

cd legacy/src/ && make bin/undionly.kpxe EMBED=embed.ipxe
cd
cd uefi/src/ && make bin-x86_64-efi/ipxe.efi EMBED=embed.ipxe
cd

 

With both files compiled we want to now create main.ipxe. This is the file iPXE will call when it chainloads. Go already and create an empty file with this name and copy/paste the following script substituting the URL IP for your servers:

#!ipxe
set url http://10.0.0.1/winpe/media/

menu
item --gap -- ---------------- iPXE boot menu ----------------
item winpe    Launch WinPE
choose target && goto ${target}

:winpe
kernel ${url}Boot/wimboot
initrd ${url}Boot/BCD BCD
initrd ${url}Boot/boot.sdi boot.sdi
initrd -n boot.wim ${url}sources/boot.wim boot.wim
boot

 

Save, exit, and now to move everything to the TFTP server directory:

mv main.ipxe /win-deploy/ipxe-boot/ && mv legacy/src/bin/undionly.kpxe /win-deploy/ipxe-boot/ && mv uefi/src/bin-x86_64-efi/ipxe.efi /win-deploy/ipxe-boot/

 

At this stage if you PXE boot a network client PXE should chainload iPXE and the embedded script should load main.ipxe (a blue menu on your screen).

 

 

2.3 - HTTP

Spoiler

Due to limitations on the maximum file sizes TFTP can download we need to host an HTTP server. This is where the apache2 package comes into play. Right now if you visit the IP of your server from a web browser you should see an Apache Default Page. We will be doing away with this as we just want it to host large files for iPXE to chainload.

 

Re-configuring Apache from it's default state is a very short & easy task starting with creating a new folder to host it's files:

mkdir /win-deploy/http-boot

 

Now we can edit it's configuration file /etc/apache2/sites-available/000-default.conf to look like the following:

<VirtualHost *:80>
        ServerAdmin some@mail.com
        DocumentRoot /win-deploy/http-boot

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

<Directory />
        Options +FollowSymLinks +Indexes
        Require all granted
</Directory>

 

Save, exit, restart apache2, and make sure the service is running:

root@WinPE-Server:~# systemctl restart apache2
root@WinPE-Server:~# systemctl status apache2
* apache2.service - The Apache HTTP Server
     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2023-01-16 22:28:05 UTC; 4s ago
       Docs: https://httpd.apache.org/docs/2.4/
    Process: 545 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
   Main PID: 549 (apache2)
      Tasks: 55 (limit: 230375)
     Memory: 5.3M
        CPU: 32ms
     CGroup: /system.slice/apache2.service
             |-549 /usr/sbin/apache2 -k start
             |-550 /usr/sbin/apache2 -k start
             `-551 /usr/sbin/apache2 -k start

Jan 16 22:28:05 WinPE-Server systemd[1]: Starting The Apache HTTP Server...
Jan 16 22:28:05 WinPE-Server systemd[1]: Started The Apache HTTP Server.

 

Now copy the entire winpe folder created on the Windows PC into the servers /win-deploy/http-boot directory using your choice of SFTP, a thumb drive, or other copy method. Additionally move wimboot from it's downloaded directory to the new winpe sub-folder on the server with:

mv wimboot /win-deploy/http-boot/winpe/media/Boot/

 

At this stage if you start a PXE client DHCP should chainload iPXE, which will fetch main.ipxe from TFTP thanks to the embedded script, then by manually choosing the first option listed on the screen fetch the kernel and boot files needed from HTTP to enter you into the Windows Pre-installation Enviroment or WinPE for short:

X:\windows\system32>wpeinit

X:\windows\system32>

 

 

2.4 - SMB

Spoiler

This is where the samba package comes in. This is a relatively simple to setup SMB network share for Windows and Linux users. To start we need to create a SAMBA user account with the same name as the system administrator account (other than root). These are accounts that coincide with system user accounts for the purpose of SAMBA. You can add a SAMBA user with the example:

smbpasswd -a admin
New SMB password:
Retype new SMB password:
Added user admin.

 

Afterwords we want to define the parameters that govern the network share by modifying /etc/samba/smb.conf with the following information:

[windows]

path = /win-deply/windows
available = yes
valid users = admin
read only = no
browseable = yes
public = yes
writable = yes

The first line inside the brackets is what people on the network will see the share named as. Everything else including unlisted options can be modified but this will work for our application.

 

Next create the path folder and change the owner to the system user you created a SAMBA account for:

mkdir /win-deploy/windows
chown admin:admin /win-deploy/windows

 

Now restart the service and make sure it's running:

root@WinPE-Server:~# systemctl restart smbd
root@WinPE-Server:~# systemctl status smbd
* smbd.service - Samba SMB Daemon
     Loaded: loaded (/lib/systemd/system/smbd.service; enabled; vendor preset: enable>
     Active: active (running) since Tue 2023-01-17 00:30:38 UTC; 20s ago
       Docs: man:smbd(8)
             man:samba(7)
             man:smb.conf(5)
    Process: 799 ExecStartPre=/usr/share/samba/update-apparmor-samba-profile (code=ex>
   Main PID: 808 (smbd)
     Status: "smbd: ready to serve connections..."
      Tasks: 4 (limit: 230375)
     Memory: 6.6M
        CPU: 72ms
     CGroup: /system.slice/smbd.service
             |-808 /usr/sbin/smbd --foreground --no-process-group
             |-810 /usr/sbin/smbd --foreground --no-process-group
             |-811 /usr/sbin/smbd --foreground --no-process-group
             `-812 /usr/sbin/smbd --foreground --no-process-group

Jan 17 00:30:38 WinPE-Server systemd[1]: Starting Samba SMB Daemon...
Jan 17 00:30:38 WinPE-Server systemd[1]: Started Samba SMB Daemon.

 

We can check to see if it's working by connecting to it from our running WinPE instance:

X:\windows\system32>net use n: \\10.0.0.1\windows
The password is invalid for \\10.0.0.1\windows.

Enter the user name for '10.0.0.1': admin
Enter the password for 10.0.0.1:
The command completed successfully.

X:\windows\system32>n:

N:\

 

If everything executes as exampled above you're in good shape.

 

 

3. Preparing OS for Deployment

Spoiler

At this stage there are two directions you can go. One is much more complicated to do than the other but saves a considerable amount of time when needed for large scale deployment.

 

3.1 - Using Windows Setup Files (easy but only for small scale)

Spoiler

If you don't have many clients to install a clean OS onto you can opt for this solution by mounting the windows10.iso or windows11.iso on a Windows client connected to the installing servers network. Windows supports the ability to mount .ISO's to virtual CD drives just by double-clicking or using Right-Click -> Mount.

 

Connect to the servers SAMBA (SMB) network share using your credentials and copy the contents of the mounted .ISO file to a folder such as w10.

 

After this return to a WinPE client, re-connect to the SMB share if necessary, and enter this folders directory. With the dir command you should now see all of the Windows setup files:

X:\windows\system32>n:

n:\>cd w10

n:\w10>dir
 Volume in drive N is windows
 Volume Serial Number CD92-6DB2
 
 Directory of n:\w10
01/17/2023  08:50 AM   <DIR>          .
01/17/2023  08:48 AM   <DIR>          ..
09/08/2023  12:07 AM              128 autorun.inf
01/17/2023  08:49 AM   <DIR>          boot
09/08/2022  12:07 AM          413,738 bootmgr
09/08/2022  12:07 AM        1,541,648 bootmgr.efi
01/17/2023  08:49 AM   <DIR>          efi
09/08/2022  12:07 AM           74,184 setup.exe
01/17/2023  08:50 AM   <DIR>          sources
01/17/2023  08:50 AM   <DIR>          support
               4 File(s)     2,029,698 bytes
               6 Dir(s) 130,635,268,096 bytes free
               
n:\w10>

 

From here simply running setup.exe will initiate the installation process as if you were using local media such as a bootable USB:

 

176206573_Screenshotfrom2023-01-1712-16-54.thumb.png.c7070741d9f90b49576c0f339ae53622.png

 

Within reason this can be performed on as many simultaneous clients as you desire.

 

3.2 - Using an Extracted Windows.wim Image File (hard but good for large scale)

Spoiler

If you have a large number of identical clients all of whom need the latest Windows updates, 3rd party applications installed, user accounts, and customized settings such as Active Directory (AD) or firewall options you're going to want the most time efficient method which would be this however the setup is a fair bit more complicated.

 

3.2.1 - Initial Setup

Spoiler

To start you need a sacrificial install of Windows with everything configured as you want it. The reason I say sacrificial is because as part of the setup process, for unknown reasons, it sometimes bricks the Windows install. So proceed at your own risk.

 

Once you have your install configured just the way you want it create an empty text file in an easy directory like C:\ and copy/paste the following instructions:

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="oobeSystem">
        <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">
            <OOBE>
                <HideEULAPage>true</HideEULAPage>
                <HideLocalAccountScreen>true</HideLocalAccountScreen>
                <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
                <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
                <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
                <NetworkLocation>Home</NetworkLocation>
                <ProtectYourPC>3</ProtectYourPC>
                <SkipMachineOOBE>true</SkipMachineOOBE>
                <SkipUserOOBE>true</SkipUserOOBE>
                <UnattendEnableRetailDemo>false</UnattendEnableRetailDemo>
            </OOBE>
        </component>
    </settings>
</unattend>

Now rename the file deploy.xml making sure the file extension changed from .txt to .xml.

 

Next launch an Administrative PowerShell and from the System32 directory move to the \Sysprep directory:

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Try the new cross-platform PowerShell https://aka.ms/pscore6

PS C:\Windows\system32> cd .\Sysprep\
PS C:\Windows\system32\Sysprep>

 

In order to prepare the OS for large scale deployment we want the following operations to occur:

  1. Out Of Box Experience
    1. We want OOBE so that the windows instance behaves as though it's brand new.
  2. Generalize
    1. There are many attributes about the current install you do not want to image to other computers such as the Product Key, Computer Name, Hardware information, Wi-Fi credentials, among a plethora of other details.
  3. Shutdown
    1. Automatically shutdown the computer once Sysprep is done with it's job.
  4. Unattend
    1. This will point to our deploy.xml file. It's purpose is to skip the normal Windows operations at first startup since everything has already been defined for Sysprep by us.

In the language of PowerShell that will look like this:

sysprep.exe /oobe /generalize /shutdown /unattend:c:\deploy.xml

 

Assuming you do not immediately receive a Sysprep Was Not Able to Validate Your Windows Installation error (if you do though please refer to the .log file for details on the reason) Sysprep will slowly run the preparation process and shut down the computer.

 

At this time PXE boot this machine and load WinPE as we need to create a windows.wim file from the C:\ partition. Once WinPE has loaded the connected drive should have automatically mounted the C:\ partition with it's respective drive letter. If not there are steps to assign it. Now connect to the SAMBA (SMB) network share.

 

With both the C:\ drive and N:\ share accessible it's time to image the partition with:

dism /capture-image /imagefile:"n:\windows.wim" /capturedir:c:\ /name:windows

This will launch DISM the Deployment Image Servicing and Management tool. If everything goes correctly the windows.wim file will be created after a couple minutes and deposited onto the SAMBA (SMB) network share folder.

 

3.2.2 - Manual Deployment

Spoiler

To perform a manual deployment we need to create a few files and put them on the SAMBA (SMB) server. These files include:

  • ApplyImage.bat
  • uefi.txt
  • legacy.txt

The contents of each file are as follows:

 

ApplyImage.bat:

@echo off
rem == ApplyImage.bat ==

rem == These commands deploy a specified Windows
rem    image file to the Windows partition, and configure
rem    the system partition.

rem    Usage:   ApplyImage WimFileName 
rem    Example: ApplyImage E:\Images\ThinImage.wim ==

rem == Set high-performance power scheme to speed deployment ==
call powercfg /s 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c

rem == Apply the image to the Windows partition ==
dism /Apply-Image /ImageFile:%1 /Index:1 /ApplyDir:W:\

rem == Copy boot files to the System partition ==
W:\Windows\System32\bcdboot W:\Windows /s S:

:rem == Verify the configuration status of the images. ==
W:\Windows\System32\Reagentc /Info /Target W:\Windows

echo All operations executed. Press any key to shutdown.
pause >nul
wpeutil shutdown

 

uefi.txt:

rem == CreatePartitions-UEFI-FFU.txt ==
rem == These commands are used with DiskPart to
rem    create four partitions
rem    for a UEFI/GPT-based PC.
rem    Adjust the partition sizes to fill the drive
rem    as necessary. ==
select disk 0
clean
convert gpt
rem == 1. System partition =========================
create partition efi size=100
rem    ** NOTE: For Advanced Format 4Kn drives,
rem               change this value to size = 260 ** 
format quick fs=fat32 label="System"
assign letter="S"
rem == 2. Microsoft Reserved (MSR) partition =======
create partition msr size=16
rem == 3. Windows partition ========================
rem ==    a. Create the Windows partition ==========
create partition primary 
rem ==    c. Prepare the Windows partition ========= 
format quick fs=ntfs label="Windows"
assign letter="W"
list volume
exit

 

legacy.txt:

rem == CreatePartitions-BIOS-FFU.txt ==
rem == These commands are used with DiskPart to
rem    create three partitions
rem    for a BIOS/MBR-based computer.
rem    Adjust the partition sizes to fill the drive
rem    as necessary. ==
select disk 0
clean
rem == 1. System partition ======================
create partition primary size=100
format quick fs=ntfs label="System"
assign letter="S"
active
rem == 2. Windows partition =====================
rem ==    a. Create the Windows partition =======
create partition primary
rem ==    c. Prepare the Windows partition ====== 
format quick fs=ntfs label="Windows"
assign letter="W"
list volume
exit

 

The contents of each file can be customized quite a bit to suit your application needs including details on how to include a Recovery Partition if your deployment requires it. Documentation on this can be found on Microsoft's website. With the files created move them to the SAMBA (SMB) share.

 

To deploy the windows.wim image first determine weather the computer to be imaged is booted into Legacy or UEFI mode as this will determine how the drive is to be partitioned. Once this is known connect to the SAMBA (SMB) share and run the single respective partitioning script. Note this is only recommended if the computer has a single storage drive:

diskpart /s n:\uefi.txt
diskpart /s n:\legacy.txt

Regardless of disk capacity these scripts as they are will fill the drive

 

Now to apply the image file:

N:\ApplyImage.bat N:\windows.wim

If everything works correctly the created partitions will be prepped and the .wim file written to the primary partition. Once complete you should be offered to shutdown the machine. Once turned back on it may take a few minutes but you will be dropped to the Windows login screen or desktop.

 

3.2.3 - Automated Deployment

Spoiler

If you want to make things go even faster you can automate the WinPE process with an embedded script by modifying X:\Windows\System32\startnet.cmd but to do this is a process in itself. To start begin by creating the three files outlined in 3.2.2 - Manual Deployment but do not upload them to the server. Keep them on your desktop for now.

 

From here connect to the HTTP server and download /winpe/amd64/media/sources/boot.wim. Boot.wim is the core of WinPE. Once downloaded put it in an easy directory like C:\ then create a folder called winpe next to it.

 

Open an Administrative PowerShell and run the command:

Dism /Mount-image /imagefile:c:\boot.wim /index:1 /mountdir:c:\winpe

This cracks open WinPE's own .wim file and makes it so WinPE in itself can be modified.

 

From here enter the winpe folder you created and navigate to /Windows/System32/startnet.cmd. This file is only responsible for executing a single program wpeinit but you can append to and modify the file to suit your auto deployment needs beyond that. For example:

wpeinit
@echo off
echo Checking if PC is booted in Legacy or UEFI mode.
wpeutil UpdateBootInfo
for /f "tokens=2* delims=	 " %%A in ('reg query HKLM\System\CurrentControlSet\Control /v PEFirmwareType') DO SET Firmware=%%B
@if x%Firmware%==x echo ERROR: Cannot determine boot method. Manual entry required.
@if x%Firmware%==x goto CHOICE
@if %Firmware%==0x1 diskpart /s legacy.txt 
@if %Firmware%==0x2 diskpart /s uefi.txt
echo Connecting to SMB Server.
net use n: \\10.0.0.1\windows password /user:\admin
goto INSTALL
:CHOICE
echo To format storage for Legacy BIOS run: diskpart /s legacy.txt
echo To format storage for UEFI BIOS run: diskpart /s uefi.txt
echo ------------------------------------------------------------- 
echo To complete install run: ApplyImage.bat N:\windows.wim
goto SHELL
:INSTALL
echo Installing Windows Image.
ApplyImage.bat N:\windows.wim
:SHELL
@echo on

This modification to startnet.cmd determines if the computer is booting via Legacy or UEFI, partitions the drive accordingly, connects to the SAMBA (SMB) share for you, and executes the windows.wim deployment. After editing this file you can save it.

 

Now copy the three files ApplyImage.bat, legacy.txt, & uefi.txt into the winpe/Windows/System32 directory.

 

To save the changes made to boot.wim and unmount it run the command:

dism /unmount-image /mountdir:c:\winpe /commit

 

With the file now updated with our modifications re-upload it to the HTTP server in the same directory we got it from replacing the currently existing one.

 

Consequently if dealing with multiple machines of different models you're going to want a separate boot.wim WinPE file pointing to a different windows.wim as the image drivers persist for that computers hardware devices and may not match with different hardware. In this scenario you can create sub-directories inside the TFTP server's /winpe/amd64/media/sources directory and have main.ipxe point to each different boot.wim file after adding additional menu options to it's config file.

 

 

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×