Deploy MDT Litetouch on Linux with TFTPD and Syslinux

Deploy MDT Litetouch on Linux with TFTPD and Syslinux

No, I would not like to say hello, Cortana.

Nothing is as monotonous, boring, and brain-numbingly automatable as installing Windows, installing applications, joining a domain, and clicking all the right boxes in all the right places. And it’s even worse in the latest versions of Windows 10, where we’re greeted by the condescending robot voice of Halo’s deceptive antagonist artificial intelligence during the OOBE setup phase.

I’ve taken a particular liking to MDT, Microsoft’s solution to the absolute eye-glazing snorefest of configuring a new workstation or server. Not only does it automatically apply an answer file to get past the distorted screaming of your PC speaker, it gets you past almost all of the setup routine, so you can focus on more important things.

In seriousness, I’m a big fan of automation for a number of reasons. Not only can a tool like this save you time, it can also make sure that every machine you deploy is consistent and compliant with the policies of your organization. Can you really trust a human to go through all the correct steps during setup?

The licensing problem

First off, volume licensing is required to run MDT, or modify images in any way. So don’t do this unless your org is set up correctly.

Next is the issue of server licensing. To do this the ‘right’ way a WDS server would be used to host the PXE boot for MDT litetouch deploys. To me, this doesn’t make a ton of sense. There are plenty of free alternatives that don’t require thousands of dollars in licensing, and more importantly aren’t as cumbersome. If you update MDT boot media with new drivers or directives it requires an RDP session into your WDS server to add the new WIM files to the deployment directory, then removing the old version of the boot media from the server. At its best it’s a simple way to run PXE boot on a fairly static environment, and at its worst it’s a waste of precious Standard licenses and a waste of time managing and patching a Windows server.

Using a Linux server to deploy Windows

If that isn’t sacrilegious, then I don’t know what is.

Fundamentally, what we’re doing is mounting the MDT boot images network share to the TFTP boot directory (readonly of course), configuring a simple PXELINUX loader with memdisk, and configuring our DHCP server to point to this tftp location for boot media.

The first step is to spin up a Ubuntu 18.04 (or whatever your flavour is!) virtual machine, give it a static address, and get it up to date.

Setting it up

Next, the needed packages are installed on the system:

sudo apt install tftpd-hpa pxelinux syslinux cifs-utils 

Mounting the MDT share

To mount the cifs share containing the MDT content, best practice is to use a service account with as little access as possible. In this case, we will make sure our service account is denied interactive logon, restricted in its logon types, and only allowed network access to the MDT share. Lately I’ve taken to using randomly generated GUIDs as service account passwords.

$pw = New-Guid
New-ADUser -Name mdtmount -SamAccountName mdtmount -AccountPassword:($pw | Convertto-SecureString -AsPlainText -Force) -CannotChangePassword:$true -ChangePasswordAtLogon:$false

Now, we hop on over to the Linux server and set up the mount point.

In the interest of best practices, we’re using a credential file that only root has access to.

/root/.cifs

username=mdtmount
userdomain=bork.internal
password=aedb8aeb-6073-446d-96dd-aa993b275741

Then the /etc/fstab is set up such that the MDT directory will mount automatically.

//bork.internal/system/deployment/boot /var/lib/tftpboot/pxelinux/boot cifs _netdev,user=mdtsvc,credentials=/root/.cifs 0 0

Note that this is a DFS namespace in the example. Depending on domain level and the features enabled on the share, it may be required to configure some additional Kerberos parameters for it to work reliably.

PXELINUX configuration

First, we move the PXELINUX boot files to the TFTP directory.

cp /usr/lib/PXELINUX/pxelinux.0 /var/lib/tftpboot/pxelinux
cp /usr/lib/syslinux/modules/efi64/[menu.c32 ldlinux.c32 libutil.c32] /var/lib/tftpboot/pxelinux
cp /usr/lib/syslinux/memdisk /var/lib/tftpboot/pxelinux

Second, we create a simple config file to boot into the LiteTouch boot media.


DEFAULT menu.c32

MENU TITLE PXE Boot Menu
MENU AUTOBOOT Starting local system in # seconds

label bootlocal
  MENU LABEL Boot local device
  MENU DEFAULT
  LOCALBOOT 0
TIMEOUT 100
TOTALTIMEOUT 9000

label mdt_litetouch
  menu label [MDT] LiteTouch PE
  KERNEL memdisk
  INITRD boot/LiteTouchPE_x64.iso
  APPEND iso raw

DHCP Server Config

With the files in place, the DHCP server scope options for the Imaging VLAN are set:

Option Description Value
066 TFTP Server 10.1.10.30
067 File Name pxelinux/pxelinux.0

MDT Share Config

Of course, the standard principles apply for hardening an MDT deployment.

  • File share securables:
    • Remove Authenticated Users read access
    • Add mdt client service accounts with read only access
  • AD securables:
    • Lock down service accounts
    • Delegated access for domain join service account in ‘holding tank’ OU

With that said, we configure the MDT share to generate ISO images (for x64 only) and set up the config files.

Bootstrap.ini

[Settings]
Priority=Default

[Default]
DeployRoot=\\bork.internal\system\deployment
UserDomain=bork.internal
UserID=mdtsvc
UserPassword=288ea175-c7b6-44aa-8aa8-27075142cb7f
SkipBDDWelcome=YES

CustomSettings.ini

[Settings]
Priority=Default
Properties=MyCustomProperty

[Default]
_SMSTSORGNAME=BORK!
OSDComputerName=ws-#right(replace(replace("%SerialNumber%"," ",""),"-",""),10)#
OSInstall=Yes
SkipCapture=YES
SkipAdminPassword=YES
SkipProductKey=YES
SkipComputerBackup=YES
SkipBitLocker=NO
TimeZoneName=Eastern Standard Time 
SkipLocaleSelection=YES
SkipTaskSequence=NO
SkipRoles=NO
SkipRoleDisplay=NO
SkipTimeZone=YES

JoinDomain=bork.internal
DomainAdmin=mdtjoin
DomainAdminDomain=bork.internal
DomainAdminPassword=98600de8-e53e-4576-bf86-2f3c80061ce5
MachineObjectOU=OU=Holding Tank,DC=bork,dc=internal

Using this config file template, our deployments will automatically be named after the service tag, serial number, or UUID, then join the domain inside the ‘Holding Tank’ OU, where an administrator can review the deployment and move the machine object to the correct location within their hierarchy.

Bringing it all together

The process for deploying a new machine will look like this:

  1. Computer boots to UEFI network stack
  2. Computer receives TFTP information from DHCP server
  3. Computer loads PXELINUX boot screen
  4. Technician selects MDT boot from the menu
  5. Computer loads ISO into memdisk before boot:
    • Computer requests ISO file from Linux server
    • Linux server loads ISO file from DFS/File Share
  6. Computer boots into WinPE and uses mdtsvc credentials from the bootstrap config to access the network share directly, and loads configuration settings and task sequence info.
  7. Technician selects a deployment task sequence, either accepting the automatically configured hostname or entering a custom one, and starts the deployment.

A couple coffees later, the deployment is done exactly to the spec with little intervention from either technicians or administrators, and most importantly, Microsoft doesn’t get an extra dime from your tight budget!