jump to navigation

Windows 7 Notification Area Automation – Falling Back Down the Binary Registry Rabbit Hole July 8, 2011

Posted by Micah Rowland in PowerShell, Windows 7.
comments closed

Once more unto the breach dear friends.

After countless hours of searching for a programmatic way to modify the notification settings of the task tray icons, I came to the conclusion that there are many questions and no answers out there. I embarked once again on the fun task of reverse engineering a binary registry setting to change a setting that should be pretty straight forward but, alas, is not. So let’s dive in shall we:

The notification settings for the task tray are stored in the registry at HKCU\Software\Classes\Local Settings\Microsoft\Windows\CurrentVersion\TrayNotify in the IconStreams value as a binary registry key. Luckly for us, the organization of the key is not nearly as hard to understand as the Favorites Bar. The binary stream begins with a 20 byte header followed by X number of 1640 byte items where X is the number of items that have notification settings. Each 1640 byte block is comprised of at least (one of the sections is not fully decoded so it may be made up of 2 or more sections) 5 fixed byte width sections as follows:

  • 528 bytes – Path to the executable
  • 4 bytes – Notification visibility setting
  • 512 bytes – Last visible tooltip
  • 592 bytes – Unknown (Seems to have a second tool-tip embeded in it but the starting position in the block changes)
  • 4 bytes – ID?

For the purposes of my automation, the first two blocks will serve very nicely.

    [Parameter(Mandatory=$true,HelpMessage='The name of the program')][string]$ProgramName,
    [Parameter(Mandatory=$true,HelpMessage='The setting (2 = show icon and notifications 1 = hide icon and notifications, 0 = only show notifications')]
        [ValidateScript({if ($_ -lt 0 -or $_ -gt 2) { throw 'Invalid setting' } return $true})]

$encText = New-Object System.Text.UTF8Encoding
[byte[]] $bytRegKey = @()
$strRegKey = ""
$bytRegKey = $(Get-ItemProperty $(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath).IconStreams
for($x=0; $x -le $bytRegKey.Count; $x++)
    $tempString = [Convert]::ToString($bytRegKey[$x], 16)
        0 {$strRegKey += "00"}
        1 {$strRegKey += "0" + $tempString}
        2 {$strRegKey += $tempString}
[byte[]] $bytTempAppPath = @()
$bytTempAppPath = $encText.GetBytes($ProgramName)
[byte[]] $bytAppPath = @()
$strAppPath = ""

Function Rot13($byteToRot)
    if($byteToRot -gt 64 -and $byteToRot -lt 91)
        $bytRot = $($($byteToRot - 64 + 13) % 26 + 64)
        return $bytRot
    elseif($byteToRot -gt 96 -and $byteToRot -lt 123)
        $bytRot = $($($byteToRot - 96 + 13) % 26 + 96)
        return $bytRot
        return $byteToRot

for($x = 0; $x -lt $bytTempAppPath.Count * 2; $x++)
    If($x % 2 -eq 0)
        $curbyte = $bytTempAppPath[$([Int]($x / 2))]
            $bytAppPath += Rot13($curbyte)

        $bytAppPath += 0

for($x=0; $x -lt $bytAppPath.Count; $x++)
    $tempString = [Convert]::ToString($bytAppPath[$x], 16)
        0 {$strAppPath += "00"}
        1 {$strAppPath += "0" + $tempString}
        2 {$strAppPath += $tempString}
if(-not $strRegKey.Contains($strAppPath))
    Write-Host Program not found. Programs are case sensitive.

[byte[]] $header = @()
$items = @{}
for($x=0; $x -lt 20; $x++)
    $header += $bytRegKey[$x]

for($x=0; $x -lt $(($bytRegKey.Count-20)/1640); $x++)
    [byte[]] $item=@()
    $startingByte = 20 + ($x*1640)
    $item += $bytRegKey[$($startingByte)..$($startingByte+1639)]
    $items.Add($startingByte.ToString(), $item)

foreach($key in $items.Keys)
$item = $items[$key]
    $strItem = ""
    $tempString = ""

    for($x=0; $x -le $item.Count; $x++)
        $tempString = [Convert]::ToString($item[$x], 16)
            0 {$strItem += "00"}
            1 {$strItem += "0" + $tempString}
            2 {$strItem += $tempString}
        Write-Host Item Found with $ProgramName in item starting with byte $key
            $bytRegKey[$([Convert]::ToInt32($key)+528)] = $setting
            Set-ItemProperty $($(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath) -name IconStreams -value $bytRegKey

So what are we doing here. First we ask for two commandline arguments, the name of the program (normally the executable of the program) which is case sensitive, and the setting value. The possible settings values are:

  • 0 = only show notifications
  • 1 = hide icon and notifications
  • 2 = show icon and notifications

We then read in the current value from the registry and store it in a byte array, then create a string representation of the byte array to search for the application in.

We then encode the application name supplied in both a byte array and a string as with the registry value, then we search for the application in the registry key. If not found, we throw an error and drop out. Otherwise we continue.

We then store the header in its own byte array (we don’t use this header but if we need it in the future, we’ve already got the code to segregate it). After that, we loop through the remaining bytes, 1640 bytes at a time, and store each block in it’s own array which in turn is placed in a hash table using the begining byte position as its key.

Finally we loop through each key and search again for the application. If it is found, we set the 529th byte equal to the setting value passed in and then write the byte array back into the registry.

The astute script reader and novice cryptographer will notice our old friend ROT13. It turns out that Microsoft didn’t want us playing with this key so they used the unbreakable ROT13 algorithm on its contents. ROT13 is a mathematical substitution cypher (it’s much simpler than it sounds) which advances each alpha character 13 letters forward so A=N B=O C=P D=Q and so on. I’ve created a function that handles this in a case sensitive way and call it as needed.

So we have our script, but there’s one last wrinkle. The IconStreams registry value is read into memory by Explorer.exe when explorer loads and all changes to the notification area are stored in memory, then written to the registry on shutdown. This means that if we run our script, not only will we see no results right away, those results will be overwritten with the current settings when we restart the computer. Not good. It’s a simple fix though. We launch our script from a batch file and we execute taskkill /im explorer.exe /f then we execute our script, then we restart explorer.exe.

That’s pretty much it. No need for rainbow diagrams of nested dynamically sized items this time.

I will be cleaning up the script and updating this post later with additional information (default user hive info). Stay tuned!


Duplicate Driver Tool. January 10, 2011

Posted by keithga in MDT 2010, PowerShell, Troubleshooting.
comments closed


I just created a PowerShell script that can automatically detect duplicate drivers within a MDT Deployment share. For those of you who are not manually associating each driver to a specific Make and Model, this can help in trying to find drivers that are duplicates, or drivers that have been replaced by newer versions.

Typically having more than one driver in your MDT database won’t cause any errors during OS Installation, Windows will automatically determine the best driver out of all possible matches, and install only that one. However, as a Driver Database gets larger and larger, the MDT console may operate slower, and the installation process will also slow as MDT copies more possible driver matches.

This script is not a fully automated process. The output of the script is in text format, and the administrator must manually remove the drivers marked as duplicate/deprecated.


…\DupeDriverTool.ps1 [[-SelectionProfile] <String>] [[-DPDrive] <String>] [[-DPShare] <String>] [-WhatIf] [-Confirm] [-Verbose]

DPShare – By default, the script will use the First Deployment share found on your local MDT Console. Override using this parameter.

SelectionProfile – By default the script will use the “Everything” Selection Profile Group, override using this parameter.

Verbose – Use Verbose to display more debugging information and to display a list of OK drivers at the end of the report.

Each driver package within the drivers.xml file looks something like:

<driver guid=”{1d30e8a3-f9ca-479f-bc89-9c929c9f647f}” enable=”True”>
   <Name>Intel System dmi_pci.inf</Name>

Process Flow

The driver dupe tool runs as a PowerShell script, and must run on a machine with MDT 2010, or MDT 2010 Update 1 installed, as it uses some of the MDT PowerShell providers to manage the drivers.

The script will go through all driver packages and PnPID’s, looking for instances where two driver packages have matching PnPID’s. If we can do an intelligent job of determining which of the matching drivers contain the best version of the driver, we no longer need to maintain the drivers for the old drivers.

At the end of processing, the script will display a list of driver packages that are “safe” for removal. These are drivers where *all* PnPID’s are superseded by other “better/newer” drivers.

The Rules

  • Signed drivers are always preferred over unsigned drivers.
  • Signed driver dates are used to compare drivers, not driver versions.
  • Never modify any part of a Signed Driver, including the *.inf file. If you do so, the driver will no longer be signed.
  • It is always assumed that newer drivers are better than older drivers. If not, then you will need to manually keep track of which drivers are better.
  • Only driver packages where *all* PnPID’s are superdeded by better drivers are marked for removal.
  • Use the Verbose switch to see more detail while processing.


  • Find Out of Box Drivers that exist within the OS as in-box drivers.
  • Find Out of Box Drivers that exist within WinPE.
  • Automatically delete the drivers on the machine if dupes are found.
  • No SCCM support now, only MDT 2010.




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.

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

MDT 2010 Application ordering (New Tool) December 9, 2009

Posted by keithga in MDT 2010, PowerShell, VBscript.
comments closed

Got an E-Mail from a friend of mine recently:

Are you bored? 🙂 Might be a good utility to build. The PowerShell script [..] posted at http://blogs.technet.com/mniehaus/archive/2009/09/09/sorting-the-contents-of-an-mdt-2010-deployment-share.aspx shows how to rearrange the items in a folder – basically, it just reorders the GUIDs in the group and then saves the modified GUID list. The script only does alphabetical sorting, but some might want to manually rearrange – not terribly easy to do in PowerShell, so it would require a real UI.

For most scenarios, I recommend using Application Dependencies to ensure that applications install in the correct order.

I really didn’t think much of it until recently. When I had some applications that could install stand alone, by themselves, however when both were installed, they needed to be installed in a specific order. If I were creating a CustomSettings.ini file, I would manually ensure that the Applications were placed in order. However, if the user selected the applications from the wizard, then there was no way to ensure the order of the apps unless we modified the order within the ApplicationGroups.xml manually. <yuck>

The blog above shows how application ordering can be done. Simply create a temporary sub-folder, an move the items *in order* to the temporary subfolder and then move everything back.


This solution should work for sorting both Folders and Application Items within MDT 2010. The application is a simple wrapper around the MDT 2010 Powershell provider, and simply makes calls to move Applications and/or folders using Powershell.

User Interface

The wizard will prompt you for the correct MDT 2010 Deployment Share to use. The deployment share must be visible within the MDT 2010 console on the same machine and using the same user account. If you have not opened any MDT 2010 Deployment shares on this computer and this account, please run the MDT 2010 console, and open your Deployment Share.

When selected, the wizard will display a list of all applications and application folders present on the MDT 2010 Deployment share. You may highlight the Application you wish to move, and use the “Move Up” and “Move Down” buttons to rearrange the order of the application. 


When you are done, press the “Commit” button to run the Powershell script that performs the re-ordering. The applications will not be sorted until the script has finished running.

The MDT 2010 Powershell Provider will do all the necessary work to ApplicaitonGroups.xml on the back end.


You can view the script by saving the results to a txt file.

Restrictions and Limitations

It is recommended that you backup your MDT 2010 deployment periodically,

You can sort the contents of only one folder at a time.

You can not sort Folders within Application entries. Folders are always displayed first.




This tool is provided “as-is”, with no warranties.
You agree not to hold the author, Keith Garner liable for any damages.
This tool is provided “Free of Charge” for “Evaluation” purposes only.
This tool is copyrighted by the author, Keith Garner, and he retains all
ownership, this tool is not public domain.
You are not permitted to redistribute this tool without the express written
consent of the author, Keith Garner.
The license for this tool can be revoked and/or superseded at any time, by the author, Keith Garner.
Of course, you should backup your critical files before running any 3rd party program downloaded off the internet.

By Keith Garner ( http://keithga.com ) – Deployment Consultant – Dec, 2009
Xtreme Consulting Group ( http://deployment.xtremeconsulting.com )
Microsoft Deployment Toolkit 2010 ( http://microsoft.com/deployment )
Copyright Keith Garner (keithga.com), All Rights Reserved.

New Tool: MDT 2010 Build-out/Hydration Evaluation November 13, 2009

Posted by keithga in MDT 2010, PowerShell, VBscript, Windows 7.
comments closed

I wanted to give a glimpse of some projects we are working here at Xtreme Consulting Services – Deployment Team.

This is a Microsoft Deployment Toolkit 2010 Build-out (sometimes called Hydration) system.

It starts off as a small 3MB *.zip file. The MDTBuildout.exe program will automatically download all required files (around 10GB of files), create a Virtual Machine running Windows Server 2008, and build out a functional MDT 2010 system. All the files downloaded are publicly avaiable over the internet.

This sample is *not* designed for production environments. The Server OS and Windows 7 Client OS’es downloaded are time-limited Evaluation versions of Microsoft OS’es, and not intended for production environments. If you would like to setup a functional system for your corporate environment, please let us know.

Xtreme Consulting Service’s Deployment Team is ready to assist you with your deployment needs.


When complete, this Buildout (Hydration) system will create:

  • A Virtual Machine running Windows Server 2008 (R1) x86 Server Standard.
  • MDT 2010, with all dependencies installed (including WAIK).
    • OS’es: Server 2008 R1 x86, Windows 7 x86 and Windows 7 x64.
    • Microsoft Office 2007 Enterprise Evaluataion Version.
    • Applications: 7-zip, Foxit PDF Reader, MacroMedia player, Silverlight player, SQL Server 2008 Express, Sun Java Runtime, Video Lan Player, Windows Debugger (windbg).
    • Sample Driver package: Intel Chipset drivers.
    • Sample Packages: Windows 7 Language Packs, Hyper-V Drivers, Remote Server Administration Tools (RSAT)
  • A Windows 7 Media build DVD image with Office 2007.
  • It will also enable a PXE/WDS Service for servicing MDT 2010 WinPE Images (no Domain Controller required).
  • A blank Virtual Machine ready to perform a capture of Windows 7 back to the MDT 2010 server.


  • Your machine must be running windows with a version of either Hyper-V (for Windows Server 2008) or Virtual PC 7 (for Windows 7).
  • Your machine must have at least 2GB of ram, and 60GB of free hard disk space.


Download the MDTBuildout.zip file to your local machine and expand the contents to their own folder. You should see the manual: MDTBuildout.rtf and the Wizard Wrapper MDTBuildout.exe. Go ahead and launch the MDTBuildout.exe.

The MDTBuildout.exe wizard will guide you through the process of creating the virtual machine and downloading the necessary files to get it started. The wizard will create to Virtual Machines:

MDTVirtualServer – This is the Virtual Server that will host MDT 2010. It takes a couple of hours to setup, depending on the speed of your network connection.


MDTVirtualClient – This Virtual Machine will be used later on to capture a reference image of Windows 7 with Office 2007.


It is recommended that you use the default settings during the MDTBuildout.exe wizard, although, you can override the settings if required. Be sure to give the Virtual Machines enough memory, place the *.VHD files on a disk with 40+GB of space free, and select the Virtual Networking adapter with internet access.

When you select “Build” the wizard will begin the process of downloading the Windows Server 2008 Eval *.iso file to the local machine, this file is about 1.8GB. When done, the wizard will automatically create the virtual machines with the required parameters, and start the process. You can continue to watch the progress in the VM window.

The entire build process can take several hours depending on the speed of your network connection. Let the install run. When complete, you should see a dialog showing the status.

The password for the MDT Server is: P@ssword

When complete, you can start the MDT 2010 Workbench (start –> Microsoft Deployment Toolkit –> Deployment Workbench), and view the components added to the system.


The deployment share should be at: c:\deploymentshare

The media share should be at: c:\media

If you wish, you can start the PXE server, to allow other computers on the network to boot into MDT 2010, this is a great way to upgrade machines on your corporate network in a “self-service” fashion. To enable the pxe server, run the command “net start wdsserver”. WARNING: The PXE/WDS Server running on the Virtual Machine *will* conflict with other PXE/WDS servers on the network. Please ensure that you are the *only* PXE/WDS server on the network before starting! This is why the WDS service is disabled by default.

You can also burn the contents of the Win7 Media ISO to a DVD drive. Or perhaps copy the *.iso file to the local machine.

Use this server to evaluate MDT 2010 and try deploying Windows 7 (Evaluation Version), to your test lab.

When ready, you can boot the MDTVirtualClient Virtual Machine, using either the Win7 Media ISO image, or PXE/WDS. The Client Virtual Machine has been specially configured to auto install Windows 7 and upload the captured image back to the MDT 2010 Server in a completely hands off fashion.

More documentation, walkthroughs, and videos to follow.


<Edit> No longer available.


Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Example application exported by MDTAppExport November 6, 2009

Posted by keithga in MDT 2010, PowerShell.
add a comment

Wanted to include at least one example of what a MDTAppExport.exe powershell script looks like.

So for your review, I have included a sample for 7-zip.



About 7-zip

7-zip is a popular file archiver that supports numerous file formats, and is available for free over the internet.

What also makes 7-zip a great example, is that you can download the *.msi package directly over the internet.

The command line MSI installer, msiexec.exe, supports the ability to install packages over http://, so there is no need to download the package first. Note that this could cause problems if you try to install 7-zip on a machine that does not have full internet access.

Typically, in real world MDT Deployment Shares, I would download the packages first, but this makes for a fine example.

Persistent Drives

For MDT 2010, the concept of the “Deployment Share” ( Distribution Share, or Distribution Point) has been abstracted even further than that in MDT 2008. A ‘”Deployment Share” can be a Network Share, a Local folder, and it doesn’t even have to be on the same machine as the MDT 2010 Workbench Console is currently running on.

It’s also possible to keep track of multiple “Deployment Shares” within a running MDT 2010 Workbench console. The “Deployment Shares” that are added to the MDT 2010 Workbench Console are called “Persistent-Drives” because they are persisted across Workbench sessions.

Behind the scenes, you can see the settings by looking at the configuration file:

“%appdata%\Microsoft\Microsoft Deployment Toolkit\user.config”

Which Drive to use

I wanted to develop a script that you could run as-is and have the contents instantly added to your MDT Deployment Share.

However, since a machine can have multiple Deployment Shares, or none at all, the question became:

How do we find the default MDT Deployment Share, if any exists?

The best way I figured was to use the Persistent Drives defined in the MDT 2010 Workbench. The Persistent Drive meta-data could be easily read using the powershell cmdlet: “Get-MDTPersistentDrve”.

If more than one drive was defined in the MDT 2010 Workbench, then we just select the first one. Typically defined as “DS001:”


With all powershell scripts we start off with a list of parameters:

#  MDT 2010 Exported Application
#  By Keith S. Garner (KeithGa.com)
#  7-Zip File Archiver 4.65   
 [string]$Folder = "Applications\Common", 
 [string]$DPDrive = "DS001",
 [string]$DPShare = "\\$env:ComputerName\DS$",
 [switch] $Verbose,
 [switch] $WhatIf )

Here the Administrator is given the ability to override several defaults:

  • Folder – This is the folder where the Application will be placed in the workbench, and displayed in the Deployment Wizard.
  • DPDrive – This is the name of the Deployment Drive used in Powershell. Unless defined as something non-standard by the Administrator, it should be DS001:
  • DPShare – This is the UNC path to the Deployment Share, typically a Networking Share. It’s only needed if there are no persistent drives defined.
  • Confirm, Verbose, WhatIf – For more information look here.


Once started, the script will need to load the MDT 2010 environment into powershell, and restore everything:

if (!(test-path "$($DPDrive)`:\"))
 Add-PSSnapin Microsoft.BDD.PSSnapIn […]
 restore-MDTPErsistentDrive […]    
 if ((!(test-path "$($DPDrive)`:\"))
     -band (test-path $DPShare)) {
   New-PSDrive -Name $DPDrive -PSProvider `
       MDTProvider -Root $DPShare […]
 if (!(test-path "$($DPDrive)`:\")) {
   throw "$DPDrive not Mounted/Persisted."

First of all, we don’t do anything if the DPDrive is already visible to Powershell. This means that a calling “wrapper” script can perform the restoration of the Persistent drives once, and we won’t need to perform it each time for every application being imported.

Otherwise, we add the Powershell Snapin, and restore any MDT 2010 Persistent Drive(s).

We then test for DPDrive, if there is still nothing there, then we mount a new DPDrive to the Deployment Share given.

We test for DPDrive again, if there is *still* nothing there, then we fail.


If we are importing the application into an Application Folder that does not exist, we must first create the folder.

foreach($NewPath in "$($DPDrive)`:\$Folder")
  if(!(test-path $NewPath))
    new-item -type directory $NewPath –[…]

Import Application

Finally, we import the application using the settings gleamed from the application meta-data in MDT 2010.

$7zipx86 = import-MDTApplication
-path '$($DPDrive)`:\$Folder'
-ShortName '7zipx86' […] 
-Name '7-Zip File Archiver 4.65 – x86'
-CommandLine 'msiexec.exe /qb- […] /i http://[…]'
-UninstallKey '{23170F69-40C1-2701-0462-000001000000}'
-enable 'True' -hide 'True'
-SupportedPlatform @(
   'All x86 Pre-Vista',
   'All x86 Vista and Newer'

More to come

I hope this will be the first of many Application Examples from MDT 2010.


Keith Garner is a Deployment Specialist with Xtreme Consulting Group