We have been using the Microsoft Deployment Toolkit (MDT) in LTI/Lite Touch mode here at the University for a long time now. Why, we used it to deploy XP back when MDT still was called the Business Desktop Deployment Solution Accelerator (BDD). In this time, we have gone though several different driver management methods. Gone are the nightmare days of dealing with OEMSETUP files, $OEM$ directories, can elaborate “DriverPack” injection scripts for XP (thank goodness).
With the release of Vista, we moved from a PnP free-for-all model of driver detection. After Windows 8.0, we found we really needed to separate our drivers by operating system. Thus, we created Win7, Win8, and WinPE driver selection profiles.
But now we are finding that driver sprawl is becoming a major issue again. On many new systems we run though a seemingly successful deployment, but end up with a non-responsive touch screen, a buggy track pad, and (sometimes) a very unstable system.
Starting this week, we are trying a new hybrid driver management approach. We will create a driver folder for each computer model sold though our computer depot. I have developed a custom bit of VBScript to check to see if the hardware being deployed to is a known model. Driver injection will be restricted to this model if a match is found. The script contains additional logic to detect support for both Windows 7 and Windows 8 variants, and to select the most current drivers detected. Unknown models will fall back on the PnP free-for-all detection method.
Here is how it works…
- Create a new group in your OS deployment task sequence named “Custom Driver Inject”, or something similar. Grouping all actions together will allow easier transfer of these custom actions to other Task Sequences:
- Under this new group, add a new action of type “Set Task Sequence Variable”. Name the variable “TargetOS”,and set the value to the OS that you are deploying from this task sequence. You must follow the same naming convention that you use in your Out-of-box driver folder. I use Win(X), where (X) is the major OS version of the drivers in the folder. In this example, I have chose “Win8”:
- Add an action of type “Run Command Line”. Name this action “Supported Model Check”. Under the Command line field, enter “cscript “%SCRIPTROOT%\ZUVMCheckModel.wsf”. (We will import this script into the deployment share later on.)
- Add a sub-group named “Supported Model Actions”. Under the “Options” tab, add a condition of type “Task Sequence Variable”. Use the variable “SupportedModel”, the Condition “equals”, and the Value “YES”. (The SupportedModel variable gets set by the CheckModel script run in the previous step.):
- Under this new group, add a new action of type “Set Task Sequence Variable”. Name this task “Set Variable DriverGroup002”. Under “Task Sequence Variable”, set “DriverGroup002”, and set the value to “Models\%TargetOS%\%Model%”. (Note: You could use “DriverGroup001”, but I already am using this variable to hold a default group of drivers that I want added to all systems. The value “%TargetOS%\%Model%” defines the path to the driver group in the deployment share. If you use a different folder structure, you will need to modify this path.):
- Create a new task of type “Inject Drivers”. Name this task “Inject Model-Specific Drivers”. For the selection profile, select “Nothing”. Be sure to select “Install all drivers from the selection profile”. (NOTE: The dialog implies that we will be injecting only divers from a selection profile. In fact, this step will inject drivers from any paths defined in any present “DriverGroupXXX” Task Sequence variables.)
- Now, under our original Custom Driver Inject group, add a new task of type “Inject Drivers”. Choose from the selection profile “All Drivers”, or use a different fallback selection profile that suits the needs of your task sequence. This time, select “Install only matching drivers from the selection profile”:
Under the “Options” tab, add the condition where the “Task Sequence Variable” named “Supported Model” equals “NO”:
This step will handle injection of matching drivers into hardware models for which we do not have a pre-defined driver group.
- Optionally, you now can open the “CustomSettings.ini” file and add the following to your “Default” section:
(I have a “Peripherals” driver group configured which contains USB Ethernet drivers used in our environment. These are a necessity when deploying to hardware that does not have an embedded Ethernet port, such as the Dell XPS 12 and XPS 13. You also could add common peripherals with complicated drivers such as a DisplayLink docking station or a Dell touch screen monitor.)
- Add the “ZUVMCheckMedia.wsf” script to the “Scripts” folder of your deployment share. The code for this script is included below. I think the script should be fairly easy to adapt for your environment.
- Finally, structure your “Out-of-Box Drivers” folder to contain a “Models” folder, and a single folder for each matching hardware model in your environment. I get most of our driver collections from Dell:
(NOTE: Thanks Dell!)
The real challenge of maintaining this tree is in getting the model names right. Use “wmic computersystem get model” to discover the model string for any new systems in your environment. A table of a few current models I have been working with is included below.
Dell Marketing Model Name to WMI Name Translator Table:
- Dell XPS 12 (first generation) – “XPS 12 9Q23”
- Dell XPS 12 (second generation) – “XPS 12-9Q33”
- Dell XPS 13 (first generation) – “Dell System XPS L321X”
- Dell XPS 13 (second generation) – “Dell System XPS L322X”
- Dell XPS 14 – “XPS L421Q”
- Dell Latitude 10 – “Latitude 10 – ST2”
- VMware Virtual Machine – “VMware Virtual Platform”
- Microsoft Hyper-V Virtual Machine – “Virtual Machine”
A fun nuance we encountered last week was a Latitude E5430 model that contained “no-vPro” after the model number. Dell does not provide separate driver CABs for vPro/non-vPro models, so I added a regular expression test for Latitudes, and strip any cruft after the model number. There is one more problem down…
The following site contains a list of older model name translations:
As you can see, most Latitudes and Optiplexes follow sane and predictable model name conventions. I wish the same were true for the XPS.
Finally, I am indebted to the following sources for their generously detailed posts on driver management. Without their help, I doubt I would have been able to make this solution fly:
Jeff Hughes of the Windows Enterprise Support Server Core Team:
Andrew Barnes (aka Scriptimus Prime), whose posts on MDT driver management give the basics DriverGroups and model selection:
AND, of automating driver import into MDT (written for MDT 2012… some changes required for 2013):
The incredible Michael Neihaus, who in this post discusses the use of DriverGroups and Selection Profiles:
And finally Eric Schloss of the “Admin Nexus”, who give me the idea of developing a fallback for systems that do not match a known model. It was this key bit of smarts that gave me the confidence to move forward with a model-specific driver grouping strategy:
(NOTE: WordPress stripped off the WSF headers and footers from my script. These are the first three and last two lines in the script. If you copy from this post, please remember to place greater than and less than tags around these lines before running, as indicated in the comments.)
' Uncomment and wrap each of the following three lines in less than/greater than characters to convert them to tags.
'script language="VBScript" src="ZTIUtility.vbs"/
'// Main Class
'// Constructor to initialize needed global objects
Private Sub Class_Initialize
'// Main routine
' // File: ZTIUVMCheckModel.wsf
' // Purpose: Checks the model of this system against
' // a list of known machine models. Returns
' // TRUE if a matching model is detected.
' // Usage: cscript ZUVMCheckModel.wsf /Model: [/debug:true]
'Use the following lines for debugging only.
'oEnvironment.Item("TargetOS") = "Win7"
'oEnvironment.item("DeployRoot") = "c:\local\mdt"
'oEnvironment.Item("Model") = "Latitude E6500 some annoying variation"
'End debug Params
Dim aModels() 'Array of models taken from DriverGroups.xml
Dim bOldDrivers 'Boolean indicating drivers present for an older OS version
Dim i 'Generic integer for looping
Dim j 'Generic integer for looping
Dim iRetVal 'Return code variable
Dim iMaxOS 'Integer representing the highest matching OS driver store
Dim oXMLDoc 'XML Document Object, for reading DriverGroups.xml
Dim Root,NodeList,Elem 'Objects in support of oXMLdoc
Dim sDGPath 'Path to DriverGroups.xml file
Dim sInitModel 'String representing the initial value of
Dim sItem 'Item in aModels array.
Dim sMaxOS 'OS Name of highest matching OS driver store
Dim sOSFound 'OS Name for a given matching driver set.
oLogging.CreateEntry "Begin ZUVMCheckModel...", LogTypeInfo
'Set the default values:
oEnvironment.Item("SupportedModel") = "NO"
iMaxOS = CInt(Right(oEnvironment.Item("TargetOS"),1))
'wscript.echo "Default value for iMaxOS = " & iMaxOS
bOldDrivers = false
sInitModel = oEnvironment.Item("Model")
'wscript.echo "sInitModel value = " & sInitModel
Set oRegEx = New RegExp
oRegEx.Global = True
oRegEx.IgnoreCase = True
'Modify the detected model name to handle known variations:
oRegEx.pattern = "Latitude"
if oRegEx.test(sInitModel) then
oLogging.CreateEntry "Model is a Latitude. Cleaning up the model name...", LogTypeInfo
oRegEx.pattern = " "
set oMatch = oRegEx.Execute(sInitModel)
'wscript.echo "oMatch Count is: " & oMatch.count
if oMatch.Count > 1 then
i = oMatch.item(1).FirstIndex
oEnvironment.Item("Model") = Left(sInitModel,i)
'Check for DriverGroups.xml file, which will contain the supported model list:
iRetVal = Failure
iRetVal = oUtility.FindFile("DriverGroups.xml", sDGPath)
if iRetVal Success then
oLogging.CreateEntry "DriverGroups file not found. ", LogTypeError
oLogging.CreateEntry "Path to DriverGroups.xml: " & sDGPath, LogTypeInfo
'Parse the DriverGroups.xml file:
oLogging.CreateEntry "Parsing DriverGroups.xml...", LogTypeInfo
Set oXMLDoc = CreateObject("Msxml2.DOMDocument")
oXMLDoc.setProperty "SelectionLanguage", "XPath"
Set Root = oXMLDoc.documentElement
Set NodeList = Root.getElementsByTagName("Name")
oLogging.CreateEntry "NodeList Member Count is: " & NodeList.length, LogTypeInfo
'oLogging.CreateEntry "NodeList.Length variant type is: " & TypeName(NodeList.Length), LogTypeInfo
i = CInt(NodeList.length) - 1
ReDim aModels(i) 'Resize aModels to hold all matching DriverGroup items.
'oLogging.CreateEntry "List of Available Driver Groups:", LogTypeInfo
i = 0
For Each Elem In NodeList
if InStr(Elem.Text,"Models\") then
aModels(i) = Mid(Elem.Text,8) 'Add text after "Models\"
'oLogging.CreateEntry aModels(i), LogTypeInfo
i = i + 1
oLogging.CreateEntry "End Parsing DriverGroups.xml.", LogTypeInfo
'Loop through the list of supported models to find a match:
oLogging.CreateEntry "Checking discovered driver groups for match to: " & oenvironment.Item("Model"), LogTypeInfo
For Each sItem in aModels
oLogging.CreateEntry "Checking Driver Group: " & sItem, LogTypeInfo
i = InStr(1, sItem, oEnvironment.Item("Model"), vbTextCompare)
'wscript.echo TypeName(i) 'i is a "Long" number type.
If i 0 Then
oLogging.CreateEntry "Matching Model found.", LogTypeInfo
j = InStr(sItem,"\")
sOSFound = Left(sItem,j-1)
'wscript.echo "sOSFound = " & sOSFound
if (InStr(1,sOSFound,oEnvironment.Item("TargetOS"),vbTextCompare) 0) then
oLogging.CreateEntry "Drivers matching the requested OS are available. Exiting with success.", LogTypeInfo
oEnvironment.Item("SupportedModel") = "YES"
iRetVal = Success
Main = iRetVal
if iMaxOS > CInt(Right(sOSFound,1)) then
iMaxOS = CInt(Right(sOSFound,1))
'wscript.echo "iMaxOS = " & iMaxOS
sMaxOS = sOSFound
bOldDrivers = true
'wscript.echo "sMaxOS = " & sMaxOS
If bOldDrivers Then 'Run if sMaxOS is defined... set a boolean when this is defined and test against that?
oLogging.CreateEntry "Model drivers were found for an OS older than the one selected...", LogTypeWarning
oEnvironment.Item("SupportedModel") = "YES"
oEnvironment.Item("TargetOS") = sMaxOS
oLogging.CreateEntry "No matching drivers were found for this model.", LogTypeInfo
oLogging.CreateEntry "End ZUVMCheckModel.", LogTypeInfo
iRetVal = Success
Main = iRetVal
' Uncomment and wrap each of the following two lines in less than/greater than characters to convert them to tags.