MDT – Windows 10 deployment watch

With our MDT environment up and running we’ve been refining our Windows 10 build over the past couple of months, sending out pilot builds to specific areas so we’re confident in the process when it comes to large-scale deployment over summer.#

This post focuses on a few Windows 10-specific tweaks that we’ve made to the Task Sequence that may be of interest…

Thin image approach

In the past I was a fan of what could be called a Hybrid image model in as much that I’d create a “Base” Reference image in a VM, usually comprised of Windows + Office + Updates. That would get captured and become the WIM file that goes into the Task Sequence.

However with Windows 10 I’ve decided to go down the completely thin approach that’s best represented as either a sandwich or hamburger depending on your culinary preference (!) Effectively the deployment gets built from its component parts, starting from an unaltered source Windows 10 WIM file extracted from its parent ISO image.

In our case we’ve settled on Education 1709 x64 as the build to deploy, due to some useful features such as OneDrive Files on Demand and Windows Defender Exploit Prevention. Along the way we’ve also used the 1607 and 1703 builds. The advantage of using the Thin image method is that we can swap the OS out at will with two clicks, rather than having to go through a Capture process that seems to have the potential for error.

Secure Boot validation

Windows 10 1709 brought in some new security features which benefit from machines being converted to UEFI rather than BIOS mode and in some cases (Windows Defender Credential Guard) needs Secure Boot too. Seeing as we need to update the BIOS > UEFI on older machines anyway it made sense to enable Secure Boot at the same time.

Ref: https://docs.microsoft.com/en-us/windows/whats-new/whats-new-windows-10-version-1709

The question was how to ensure that a machine is correctly configured before starting the imaging process (as converting later on is far from ideal).

The answer is to run cmd.exe to send a non-zero return code if specific requirements are met:

  1. Task Sequence variable isUEFI is false and \ or
  2. UEFISecureBootEnabled registry key is 0

If the machine is configured incorrectly the Task Sequence will fail before it even starts to pull down the image. To ensure you catch it early enough add the step here:

Putting the two together looks like this:

  

Removing the cruft

Sadly despite Microsoft giving Education our very own specific build of Windows they didn’t extend the effort into cleaning up the junk that gets pushed down with a standard Windows 10 installation. Seriously who wants Candy Crush on their business machines?!

Fortunately scripts exist to assist with cleaning up the junk shipped with the OS so it’s suitable for deployment. Now we can do this with DISM at image level but again my aim is to avoid tinkering with the Microsoft media if possible so I prefer the following PowerShell method…

PowerShell: Removing UWP apps from Windows 10 1607/1703/1709

Disable Refresh \ Reset

Another Windows 10-specific tweak is to disable the Refresh \ Reset menu that users can access either by using the Settings app or by holding shift while a machine reboots. In our case we don’t want users to wipe their machine clean of provisioned applications and it appears that this functionality will work even without local admin rights (!)

The solution to this one came via the EduGeek forums courtesy of ErVaDy using bcdedit commands:

Ref: http://www.edugeek.net/forums/windows-10/164236-preventing-shift-restart-into-recovery-mode-5.html

Place the commands below into a batch file and run as an Application or Task Sequence step:

reagentc /disable
bcdedit /deletevalue {current} recoverysequence
bcdedit /set {bootmgr} bootems off
bcdedit /set {bootmgr} advancedoptions off
bcdedit /set {bootmgr} optionsedit off
bcdedit /set {bootmgr} recoveryenabled off
bcdedit /set {current} bootems off
bcdedit /set {current} advancedoptions off
bcdedit /set {current} optionsedit off
bcdedit /set {current} bootstatuspolicy IgnoreAllFailures
bcdedit /set {current} recoveryenabled off

Updating OneDrive Files on Demand Client

In a way that only Microsoft can Windows 1709 shipped with an old version of the OneDrive client that doesn’t work with the much-anticipated Files on Demand feature straight out the box 😦

Although the client does auto-update we didn’t want any automatic sync starting without the placeholder functionality being in place so I’ve scripted an Application in the MDT Task Sequence to take ownership of the file on the newly deployed image, copy the latest version of the client over and then set everything back as it was.

For more details and the script itself please see my previous post OneDrive Files on Demand – update!

Pre-staging printer drivers

During our Windows 10 deployment we’re also migrating to a new set of Windows Print Servers, along with new GPOs to map them. However in initial testing I noted the first user to log in had a long wait whilst drivers were copied down from the server and installed.

Although subsequent logins won’t get this issue it doesn’t give a good first impression to the initial user so I wanted to find a way around it.

Step forward the very useful printui.dll 🙂

Ref: https://larslohmann.blogspot.co.uk/2013/12/install-printer-driver.html

Because we’ve rationalised our print fleet over the past few years in a move towards MFDs I only have 3 drivers to cover the entire range of hardware. By using a script method I can then pre-stage the drivers onto the machine at image time and speed up that first logon significantly!

Again paste this into a batch file and call as an Application (use an Application step instead of  Run Command Line as you want the driver files copied into the Deployment Share)

cscript "prndrvr.vbs" -a -m "HP Universal Printing PCL 6" -i "%CD%\HP Universal Print Driver\pcl6-x64-6.4.x.xxxxx\hpcu196u.inf"

Note the use of %CD% to ensure the path to the driver file is resolved correctly!

WSUS resources

Although there’s nothing special about running Windows Updates in MDT (use the built-in Task Sequence steps) we noticed that our WSUS server was struggling and sometimes hung the “Install Updates” step of the Sequence. The WSUS console then become unresponsive on the server end too.

After further research it turns out our increasing number of machines needs more resource than the default WSUS limit of 2GB  in the IIS Application Pool to handle the connections. Upon making the change below it’s back to being stable again.

Ref: https://sysadminplus.blogspot.co.uk/2016/11/wsus-console-crashed-after-running-some.html

Ref: https://www.saotn.org/wsuspool-keeps-crashing-stops

Ref: https://blogs.technet.microsoft.com/configurationmgr/2017/08/18/high-cpuhigh-memory-in-wsus-following-update-tuesdays

Run WinSAT

An oldie-but-goodie; running the WinSAT assessment tool at the end of setup will make sure your machine is properly benchmarked and appropriate performance tuning is performed by Windows. It doesn’t take long so I thought it worth continuing with:

Ref: https://deploymentresearch.com/Research/Post/624/Why-adding-WinSAT-formal-to-your-task-sequence-can-be-a-shiny-thing-to-do

Just add a Run Command Line step with the following in the box:

winsat.exe formal

MDT imaging megapost – part 2 (database automation)

With MDT installed we initially used some basic out-the-box Task Sequences to get up and running. Deployment worked as expected but it was quite a manual process (entering the machine name, selecting Applications to install and so on).

On our old ZCM \ Windows 7 imaging project we were starting from scratch to some extent with a lot of new hardware so entering certain information manually at image time was actually a desired behaviour. Not so much so now with a fairly settled estate and ever increasing time pressures – automation is name of the game.

As such the database-driven model now makes a lot more sense as we were able to export a list of machines and roles from ZENWorks so MDT could “know” what it needs to do with a machine rather than anyone needing to tell it.

SQL Installation

Nice and simple (free too) with SQL Express as per the previous post. One thing you need to watch out for is to ensure Named Pipes are enabled in SQL Server Configuration Manager or you’ll get errors when trying to connect to the database remotely.

Ref: http://www.vkernel.ro/blog/creating-and-configuring-the-mdt-database

Now go ahead and create the database itself…

Ref: https://docs.microsoft.com/en-us/windows/deployment/deploy-windows-mdt/use-the-mdt-database-to-stage-windows-10-deployment-information

Managing the database

The MDT console is functional when it comes to managing the database but it’s not the ideal interface, especially if you need to make a lot of changes as MMC can be somewhat clunky at times. Although you can use the MDT Workbench remotely it’s not perhaps something you’d want to give everyone access to.

However, there is a better way 🙂

Whilst browsing across forums I came across a link to a brilliant little tool called MDT Administrator, it’s currently hosted on the soon-to-be-defunct Codeplex site. Although Microsoft say an archive will be kept running how long for is anyone’s guess so keep a copy saved somewhere safe!

Ref: https://mdtadmin.codeplex.com

It’s a nifty HTA-based front-end that provides a much slicker way to manage your database. Adding and removing Roles is much quicker in particular, which is something we use a lot (more on that later).

One additional tweak to the setup was to create a new group of MDT Database Admins who were granted write access against their SQL login. This meant we could delegate management of the computer records in the database to technicians without needing to open up access to the full Deployment Workbench interface. Perfect for on-the-go updates as machines are moved around and replaced.

Restarting deployment

Sometimes we’ll come across a machine that isn’t in the database, usually something that’s been on the shelf for a while or a laptop that’s been “off the grid” and come back for reimaging. In those cases you only find out that there’s no record after the deployment wizard has started and you get offered a randomly-generated name starting with MININT.

You can also check this in the ZTIGather.log file to see what information was found about the machine and whether any matching records were returned from the database. This step can be handy to troubleshoot unexpected behaviours that can be caused by something a bit out the ordinary e.g. DMI information entered into BIOS incorrectly by the manufacturer, which has happened to us a few times.

To save yourself an unwanted reboot after amending a record in the database hit F8 whilst at the deployment wizard (assuming you’re in PXE environment) then type in the magic command

wpeinit

Deployment will now restart with a fresh “Gather” phase and query the database again to pick up your new record; you should then see the correct name appear in the deployment wizard.

Bulk operations

Picture the situation… you’ve had a batch of 100 new laptops arrive, who gets the painful job of entering them into the database? Answer: PowerShell!

If manual data entry leaves you cold you’ll love the next set of scripts, allowing you to create a CSV of import data then run one command et voila, lots of effort and fingers saved.

First though you need to do a little fix on the database:

Ref: https://syscenramblings.wordpress.com/2016/01/15/mdt-database-the-powershell-module-fix/

The package comes in two parts:

  1. PowerShell cmdlets: https://blogs.technet.microsoft.com/mniehaus/2009/05/14/manipulating-the-microsoft-deployment-toolkit-database-using-powershell/
  2. Import Check script: https://deploymentbunny.com/2016/04/22/os-deployment-using-the-powershell-to-work-with-the-mdt-database-module-sample-1/

The check script is rather important as without it as MDT will quite happily create duplicate records and you don’t want that! If you don’t want to do the additional checks in Active Directory you can disable those sections by commenting them out.

I then made some changes to the Import Check script so it would process a CSV file to do all the work in one go. One big change was to replace the BREAK sections with CONTINUE as I didn’t want one duplicate record error to prevent the rest of the import from running. It seems to work for me but I’d advise testing that yourself before doing the same.

Ref: http://www.computerperformance.co.uk/powershell/powershell_continue.htm

Roles

Another part of the database that comes in really useful is Roles. In our case we install different software for machines deployed in a classroom to those that go in offices. On our previous ZCM imaging system I made a custom script for the technician to select the machine type but now we can automate that via the database.

Once a Role is assigned to a machine specific Applications can be assigned. That’s neat in itself but for added flexibility you can also then query the Roles during Task Sequence execution to take specific actions based on what type of machine you’re dealing with.

Ref: https://docs.microsoft.com/en-us/windows/deployment/deploy-windows-mdt/assign-applications-using-roles-in-mdt

At the moment I’ve stuck to only using one Role per machine in the database to make life easy for myself in the Task Sequence. Reason being that way I know when I query the TS variable “Role001” it will always return the data I’m looking for i.e. is this a classroom machine or one in an office? In an ideal world I’d test with multiple machine Roles to see what order they’re returned in and split things out a bit but I’m short on time and this method works for what we need.

During the Task Sequence I can then use WMI queries to get the granularity required to deploy software for specific machines, more on that in a later post…

MDT imaging megapost – part 1 (our first server)

The great thing about working in the tech field is that it keeps moving on, ever changing, always evolving. That means sometimes you have to let go of systems that were once the bright shining light of progress once it becomes apparent something better has taken its place. Now is that time for my trusty ZCM 11 custom imaging system; built back in 2013 and star of a 6-part thread series I look back on now and think “wow, I actually did that”.

Until I moved imaging onto a Satellite the stats say the original Primary server pushed out over 5000 images. Given the length time the Satellite has been in place, plus the stats from our other sites that figure can easily be doubled and over the course of 4 years around 10,000 image cycles have been completed.

Compared to the previous process that was in place a huge amount of time was saved and allowed us to complete a large-scale Windows 7 migration with relative ease. Add to that a 4-year saving on ENGL license costs and my motley crew of Bash and PowerShell scripts can retire with a satisfied feeling of a job well done 🙂

The future calls, and it’s shaped like the number 10…

However we need to move on, funny enough it’s another OS migration knocking on the door that prompted the change along with a shift in hardware and environment that meant the Linux-based PXE environment was starting to hold us back.

Windows 10 support from ZCM seemed patchy at best, as was timely support for new hardware such as Surfaces and their ilk. Reading the forums and email groups didn’t inspire much confidence either so we decided to start looking elsewhere.

SCCM was the natural direction of travel but having made a substantial investment of time creating ZCM Bundles we weren’t necessarily ready to move all that just yet. Similarly ZCM Patch Management works pretty well these days for covering our 3rd-party apps. With that in mind the Microsoft Deployment Toolkit was the obvious choice.

A nice GUI-based managed scripting environment with Windows PE as the underlying OS ticked all the boxes. Oh and did I mention it’s free!

It’s time for my own MDT… Massive Deployment Thread!

What originally started as a small side-project to push Windows 10 out to a couple of trial tablets has now expanded into a core system that’s been at the heart of our summer works. With that in mind it’s time to write up the journey and the numerous tips, tricks and tools used along the way.

Many of those ideas come from some of the best deployment pros in the business such as Johan Arwidmark, Michael Niehaus and Mikael Nystrom so a big shout out for all the knowledge they share. Hopefully this post will give an idea of how we put those pieces together in a live environment.

The beginning, our first server

Initially we started out deploying MDT for the sole purpose of imaging up a batch of demo Surface 3 devices so the first thing was to spool up a new VM with all the required software and roles installed. Links can be found below to save you some time:

Early fixes and customisations

After getting the basic Deployment Share running we hit a few minor issues that need resolving, which are worth bearing in mind:

Multiple DNS namespaces

We have two domains that are in use internally, one of which usually gets appended as part of the domain join process and the other via DHCP.

In the PE environment the machine isn’t domain joined and as such the default setting in Bootstrap.ini wouldn’t connect to the deployment share as it didn’t know the correct DNS suffix to append.

Ref: https://scottisageek.wordpress.com/2011/12/22/mdt-2010-and-multiple-dns-namespaces/

…we found it quicker in our case to change the DeployRoot setting to the MDT server’s FQDN rather than short name… problem solved 🙂

Share permissions

The default permissions applied to the Deployment Share by the installation wizard weren’t set up as we liked. Can’t remember the exact reason now but looking back documentation on other sites I think the share needed locking down to prevent users viewing the Deployment Share content or (even worse) making unauthorised changes to it (!)

We now have specific AD groups and a service account set up so nominated MDT Administrators can read \ write to the share to upload Application install files etc. but the imaging account (more on that later) can only read and all other users are denied access by virtue of having no rights.

Set UK Locale

A quick an easy tweak sets up the keyboard settings for UK users in Bootstrap.ini

Ref: http://kabri.uk/2010/01/20/sample-bootstrap-ini-for-uk-deployments/

Similarly set them also in CustomSettings.ini

Ref: https://scriptimus.wordpress.com/2011/06/23/mdt-2010-sample-customsettings-ini-for-fully-automated-deployments/

There are quite a few other settings you’ll want to add in CustomSettings.ini but more detail on those will follow in relevant posts so keep your eyes peeled!

Update the Deployment Share

This is one action you’ll soon need to get into the habit of! If you make changes to the settings in any of the .ini files or add drivers that you’ll need in the PE environment (basically network and storage) then you need to update the Deployment Share.

This recompiles the Boot Images to include your changes, otherwise you’ll find all those nice new additions above make no difference whatsoever!

Think of this as step 1 / 2 to completely updating the Boot Images though. If the MDT wizard says that the Boot Images have changed you also need to copy the new WIMs over to WDS so PXE boot is using the latest images.

In WDS browse your server select Boot Images then right click as per screenshot above and click Replace Image. Browse to your Deployment Share’s Boot folder and select the correct image for each architecture.

Windows Deployment Services service won’t start

At an early point in our testing WDS decided it didn’t want to start after a server reboot and was spewing error code 0x906. We weren’t sure why and were on the verge of reinstalling from scratch when I spotted this:

Ref: https://social.technet.microsoft.com/Forums/windows/en-US/265b4b53-63ac-491f-817c-6030daa39b81/cant-start-windows-deployment-services-service?forum=itprovistadeployment

As per Aaron Tyler’s advice in the link above run the wdsutil commands to uninitialize then reinitialize the server manually pointing to the RemoteInstall folder WDS creates.

wdsutil /uninitialize-server
wdsutil /initialize-server /reminst:[PATH_TO_REMOTEINSTALL_DIRECTORY]

Next time…

That should be enough to get your first server up and running. For the second post in the series we’ll look at the MDT Database and how it turns MDT from a good imaging solution into a great one 🙂