jump to navigation

Hard Link Migration using USMT October 12, 2010

Posted by Dave Field in USMT.
1 comment so far

Okay, so you’ve probably heard about how USMT can now migrate files without actually removing them from your computer…

You hadn’t? Well, let me be the first to tell you, and show you a short test you can do to prove this capability to yourself.

Hard Links

The NTFS file system supports a method of linking files that will be familiar to those with backgrounds in UNIX or Linux. These file system structures are called links. Essentially, they are aliases that point at a physical file stored somewhere else on a storage volume. The file can be accessed from either location, just as if it was stored there. The exact mechanism by which this is accomplished is different in NTFS, but the feature looks and acts the same.

How USMT uses Hard Links

When the User State Migration Tool (USMT) uses hard links, it creates a folder on the file system and writes links in it to files that are to be migrated to the new operating system. When the new operating system is installed, scripts can delete the original folders, clearing space on the disk for the new OS. As long as the folder created by USMT is not deleted, either directly or by formatting the volume, the files are preserved by the hard links. When the OS installation is completed, USMT runs again, restoring the original file links and removing the hard link folder.

Does this really work?

Try this short test. On a command prompt, issue the commands below and observe the output:

Create two folders for the test:

C:\>mkdir test1

C:\>mkdir test2

Now, write a file into the first folder:

C:\>Echo “Hello world!” > .\test1\test1.txt

Verify the file is readable:

C:\>type .\test1\test1.txt

“Hello world!”

Now make a hard link to a filename in the second folder:

C:\>mklink /H c:\test2\test2.txt c:\test1\test1.txt

Hardlink created for c:\test2\test2.txt <<===>> c:\test1\test1.txt

Verify that you can read the second “file”.

C:\>type test2\test2.txt

“Hello world!”

You are reading the original through a hard link!

Now delete the first file:

C:\>del test1\test1.txt

Is the file gone? Let’s check:

C:\>type .\test1\test1.txt

The system cannot find the file specified.

Looks gone to me…let’s check the second file:

C:\>type test2\test2.txt

“Hello world!”

More in using USMT to migrate users

Well, that’s about it. USMT preserves your data right on the disk even as the new operating system is installed. You’ve seen it with your own two eyes, so you know it works. Now take USMT for a spin. You can find out more about USMT in Tim Mintner’s post: Understanding USMT with MDT 2010


User Tiles in the Domain October 8, 2010

Posted by Micah Rowland in Uncategorized.

In a previous post, I described a solution for handling user account tiles for local accounts. A reader recently emailed me inquiring about user tiles for domain users and I realized that I had failed to cover how Windows 7 handles this and how an IT department could leverage this functionality.

For those of you who have read the previous post, you’ll remember that Windows 7 (and Vista) stores the usertile picture that is displayed on login and on the start menu in the registry buried in the SAM hive. This article assumes you have read the previous one and will only briefly reiterate key points as necessary.

For domain accounts, the story is slightly different. Domain account usertiles are not in fact stored in the registry but in a much more accessible location, C:\ProgramData\Microsoft\User Account Pictures\ . If only local accounts were so easy. But don’t worry, we’ll still have a fun time. The usertile files for each user are saved as DOMAIN+username.dat. For example, John Doe at Contoso would be stored as CONTOSO+jdoe.dat. The contents of this dat file will look very familiar to any of you who have looked at local usertiles in the registry. In fact, the hex data stored in this file is EXACTLY the same as how it would be stored in the registry.

So lets get our plan of attack:

  1. Login to a domain account. Any will do.
  2. Set our usertile the old fashioned way.
  3. Retrieve the generated dat file.
  4. Use it!


Let’s take the following scenario posed by one reader. Contoso would like users in each of 5 departments to have a department specific usertile. First we login using a domain account (for our example John Doe will do) and set its usertile to the first department’s desired tile. We then copy the C:\ProgramData\Microsoft\User Account Pictures\CONTOSO+jdoe.dat file to a holding area and rename it to departmentname.dat. We then repeat for the remaining 4 departments. Now that we have the proper dat files we can leverage them in a few different ways. Most users will go the route of the logon script: Check for group membership to ascertain the department of the user (or read an Active Directory attribute) and copy the proper department dat file into the local computer’s User Account Picture directory. We could also preload our OS deployment with a set of all user files for all members of the domain (probably only a good idea in an organization with a small number of users). Using a logon script will ensure that the proper department icon is used, even if a user changes it, though placing a complementing logoff script would be prudent to prevent any non-compliance.

Whatever way you cut it, it’s pretty much up to you at this point. Bon Appétit!

P2V Migration for Software Assurance Beta 2 Now Available – with System Center Configuration Manager 2007 integration September 26, 2010

Posted by keithga in Announcements, MDT 2010, System Center Configuration Manager, Troubleshooting, USMT, VBscript, Windows 7.
comments closed


We’ve been busy here at Xtreme Consulting Group, recently Keith worked as a Developer on the P2V Migration project with Microsoft.

For more information on P2V Migration, click here.

P2V Migration adds documentation and support of System Center Configuration Manager 2007 Zero Touch Installation! 

What is better than spending a moment to kick off a completely automated process to redeliver an existing operating system as a virtual machine within a new build of Windows 7?
Answer: Making the entire process “zero touch” without necessitating a visit to the target computer or manually initiating the migration!

P2V Migration for Software Assurance can now be implemented using System Center Configuration Manager 2007 Operating System Deployment as well as native Lite Touch Installation with the Microsoft Deployment Toolkit 2010 U1. Computer refresh, replace and restore task sequence templates for Configuration Manager are included and documented in this Beta release.

clip_image001 P2V Migration templates integrated with Microsoft Deployment Task Sequence options in System Center Configuration Manager. Can be created and advertised as with other task sequence options.

Additional optimizations beyond Configuration Manager functionality included in this release are:

1. Better flexibility for backing-up and restoring VHD files using default file locations

2. Support for PCs using system and boot volumes

3. Globalization of scripts to handle varying regional and locale formats

4. General bug fixes and improved documentation

These fixes reflect the feedback of our Connect community and MVPs – thanks to everyone for submitting feedback!

Download P2V Migration for Software Assurance Beta 2 now:

P2V Migration for Software Assurance

New to P2V Migration for Software Assurance?


This solution was built to help unblock OS deployments by redelivering blocking users’ old Windows environments, applications and browsers seamlessly in Windows 7 using automated physical-to-virtual migration

P2V Migration for Software Assurance uses the Microsoft Deployment Toolkit, Sysinternals Disk2VHD and optionally System Center Configuration Manager 2007 to convert a user’s existing Windows XP or newer client environment to a virtual hard disk then automates the delivery of an updated and personalized Windows 7 operating system containing a virtual machine with the user’s previous Windows environment, applications and Web browser. The user’s previous virtual desktop retains its existing management components, domain membership and policies. The process also publishes applications and the browser for the user to access them seamlessly within Windows 7’s start menu.

How it Works

clip_image002 Completely automated process enables the previous operating system to be a child virtual machine inside the Windows 7 host.
clip_image003 Standalone application and Internet Explorer links published from virtual machine to native Windows 7 start menu. These applications can be launched individually using RemoteApp integration – without showing the entire virtual machine’s desktop.


Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Windows 7 IE8 Favorites Bar Organization – A Descent Into Binary Registry Storage September 24, 2010

Posted by Micah Rowland in PowerShell, Windows 7.

Recently a client requested that the favorites and a different set of favorites links be populated in the favorites bar area based on the geographic location of the imaged computer. Most of the normal favorites were static, that is to say the same no matter which location the computer was at, only a few needed to be customized. In addition, the links in the Favorites Bar were required to be displayed in a specific order with any additional links placed on the far right. On the surface this appeared to be a pretty straight forward request. If only it was…

Initially I attempted to delete the contents of the %USERPROFILE%\Favorites\ folder and add a task sequence to copy a captured Favorites folder’s contents in. Unfortunately, this only partially worked. The links were required to have their respective icons cached and copying in a new set caused all of the links to revert to the default link icon. In addition, the Favorites Bar was empty and the Favorites menu had an additional folder named Favorites Bar that contained the links that should have been on the Favorites Bar itself. Time to dig in.


On the surface it appears that Favorites Bar links are stored at %USERPROFILE%\Favorites\Favorites Bar. Looking at the properites window, the Read-only check box is blacked out, a good indication that the folder is a “special folder”. Digging a bit deeper, I found that the “Favorites Bar” folder is a special folder whose actual folder path is %USERPROFILE%\Favorites\Links (even though it displays as “Favorites Bar”). When Internet Explorer launches, it checks for the Links folder, not the Favorites Bar folder. If the Links folder does not exist, one is created as a special folder that displays in Windows as “Favorites Bar”.


I’m always a fan of letting Windows do what it’s best at instead of trying to reverse engineer everything. So, to start I included the base set of favorites in the image. I then wrote a PowerShell script that determined the location based on the computers default gateway, opened the links that required customization, read through the lnk file and replace the FeedUrl and URL properties with the correct values, and rename the url files that required custom names reflecting the location. Here’s a brief script that illustrates the one used. Lets assume the link is named Link.url and the customer has 5 sites with different gateways. This script determines the gateway, maps it to a static list (you could use a environmental variable set by MDT), and then replaces the CITY in the link http://www.contoso.com/CITY/site.html and renames the favorites file.

#Get Gateway (assuming only one active network connection)
$strComputer = "."
$Nics = Get-wmiobject win32_NetworkAdapter -comp $strComputer
foreach ($Nic in $Nics)
{     $Config = Get-Wmiobject win32_NetworkAdapterConfiguration | where {$_.index -eq $Nic.index}     if ($config.IPAddress -ne $null)     {         $VGateway = $config.DefaultIPGateway     }
#Setup hash table and set urlLocation and city variables
$GatewayList = @{"" = "urlfolder1";""  = "urlfolder2";"" = "urlfolder3";""  = "urlfolder4";""    = "urlfolder5"}
$CityList = @{"" = "City1, CA";""  = "City2, CO";"" = "City3, WI";"" = "City4, IA";"" = "City5, NY"}
$urlLocation = $GatewayList[$VGateway]
$city = $CityList[$VGateway]
#Repeat for each file that needs customization
$file = get-item 'c:\users\USERNAME\favorites\links\CustomizedLinkFolder\Link.url'
$newcontents = ""
$reader = $file.opentext()
while ($reader.EndOfStream -eq $FALSE)
{     $curline = $reader.ReadLine()     if ($curline.contains("BASEURL="))     {         $newcontents += "BASEURL=<a href="http://www.contoso.com/"">http://www.contoso.com/"</a> + $urlLocation[0] + "/site.html`n"     }     elseif ($curline.contains("URL="))     {         $newcontents += "URL=<a href="http://www.contoso.com/"">http://www.contoso.com/"</a> + $urlLocation[0] + "/site.html`n"     }     else     {         $newcontents += $curline + "`n"     }
out-file -filepath $file -InputObject $newcontents
#After all are edited, rename webslices to city
get-childItem C:\users\USERNAME\Favorites\Links\*.url | rename-item -newname { $_.name -replace 'CITYHERE', $city }

Ok, that’s not too bad, just cracking open the URL files and editing inline… So now I have the right links, on to the Favorites Bar!

Using Sysinternals Process Monitor, IE was monitored and the first time Internet Explorer opens, process monitor showed it writing to a  the contents of the Favorites Bar and stores it in the registry in a key named Order located at:


So let’s open up the registry and take a look…



Oh boy!

A half hour later and a few well crafted web searches later (A needle in a haystack search… got lucky) landed me at a website that described the format of this key for Windows XP and Windows Vista.


After comparing the binary format as described at this site with the stored values, something didn’t quite line up so I loaded the hex into excel and started reverse engineering the data using the provided binary format information as a starting point.


The information from Code Project was invaluable at deconstructing this (and I learned a lot about how Microsoft stores data in binary registry values). The Subrecord1 header in Windows XP is 18 bytes long, in Windows Vista is 36 bytes long, and, as I found, in Windows 7 is now 40 bytes long. There may be other changes to each field, but the main change is the size of that header. A side point: the length field of the header is stored in 2 bytes which as you read the hex need to be reversed…. so a length of 1C0 (448 bytes) is stored as C0 01.

Code Project does a  good job of describing the format, once you understand it… I’ll try to briefly explain and diagram it, I hope it clears it up a bit.

The data is stored much like a TCP IP packet with encapsulation (stay with me here). Think of it as a giant envelope that has a bunch of envelopes inside, and each envelope inside has an envelope in it that contains a letter. The binary data starts with a header that is 20 bytes long. This header tells us how many bytes long the payload is and how many items are in it. After we read the header, the first payload (envelope) follows. This envelope has an 8-byte header as well that tells you how long (how many bytes) its payload is as well as *TADA* the order it is to appear in the Favorites Bar. The length is how we know where one favorites bar item ends and the next begins. We then grab that many bytes and we have the item envelope. The item envelope has a 28-byte header that tells us what type of item it is (link or folder). Finally we have the “letter”, the actual data about the favorites bar item. The data contains such information as the file name of the item, the type of the item (link or folder)


After rereading that explanation above, I’ll be honest, it’s a complex topic and trying to explain it may have just made some readers’ nose bleed.

Sufficed to say, the above information allowed me to craft a PowerShell script that reads the data, scans each item and checks it’s longname property for a specific set of strings, and sets the order value in the purple header as desired.

And here’s the PowerShell script to reorder:

$key = get-item HKCU:\software\microsoft\windows\currentversion\explorer\menuorder\Favorites\Links
$value = $key.getvalue("Order")
write-host Original Size: $value.count
[byte[]] $newvalue = @()
$newvalue = $value[0..19]
$payload =  $value[20..$value.GetUpperBound(0)]
$list = @{}
[int] $i = 0
$remPayload = $payload.Clone()
write-host Payload Size: $remPayload.Count
while ($remPayload.Count -gt 8)
{     [byte[]] $currentrecord = @()     $length = $remPayload[3]*65536+$remPayload[2]*4096+$remPayload[1]*256+$remPayload[0]     $currentrecord = $remPayload[0..($length-1)]     write-host Record $i Size = $length     $list.add($i, $currentrecord)     $remPayload = $remPayload[$length..$remPayload.GetUpperBound(0)]     $i++
#$list.add($i.tostring(), $remPayload)
$order = 6
write-host Begining For list is $list.count members
for ($i = 0; $i -lt $list.count; $i++)
{     write-host Processing list item $i order $list[$i][4]     if ($list[$i].count -gt 8)     {          $startposition = $list[$i][-8] + 49          $teststring = ""          for ($y=1; $y -le 14; $y++)          {             $teststring += [char] $list[$i][$startposition + $y]          }          #$teststring          $n = [char](0)          if ($teststring.Contains("D"+$n+"o"+$n+"w"))          {             $list[$i][4]=0          }          elseif ($teststring.Contains("W"+$n+"e"+$n+"a"+$n+"t"+$n+"h"+$n+"e"+$n+"r"))          {             $list[$i][4]=1          }          elseif ($teststring.Contains("T"+$n+"r"+$n+"a"+$n+"f"+$n+"f"+$n+"i"+$n+"c"))          {             $list[$i][4]=2          }          elseif ($teststring.Contains("N"+$n+"e"+$n+"w"+$n+"s"))          {             $list[$i][4]=3          }          elseif ($teststring.Contains("W"+$n+"e"+$n+"b"))          {             $list[$i][4]=4          }          elseif ($teststring.Contains("C"+$n+"o"+$n+"n"+$n+"t"+$n+"o"+$n+"s"+$n+"o"))          {             $list[$i][4]=5          }          else          {             $list[$i][4]=$order             $order++          }     }
}    for ($i = 0; $i -lt $list.count; $i++)
{     if ($list[$i].count -gt 8)     {         write-host List item $i byte 10 = $list[$i][10] order = $list[$i][4]             $newvalue += $list[$i]     }
}        $value.count
set-itemproperty HKCU:\software\microsoft\windows\currentversion\explorer\menuorder\Favorites\Links Order $newvalue
write-host Total List Items: $list.count

As you can see, the script is very static, it’s looking for a link that has Dow, Weather, News, Web, or Contoso and puts those in positions 0 through 5, any other links are put afterwards.

Theoretically this script could be executed as part of a logon script so that the favorites bar gets reorganized everytime the user logs on and places any user added links at the end of the favorites bar.

Well that’s it. Wish this was stored in a much easier format like XML. C’est la vie.

UserTile Automation June 23, 2010

Posted by Micah Rowland in USMT, VBscript, Windows 7.

Recently a customer requested that two accounts be created during the MDT process and required each to have a preset user account picture (referred to by Windows as the User Tile). There are a number of ways to accomplish this, USMT/Easy Transfer wizard being the easiest. However, due to the processes they already had built into the base image, I decided the best tack was to investigate the actual process employed in storing the User Tile and build automation that could quickly accomodate changes to the specifications of which image to use. After a quick search of the web, it seems that this particular problem, programatically changing the User Tile has not yet found a solution. I present to you my findings along with a sample script to tackle this seemingly simple manual process.

The first step in automating any process is to do it manually first and watch what happens. I rely on the Sysinternals tool Process Monitor as well as Process Explorer for most of my research. Launching Process Explorer as an administrator led me down a few dead ends. It was only after running Process Explorer in the System context using PSExec with the -s and -i switches that I was able to locate the location that Windows 7 uses to store the user tile.

The User Tiles configuration information is stored in Windows registry at HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\########, where ######## is a unique 8 digit hexadecimal ID, in as a binary value named UserTile. But how can we cross reference a username to this 8-digit ID? Glad you asked!

Taking a further look at the registry key, you’ll notice that beneath the user ids, there is another key called Names. If you expand this key you’ll see a list of all local user accounts on the system. Upon opening any of the user keys you’ll notices that only a default value exists. However, take a look at the value type… It is not a standard type, but a hexidecimal number 0x### where the ### id crossreferences nicely with the list of user ids above. In our script we need only pad this value with leading 0’s until it reaches the desired 8-digits. Trying to retrieve this hexidecimal “type” throws exceptions in nearly every method I tried. Exporting the key to a .reg file gave me the output I needed to be able to search for a specific username and retrieve its id.

The SAM Hive of the registry is not generally accessible to any user accounts and is configured, by default, to only be accessed by the System account. To maintain security, both the read and the write operations necessary to modify the UserTiles programmatically are accomplished by executing the operations in the System context. Execution in the system context is accomplished by utilizing the PSExec tool.

When a user interactively changes the UserTile by means of the User Account control panel, Windows resizes the image specified by the user to 128×128 and saves new image as a 24-bit bitmap file in the C:\ProgramData\Microsoft\User Account Pictures\ folder as USERNAME.bmp. The bitmap is then stored in the registry in the SAM and the user’s contact card is updated.

The binary data is composed of a header followed by a payload containing the binary graphic used for the UserTile and closed with a footer that contains the path to the file used as the UserTile. The header is 16 bytes long.

  1. 12-bytes (seem to be constant at 01 00 00 00 03 00 00 00 01 00 00 00)
  2. 4-byte field representing the size of the payload

The payload data reveals that the image stored in the registry is 126×126 pixels, presumably to make up for the 1 pixel wide border around the image when displayed on the logon screen and on the start menu. Further, the image is stored in 16-bit color depth using BI_BITFIELDS compression.

The footer contains the type of image file used and the location of the file used using Unicode (2-bytes per character). The format is as follows:

  1. 4-byte field (purpose unknown, possibly the length of the following field)
  2. A null-terminated Unicode string representing the file type
    1. Eg. “bmp” = 62 00 6D 00 70 00 00 00
  3. 4-byte field (purpose unknown, always 02 00 00 00)
  4. 4-byte field representing the payload (bitmap) size in bytes
  5. A null-terminated Unicode string representing the file location padded to the nearest 4 bytes.

Phew! Needless to say, this level of detail is not necessary for part 1 of this post, however in crafting a truely dynamic programmatic approach to changing the uer tile, we will need this information.


It should be possible to engineer an application that accepts a username and a bitmap file to use. However, for part 1, it is simpler to export the registry keys from a sample machine using either the reg.exe or the regedit.exe utility under the System context using PSExec.

When scripting this automation, we must be aware that the first time PSExec is run, a EULA must be accepted. To avoid this, the following registry file is imported (AcceptPSExecEULA.reg):

Windows Registry Editor Version 5.00

The following script was created to accomplish the goal of changing the UserTiles:

'   Title:   User Tile Change Script
'   Author:  Micah Rowland (Xtreme Consulting)
'   Date:    07/14/2010
'   Desc:    This script is designed to programatically replace
'            a single local user's User Tile on Windows Vista
'            and above.
'   Prereq:  This script requires the use of PSExec available
'            from http://live.sysinternals.com/psexec.exe
'   Usage:   UserTile.vbs USERNAME USERTILEFILE

set objShell = CreateObject("Wscript.Shell")
set objFSO = CreateObject("Scripting.FileSystemObject")
set objArgs = Wscript.Arguments
set objShell = CreateObject("Wscript.shell")
if CheckArgs() <> "" then
     Wscript.echo Checkargs() & vbcrlf
     wscript.echo "Arguments Invalid. Usage: ChangeUserPicture.vbs USERNAME UserTileFile"
end if

strUsername = objArgs(0)
strUserTileFile = objArgs(1)
strUserIndex = GetUserIndex()
set objFile = objFSO.GetFile(strUserTileFile)
set objTS = objFile.OpenAsTextStream(1)
strUserTile = objTS.ReadAll
wscript.echo strUserTile
strRegFile = objShell.ExpandEnvironmentStrings("%temp%") & "\UserIndexes2.reg"
set objRegFile = objFSO.OpenTextFile(strRegFile, ForWriting, true)
contents = "Windows Registry Editor Version 5.00" & vbcrlf & vbcrlf & "[HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\" & strUserIndex & "]" & vbcrlf & strUserTile
objRegFile.Write contents

strImport = "PSEXEC -S -I reg import " & strRegFile
objShell.Run strImport

Function GetUserIndex

     ' This function exports the Names key of the SAM from the registry to determine a match between the user account provided
     ' and its hex ID for use in creating the new registry file for import. Because the .reg file is exported in Unicode format
     ' we use the type command to pipe it from stdout to a new text file.

     strUsername = objArgs(0)
     strUserTileFile = objArgs(1)
     objShell.run "cmd /c reg export HKLM\SAM\SAM\Domains\Account\Users\Names %temp%\UserIndexes.reg /y"
     objShell.run "CMD /C type %temp%\userindexes.reg > %temp%\userindexes.txt"
     set objFile = objFSO.GetFile(objShell.ExpandEnvironmentStrings("%temp%") & "\userindexes.txt")
     set objRegExport = objFile.OpenAsTextStream(FORREADING)
     curLine = objRegExport.ReadLine()
     do until instr(lcase(curLine), lcase(strUserName)) or objRegExport.atendofStream
          curLine = objRegExport.ReadLine
     if objRegExport.AtEndOfStream then
          wscript.echo "Username not found."
          curLine = ObjRegExport.ReadLine
          tmpGetUserIndex = mid(curLine, instr(curLine, "(") + 1, len(curline) - instr(curline, "(") - (len(curLine) - instr(curline, ")")+1))
          do until len(tmpGetUserIndex) = 8
               tmpGetUserIndex = "0" & tmpGetUserIndex
          GetUserIndex = tmpGetUserIndex
     end if
End Function

Function CheckArgs()
     ' This function makes sure that 2 arguments were provided and that the filename provided exists
     if objArgs.Count <> 2 then
          CheckArgs = "Exactly 2 arguments must be specified."
     elseif not objFSO.FileExists(objArgs(1)) then
          CheckArgs = "File not found."
          CheckArgs = ""
     end if
 End Function

The script syntax is: SetUserTile.vbs USERNAME FILE

The file used by this script consists of only the UserTile value from a registry export of a preconfigured UserTile. This can be accomplished by setting a local user account’s picture, opening regedit using the psexec -s -i regedit.exe command, navigating to the SAM key mentioned above, determining the correct user account as detailed above, and exporting the key. Then remove all data in the .reg file except the “UserTile=hex:…” entry.

To leverage this script, a command-script file was created and added to the Software Installation task sequence.

reg import AcceptPSExecEULA.reg
cscript SetUserTile.vbs "USERNAME" DATAFILE.txt

I hope to have a commandline based program developed in the future to tackle this which will accept any sized bitmap image as it’s input as opposed to using a captured registry value. I hope you have enjoyed this post. If you have any questions feel free to ask!

Introduction June 23, 2010

Posted by Micah Rowland in Uncategorized.

My name is Micah Rowland and I have been added to the Xtreme Deployment consulting group as an expert in automation and reverse engineering. As we all know, some of the tasks that customers ask of us to accomplish during the MDT process are very simple to do manually, but in the background, are tricky to nail down when we go to automate the same processes.  In my posts, I hope to reveal undocumented aspects to the Windows operating system and show how to develop automation around these processes. Let’s get started!

Assign Drivers to Computer Makes and Models March 11, 2010

Posted by keithga in Uncategorized.

Speaking of Make and Models… one of the things I’ve been experimenting with lately is mixing driver management styles (grouping by Make+Model vs PnPID).



Make/Model Match

Historically, if you had different hardware platforms, and you wanted to install drivers on each type, you would create separate packages for each Make and Model. Then you could query the Make and Model information from the BIOS to determine which package to install.



For example here are four Make+Model examples:

Make Model
Dell D630
Dell D830
HP DC7800
HP DC7900

The disadvantage of this method is that you have to update the driver packages when new Models come along, and it’s also possible that you might keep multiple instances of the same driver package across many Make and Model repositories. 




PnP-ID Match

With MDT, ZTIDrivers.wsf was designed to do things in a different manner. Instead of downloading drivers and grouping them based on Make/Model, you would import the driver directly into MDT, MDT would parse the driver package, and MDT would install the driver package on the machine if the Plug and Play ID matched.



For example, Windows might search for Plug and Play ID’s that look like:






Generally this system works better than copying based on Make and Model except for a few points:

  • You must import the drivers in a correct fashion so MDT can parse the INF files, and so each driver package is a seperate entry (more on importing drivers later…)
  • Some PC Makers only certify (support?) a subset of driver versions, so it is possible that MDT may give Windows the latest non-certified version of a driver.
  • There also may be compatibility problems with specific drivers. When placed on some other platforms. (However IMHO, if a driver *can* be installed on a machine, but crashes the machine, then that is a bug of the driver).


Hybrid Make-Model + PnPID Match Solution

For my MDT environments, I don’t want to place all drivers into Make/Model groupings by default, since I loose the advantages of ZTIDrivers.wsf where it copy by PnPID. For example, I have the drivers for my Dell D620 integrated now, but it’s good to know that I probably have good coverage for any D820’s out there since they share mostly the same components.


I’ve seen some MDT implementations that totally throw away the ZTIDrivers.wsf PnPID style in favor of maintaining a manual mapping of drivers to Make+Model. However, I do concede, that there are some drivers out there that “… do not play nicely with others…”, and need to be quarantined somehow, and according to a make+model works well.


Create a Folder Structure in MDT Workbench:
























Then in your CustomSettings.ini file, you can add the following: 





(Note that DriverSelectionProfile=nothing is required if using DriverGroups).

%Make% and %Model% will be auto-expanded in the customsettings.ini file with the Make and Model values from the system BIOS.

  • If you have a driver that will work for all Makes and Models, then place it under \Common.
  • If you have a driver that is only supported for a single Manufacturer, then place it under \%Make%\Common.
  • If you have a driver that only works on a specific Make and Model, then place it under \%Make%\%Model%.

This should allow you to use generic drivers by default, moving drivers to specific makes and models when required.


Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Keith Garner on MDT 2010 March 10, 2010

Posted by keithga in Announcements, MDT 2010, PowerShell, Speaking Engagements.
comments closed

(It’s been busy around here at Xtreme Consulting)…

Keith was interviewed on the PowerScripting Podcast last week!

MDT 2010 uses Powershell quite extensively for Server Management, and Xtreme Deployment has been developing custom scripts for MDT 2010.

You can hear the podcast here.

– Keith

Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Bitlocker Recovery Password saved to file January 22, 2010

Posted by keithga in MDT 2010.
1 comment so far

Saw a question posted recently:

In MDT deployment I have Bitlocker set to save the recovery key to AD.  However, I am noticing that it is also copying the recovery key to either C: root or the USB flash drive.  How do I control this behavior?

In MDT 2010, the ZTIBDE.wsf script will perform most nasty administrative tasks in the background automatically. That is the beauty of MDT. However, some administrators may wish to control this Recovery File in a manner other than the default, which is to save the file to the C: drive or to a USB Key.

MDT Saves the recovery key even though the administrator told MDT to save the Password into Active Directory, as a backup process, just in case AD was *not* able to save the data to AD.

Disable Key Save

There are two ways to prevent ZTIBDE.wsf from saving the Administrator password in Active Directory.


Comment out lines 722 – 724 in the ZTIBDE.wsf script. (MDT 2010 Only).


Set the variable in your customsettings.ini file to point to a location that is cleaned at the end of the Task Sequence process:


If you don’t save the Password, and the AD backup of the recovery key fails for some reason, you will have no record of the recovery key.


Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Great Overview on Windows Deployment A-Z… January 18, 2010

Posted by keithga in Announcements, MDT 2010, Windows 7.
add a comment

Our friend Jeremy Chapman has written a whitepaper:

Deploying Windows 7 from A to Z.doc

It’s a good introductory reference to MDT, SCCM, and Deployment in general.


Keith Garner is a Deployment Specialist with Xtreme Consulting Group