jump to navigation

Thanks! August 25, 2013

Posted by keithga in Announcements.
comments closed

UPDATE:

Time passes, and most of the members of the Xtreme Deployment Consulting Team have moved on.

We can be reached at the following:

Tim Mintner – http://www.linkedin.com/pub/tim-mintner/2/740/6a1

Keith Garner – http://www.linkedin.com/pub/keith-garner/24/719/474

Dave Field – http://www.linkedin.com/in/davefield

Polly Reese – http://www.linkedin.com/pub/polly-reese/1/441/a84

Micah Rowland – http://www.linkedin.com/in/micahjrowland

Thanks to our clients and friends.

It’s GUID To See You CMD er…. August 28, 2012

Posted by Micah Rowland in Uncategorized.
1 comment so far

It’s been far too long as always. New projects, new pits, but every now and then you realize how much you take some things for granted have to improvise from scratch.

I was tasked with modifying an old cmd script to add an event log entry at the beginning of the scripts run and an event log entry when it completed. To avoid cluttering the Application event log or one of the other default event logs that I lovingly refer to as event junk drawers, I created my own log with the New-EventLog cmdlet in PowerShell. After that, I added calls at the beginning and end of the cmd script that ran powershell.exe using the -Command argument and passed a short block of powershell commands.

What I did not forsee was the possibility of the cmd script running concurrently with an already running instance of the script, thus jumbling the logs and making it impossible to match a start log with its correct end log entry. I needed some way to uniquely identify each set of logs. That’s when I came up with my guid idea! (get it? good idea = guid idea… forgive the pun, I promise it will be the last)

That’s right, if I generated a guid in the cmd script and stamped that same guid in the start and end event log entries, I could match them up later. If this was a pure PowerShell script, I could simply call the [System.GUID]::NewGUID() .Net function and walk away. Unfortunately, cmd scripting leaves a little to be desired in terms of flexibility, so I broke out my old command scripting references, fired up Bing and went to work.

A GUID is a random string made up of 32 hexadecimal digits grouped in a {8-4-4-4-12} format. My first step was to get a random number in a cmd script. Enter the %random% variable. When evaluated by the command interpreter, %random% returns a (pseudo)random number between 0 and 32768. Applying a modular operator gave me my pseudo-random hexadecimal digit in decimal format.

set /a tempdec=16*%random%/32768
echo %tempdec%

Pass that decimal number through a set of cmd if statements and out pops a proper pseudo-random hexadecimal digit. I present the hex-o-matic pseudo-random number generator:

set /a tempdec=16*%random%/32768
if %tempdec% equ 0 (set digit=0)
if %tempdec% equ 1 (set digit=1)
if %tempdec% equ 2 (set digit=2)
if %tempdec% equ 3 (set digit=3)
if %tempdec% equ 4 (set digit=4)
if %tempdec% equ 5 (set digit=5)
if %tempdec% equ 6 (set digit=6)
if %tempdec% equ 7 (set digit=7)
if %tempdec% equ 8 (set digit=8)
if %tempdec% equ 9 (set digit=9)
if %tempdec% equ 10 (set digit=A)
if %tempdec% equ 11 (set digit=B)
if %tempdec% equ 12 (set digit=C)
if %tempdec% equ 13 (set digit=D)
if %tempdec% equ 14 (set digit=E)
if %tempdec% equ 15 (set digit=F)
echo %digit%

Now I just need 31 more. My first instinct was to drop my pseudo-random hex-o-matic code inside of the cmd script version of a iterative for loop

FOR /L %a (1,1,32) do ACTION

and call it a day. Unfortunately, that was too easy. %random% uses the current time as its seed, and when the FOR command is run, it gets the time at launch and provides that time value to the code in its care every time it is requested. That means that %random% uses the same time value and generates the same pseudo-random number every time it is called in the FOR command.

There was only one thing to be done, 32 successive hex-o-matic blocks stacked end to end, each adding one hexadecimal digit to the guid.

set /a tempdec=16*%random%/32768
if %tempdec% equ 0 (set guid=0)
if %tempdec% equ 1 (set guid=1)
if %tempdec% equ 2 (set guid=2)
if %tempdec% equ 3 (set guid=3)
if %tempdec% equ 4 (set guid=4)
if %tempdec% equ 5 (set guid=5)
if %tempdec% equ 6 (set guid=6)
if %tempdec% equ 7 (set guid=7)
if %tempdec% equ 8 (set guid=8)
if %tempdec% equ 9 (set guid=9)
if %tempdec% equ 10 (set guid=A)
if %tempdec% equ 11 (set guid=B)
if %tempdec% equ 12 (set guid=C)
if %tempdec% equ 13 (set guid=D)
if %tempdec% equ 14 (set guid=E)
if %tempdec% equ 15 (set guid=F)

set /a tempdec=16*%random%/32768
if %tempdec% equ 0 (set guid=%guid%0)
if %tempdec% equ 1 (set guid=%guid%1)
if %tempdec% equ 2 (set guid=%guid%2)
if %tempdec% equ 3 (set guid=%guid%3)
if %tempdec% equ 4 (set guid=%guid%4)
if %tempdec% equ 5 (set guid=%guid%5)
if %tempdec% equ 6 (set guid=%guid%6)
if %tempdec% equ 7 (set guid=%guid%7)
if %tempdec% equ 8 (set guid=%guid%8)
if %tempdec% equ 9 (set guid=%guid%9)
if %tempdec% equ 10 (set guid=%guid%A)
if %tempdec% equ 11 (set guid=%guid%B)
if %tempdec% equ 12 (set guid=%guid%C)
if %tempdec% equ 13 (set guid=%guid%D)
if %tempdec% equ 14 (set guid=%guid%E)
if %tempdec% equ 15 (set guid=%guid%F)

... (30 more of the above block)

Now I had a wonderful string of 32 hex digits in a row. Enter the cmd script substring syntax: %VARIABLE:~[STARTINDEX],[NUMBEROFCHARS]%

So we take our %GUID% string and…

set GUIDF={%GUID:~0,8%-%GUID:~8,4%-%GUID:~12,4%-%GUID:~16,4%-%GUID:~20,12%}

VOILA! Out pops

{329674F9-97A7-CC6C-A0DA-DB21ED8EA0D3}

 

So the next time you whine about how PowerShell should include a Get-GUID cmdlet, just remember, it used to take 577 lines of cmd script to accomplish what $GUID = [System.GUID]::NewGUID() does in one. Ain’t the future grand?

Deploying Silverlight Applications (XAP) February 17, 2012

Posted by Micah Rowland in Uncategorized.
add a comment

It has been far too long since I last wrote and I thought I’d address a new type of application to deploy: Out-of-Browser Silverlight applications.

The Quest

A customer recently decided to spruce up its tablet/slate fleet by including some simple third-party Silverlight applications. These applications are normally downloaded and installed using a web browser. So the question was raised, “Can we install this programatically as part of the imaging process?” My interest was piqued and I embarked on my foray into Silverlight deployment.

We’ll use the Fandango Windows Slate located (for now) at http://images.fandango.com/mobile/slate/ as our example.

Upon navigating to the above web address, the site checks to see if the application has already been installed. If not, the user is prompted to Tap to Install, a simple feat to accomplish if you have fingers.  After tapping the prompt, a security warning dialog box appears and after a short countdown allows the installation by clicking/tapping the Install button.

Breaking out Sysinternals Process Monitor and filtering the data reveals the Silverlight.Configuration.exe, a prime suspect. A quick right-click Properties action and a tap on the Process tab gives us a place to start. The command-line being executed is:

“C:\Program Files (x86)\Microsoft Silverlight\4.0.60831.0\Silverlight.Configuration.exe” -installtrustedApp 3835182041.images.fandango.com 231aa8

The next step was to see if this command alone would install the application. Uninstall, cmd, paste, enter. No error message… but no program in the start menu and nothing in Programs and Features. A dead end? What could I be missing?

Time to break out Sysinternals Process Explorer. Let’s watch the install happen real-time and see what other related parent and/or child processes may be involved. Processes launch and close relatively quickly in Process Explorer, so to give myself the extra time I may need, I accessed the Difference Highlighting Duration dialog from the Options menu. Unfortunately this value is limited from 0-9 so 9 will have to work. Uninstall, then, side by side, reinstall the app and watch.

Internet Explore launches a Silverlight.Configuration.exe child process, which launches a second Silverlight.Configuration.exe child process, which launches a sllauncher.exe child proces, which launches a second sllauncher.exe child process. A new suspect! Inspecting the properties of the child process reveals this command-line:

“C:\Program Files (x86)\Microsoft Silverlight\sllauncher.exe” 3835182041.images.fandango.com

Very similar to the above Silverlight.Configuration.exe command-line. Let’s search for this sllauncher.exe and see if it has some command line arguments we can use. A quick search reveals the perfect article: How to Install a Silverlight Out-of-Browser Applications (XAP) Silently

JACKPOT? A quick read reveals a sample scenario involving deploying your own in-house developed XAP… Not quite what we have in mind but it’s a great start. The sample command-line they use is:

“C:\Program Files\Microsoft Silverlight\sllauncher.exe” /install:”C:\MySilverlightApps\Silverlight4.OOB.ChromelessWindow.Demo.xap” /origin:http://www.kunal-chowdhury.com/private/apps/ClientBin/Silverlight4.OOB.ChromelessWindow.Demo.xap /shortcut:desktop+startmenu /overwrite

Not a bad start, but not perfect for our needs. We don’t have the xap file that the website is using nor do we know the origin of the file. Now what? Let’s pop back open Sysinternals Process Monitor and take a look at the file changes during install, maybe we can find a locally stored copy?  Pause and clear the capture, set the filters back to default, add a filter to include only sllauncher.exe processes, uninstall the app, start capture, install the app. Now filter only file operations and… nothing. Let’s try Silverlight.Configuration.exe… nope. Let’s try path contains xap… BINGO! An application.xap file in the folder 3835182041.images.fandango.com. We’ve seen that path before. The full path to that xap is:

C:\Users\Micah\AppData\Local\Microsoft\Silverlight\OutOfBrowser\3835182041.images.fandango.com

Ok, what have we got? Let’s see, we have an icon, a png of the icon, the application.xap file, an index.html file and 3 un-extensioned files: installstate, metadata, and state. Well it’s a good bet that the application.xap file is the source we need… what about the origin parameter? The installstate and state files are empty (probably flag files), but the metadata is the jackpot we’ve been waiting for:

ShortcutName=Fandango Slate Application
LaunchPath=C:\Users\Micah\AppData\Local\Microsoft\Silverlight\OutOfBrowser\3835182041.images.fandango.com\index.html
CustomIcon=1
TrimmedSourceDomain=images.fandango.com
TrimmedTitle=Fandango Slate Application
TrimmedName=Fandango Slate Application
ElevatedPermissions=2147483647
XapLastModified=Tue, 02 Aug 2011 23:16:17 GMT
EnableGPUAcceleration=True
WindowStartupLocation=0
WindowTop=0
WindowLeft=0
WindowWidth=1367
WindowStyle=1
WindowHeight=767
SourceDomain=images.fandango.com
OriginalSourceUri=http://images.fandango.com/mobile/slate/ClientBin/Fandango.Slate.xap
FinalAppUri=http://images.fandango.com/mobile/slate/ClientBin/Fandango.Slate.xap
RuntimeVersion=4.0.50826.0
AppID=3835182041.images.fandango.com
Description=Fandango Slate Application on your desktop; at home, at work or on the go.
Title=Fandango Slate Application
Name=Fandango Slate Application

OriginalSourceUri looks PERFECT. Just to make sure we really have the original XAP file, let’s download that XAP, maybe the xap file changes when downloaded and installed (ok maybe not, but we can’t be too safe). Copy pasting that address into IE gives us a downloaded copy!

As far as the remaining two parameters, shortcut and overwrite: The overwrite parameter is not needed for MDT deployments or if you are sure the app has never been deployed before. Adding overwrite prevents an error from being thrown if the application already is installed. The shortcut parameter can be left out or set to one of the following:

  1. desktop
  2. startmenu
  3. desktop+startmenu

Let’s roll it all together: Place the xap file in a folder and our local command-line is as follows:

“C:\Program Files (x86)\Microsoft Silverlight\sllauncher.exe” /install:“Fandango.Slate.xap” /origin:http://images.fandango.com/mobile/slate/ClientBin/Fandango.Slate.xap /shortcut:startmenu /overwrite

Caveats

Silverlight 5 was released on December 9, 2011 and has a 64-bit installer. This means that if you are installing Silverlight 5, the path to sllauncher will always be C:\Program Files, regardless of the OS architecture, making our job simpler, one command-line for all🙂

These applications are installed on a per-user basis, so for MDT deployment, make sure copyprofile=true.

Some Out-of-Browser Silverlight apps may require Native Extensions For Microsoft Silverlight which you can find here as an msi download. Install the extensions before deploying the apps to avoid dependency issues.

Summary

So, to summarize:

  1. On a test machine, download and install the desired Silverlight Out-of-Browser application
  2. Navigate to %LOCALAPPDATA%\Microsoft\Silverlight\OutOfBrowser and open the appropriate application folder.
  3. Open the metadata file in notepad and copy the OriginalSourceUri path.
  4. Paste the path the address bar of your browser and download the XAP file.
  5. If using MDT, copy the XAP to a folder and import.
  6. Use the command-line syntax as follows:
    • 64-bit OS: “%ProgramFiles(x86)%\Microsoft Silverlight\sllauncher.exe” /install:[XAP FILE] /origin:[ORIGINALSOURCEURI PATH] /shortcut:[DESIRED SETTING] /overwrite
    • 32-bit OS: “%ProgramFiles%\Microsoft Silverlight\sllauncher.exe” /install:[XAP FILE] /origin:[ORIGINALSOURCEURI PATH] /shortcut:[DESIRED SETTING] /overwrite

Credit: I ran across a great article on Tim Heuer’s blog while proofing this draft that I failed to locate when it was originally started and would be remiss not to mention. It’s a great echo of what I’ve covered here but doesn’t cover third-party apps.

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.

param(
    [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})]
        [Int16]$Setting
    )

$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)
    switch($tempString.Length)
    {
        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
    }
    else
    {
        return $byteToRot
    }
}

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

    }
    Else
    {
        $bytAppPath += 0
    }
}

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

[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)
        switch($tempString.Length)
        {
            0 {$strItem += "00"}
            1 {$strItem += "0" + $tempString}
            2 {$strItem += $tempString}
        }
    }
    if($strItem.Contains($strAppPath))
    {
        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!

New Tool: Advanced Format Drives April 29, 2011

Posted by keithga in Troubleshooting, Uncategorized, Windows 7.
3 comments

Been slacking lately (Working on MDT 2012).

Advanced format drives are coming!

http://en.wikipedia.org/wiki/Advanced_Format

This hasn’t been much of a problem until recently, now that most of the major hard drive manufacturers have started to switch over to full Advanced Format.

When you have an Advanced format drive, you should format with an operating system that can format the drive aligned on the advanced 4k sectors rather than the 512 byte sectors found on older drives.

If  you do not format an “Advanced Format” disk aligned on the 4k sectors, then you may experience performance degradation. Windows 7 SP1 has been updated to address Advanced Format Drives, and WinPE 3.1, contained in WAIK 3.1 has been updated as well.

But how do you know if you need WinPE 3.1?

I have just written a new tool: IsAdvancedFormat.exe, that can detect if there are *any* advanced format drives present on the local machine. It will return errorlevel 0 if there are no advanced format drives, and errorlevel 42 if there *are* advanced format drives.

Source code is included.

Sample Output!

C:\> IsAdvancedFormat.exe
Enumerate all detect all Advanced Format Drives.
Enumerator: IDE Found: Maxtor 6L160M0
  Physical sector size is 512 bytes.
Enumerator: IDE Found: Maxtor 6L160M0
  Physical sector size is 512 bytes.
Enumerator: IDE Found: WDC WD10EADS-11M2B2
  Emulated sector size is 512 bytes.
  Physical sector size is 4096 bytes.
C:\> @echo %ErrorLevel%
42

Location!

http://cid-5407b03614346a99.office.live.com/self.aspx/Blog/IsAdvancedFormat.zip

Update! (7/24/2011)

It appears that Dell has taken this tool and enhanced it! Their new tool can detect if an Advanced Format drive was partitioned/formatted incorrectly.  Cool Stuff.

Since Dell’s tool is now a superset of my tool, I would recomend theirs first:  http://del.ly/afhdd 

I will continue to provide my tool here as a reference.

Thanks Dell! (and thanks Warren Byle)

Duplicate Driver Tool. January 10, 2011

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

Overview

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.

Syntax

…\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 7.0.0.1011</Name>
<…>
   <Hash>0604A3D569DCBA7CC0EFEAE6ADB0D7A69DC40D2A</Hash>
   <Version>7.0.0.1011</Version>
   <Date>09/15/2006</Date>
   <WHQLSigned>True</WHQLSigned>
   <PNPId>*PNP0103</PNPId>
   <PNPId>PCI\VEN_8086&amp;DEV_244E</PNPId>
   <PNPId>PCI\VEN_8086&amp;DEV_2448</PNPId>
</driver>

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.

Future

  • 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.

Location

http://cid-5407b03614346a99.office.live.com/self.aspx/Blog/DupeDriverTool.zip

Keith

Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Combining WIM files December 16, 2010

Posted by keithga in Uncategorized.
7 comments

I was at a customer site during the past few weeks, and the customer asked about merging WIM files. This company was a Lenovo shop, and they had three main platforms in their inventory, the Thinkpad T410, T410s, and the X201.

For technical reasons that I won’t get into here, we decided to create several Model Specific Fat images that could be deployed using SCCM as-is without loading additional Drivers. Unfortunately, when done we had about 8 images, including two base images that contained their common application suite, but did not contain any extra drivers.

When done we had the following x64 images:

  • WIN7ENTX64.base.wim
  • WIN7ENTX64.t410.wim
  • WIN7ENTX64.t410s.wim
  • WIN7ENTX64.x201.wim

Each wim was about 4-6 GB in size, all totaled about 21GB for x64.

WIM files

WIM files are containers that store other files, similar to the way that a *.zip file or a *.cab file stores other files. Microsoft Windows also provides the ability to “mount” Wim files into an existing File Folder, allowing one to Add/Delete/Modify files in the wim.  This causes problems since modifications of the WIM file can cause fragmentation, just like *.vhd files. Windows gets around this by supplying the imagex.exe /export command. This command will allow the administrator to move the content from one WIM file to another WIM file, and in doing so the new wim file will be de-fragmented!

We can use this imagex.exe /export command to move the contents of one Wim file *into* an existing wim file, the cool thing for us is that ImageX.exe will also check to see if the file already exists in the wim file, and will not add the file in order to save space!

Example

In the example case above with the three Lenovo machines and a base image, I decided to start with the base image, and to inject the three other platforms into the base *.wim image:

Imagex.exe /export WIN7ENTX64.t410.wim *   WIN7ENTX64.base.wim “WIN7ENTX64.DDrive.t410”

Imagex.exe /export WIN7ENTX64.t410s.wim * WIN7ENTX64.base.wim “WIN7ENTX64.DDrive.t410s”

Imagex.exe /export WIN7ENTX64.x201.wim * WIN7ENTX64.base.wim “WIN7ENTX64.DDrive.x201”

When I was finished the Base.wim file had increased in size by only 1.5GB, much better than trying to distribute 4 separate *.wim files totalling 20GB

-Keith

Keith Garner is a Deployment Specialist with Xtreme Consulting Group

MDT Litetouch startup -Super Flow- help; November 11, 2010

Posted by keithga in MDT 2010, Troubleshooting, Windows 7.
3 comments

Someone posted a question on a MDT forum recently:

I´m courious about what is happening when you boot your client on the WinPE iso image that is generated when you update the deploymentshare in MDT.

So, does a “superflow” or other documentation like that exist ?

Interesting question, thought I’d write down some of the basics (Things that are interesting to people in the deployment field):

  •  The Computer starts up and the BIOS is responsible for selecting which device to boot from. This can be either:
    Network (PXE), Hard Disk, USB Flash Drive, or CD-ROM (El-Torito)
  • When booting off a hard disk or USB Flash Drive, the BIOS will look for an “Active” partition, and run the code in the Master Boot Record.
  • PXE Booting is a different topic…  :^)
  • When booting from the CD-ROM, the BIOS will kick off the EtfsBoot.com program running in El-Torito No-emulation mode. ETFSBoot.com does two things:
    • Runs the BootFix.bin program – This program will test for the existence of any “Active” hard disk partitions on the machine, if found, it will prompt the user to “Press Any Key to boot from CD or DVD.” If no “Active” partition is found, or the user presses a key, then the next “BootMgr” program is run.
    • Bootmgr – ( In windows XP it was ntldr) Is responsible for management of the other Operating Systems, it reads the \boot\bcd file (a registry hive), and displays a menu to the user. It can launch Windows, WinPE, other Real Mode programs, and even boot Windows from VHD files (for premium versions of windows like Windows Ultimate or Enterprise).
  • Once the OS has been selected, BootMgr then starts the process of loading all the OS components into memory, when ready it will pass control from Real Mode into the Windows Kernel. The OS will then take over the boot up process and continue loading the rest of the drivers and components. If at this point it can’t find the hard disk from where it came from, it will Stop BugCheckEx(), with error code 0x0000007B, typically this means the Storage Driver wasn’t loaded.
  • In WinPE the process continues by calling WinPEShl.exe. If WinPEShl.exe finds the file WinPEShl.ini, it will parse it. For MDT 2010, the WinPEShl.ini file looks like:
[LaunchApps]
%SYSTEMROOT%\System32\bddrun.exe,/bootstrap
  • BddRun.exe – Will launch wpeinit.exe. It will also remain in memory and monitor the keyboard for F8 (See: http://deployment.xtremeconsulting.com/2009/10/29/78/ )
  • WpeInit.exe will start up the network, and other services and parse the unattend.xml file for commands to run. For MDT 2010, the Unattend.xml file looks like:
<?xml version="1.0" encoding="utf-8"?>
…
<Path>wscript.exe X:\Deploy\Scripts\LiteTouch.wsf</Path>
  • Litetouch.wsf will then check to see if there are any Task Sequences in progress (is there a c:\minint and/or c:\_SMSTaskSequence\TSEnv.dat file present), and if so execute.
  • If Litetouch can’t find any in-progress Task Sequences (NewComputer), it will:
    • Parse the Bootstrap.ini file
    • Display the Welcome Wizard (unless SkipBDDWelcome is defined)
    • Parse the CustomSettings.ini file (Typically from the DeployRoot found in the Bootstrap.ini file).
    • Display the Deployment Wizard (Unless SkipWIzard is defined)
    • Run the full Task Sequence…

<whew>

New Tool: ZTIAppVerify.wsf – Logs the status of all installed applications. November 8, 2010

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

Someone posted this question on a E-Mail list today:

Subject: Applications log file

Hi all,

I am working on building a LTI solution for Win7. [… is] there […] a simple solution to create a log file at the end of the deployment phase. This log file must contains a list of all applications installed in the task sequence.

It’s possible?

It got me thinking, and I realized that I *had* created a script to perform this exact same problem early this year, yet never posted it here to my blog.

So without further delay:  Introducing new tool ZTIAppVerify.wsf!

Details:

This script performs two tasks:

  1. 1. It will enumerate through the Applications specified in the Wizard, the CustomSettings.ini, and/or MDT Database. In other words, it will parse the Applications and the MandatoryApplications list properties and attempt to see if the installation was successful.
    How does it determine if the installation was successful?  If you populated the “UninstallKey” when creating your Application in MDT, that Key must then exist in the uninstall registry. For MSI applications, that UnInstallKey is just the Product Key. An Error is generated if the Key is not found (meaning the install was not successful).
  2. The script will also enumerate through all the Uninstall Registry Keys on the local machine:
    HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\…
    This is the list that is populated when you go to the Control Panel to remove an application (and a lot more). Note that output will contain the “UninstalKey” for use later on.

Just place this script in your MDT 2010 Task Sequence, somewhere *after* the ZTIApplication.wsf script(s) are run.

Sample Output:

For example, in the 2nd case listed above, the script will display a list of installed programs, the “UninstallKey”, and a friendly name for the application:

…
INSTALLED:   {23170F69-40C1-2702-0465-000001000000} \ 7-Zip 4.65 (x64 edition)
INSTALLED:   {25097770-2B1F-49F6-AB9D-1C708B96262A} \ System Center Operations Manager 2007 R2 Agent
INSTALLED:   {26A24AE4-039D-4CA4-87B4-2F86416017FF} \ Java(TM) 6 Update 17 (64-bit)
INSTALLED:   {29C93182-34F6-3275-A18D-59326851CD57} \ Microsoft Windows SDK for Visual Studio 2008 .NET Framework Tools - enu
INSTALLED:   {2F14965D-567B-4E59-ADEB-0A2CC1E3ADDF} \ Sql Server Customer Experience Improvement Program
INSTALLED:   {31E8F586-4EF7-4500-844D-BA8756474FF1} \ Windows Automated Installation Kit
INSTALLED:   {347F1DAD-AFF5-4F68-84F5-69AEB3EE1D24} \ Microsoft Deployment Toolkit 2010 Update 1 (5.1.1642.01)
…

Link:

http://cid-5407b03614346a99.office.live.com/self.aspx/Blog/ZTIAppVerify.zip

New Tool: USB Boot Tool October 28, 2010

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

Overview:

The purpose of the tool is to add/remove WinPE Boot.wim file(s) to a USB Flash Drive using a wizard. 

It is designed to integrate with the Microsoft Deployment Toolkit 2010.

Description:

It should be smart enough to find USB flash drives, find any local  MDT 2010 Litetouch.wim files, automatically mark the drive/partition active (if not already set), and it can add/remove multiple *.wim files to a single USB Flash drive if there is enough space.

This is ideal if you want multiple Litetouch WIMs, For example x86 *and* x64 litetouch.wim files on the same USB stick, or Litetouch WIMs from multiple Deployment shares (One production server, one test server)..

USBootTool.hta is a standalone *.hta file, and requires no other components/libraries.

Installation/Operation:

· Just copy this script to your %deploymentshare%\Boot\ directory.

· When the script starts up it will display the Litetouch.wim files present on that directory. If not present it will enumerate through the Deployment shares mounted in the MDT console.

Screen Shots:

clip_image001

· Note the tool found my Flash Drive, parsed the BCD file and found three entries.

· I click “add” to add a *.wim file.

clip_image002

· Note that the tool found several *.wim files in my deployment share.

· I can modify the description if required.

clip_image003

· The script will copy all the necessary files to the Flash Drive.

· It will also place the *.wim file in a separate folder. The folder name is a GUID to prevent conflicts.

· I can repeat the process to add other *.wim files to my USB flash drive

 Source

http://cid-5407b03614346a99.office.live.com/self.aspx/Blog/USBBootTool.zip

-k