UserTile Automation June 23, 2010
Posted by Micah Rowland in USMT, VBscript, Windows 7.4 comments
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.
- 12-bytes (seem to be constant at 01 00 00 00 03 00 00 00 01 00 00 00)
- 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:
- 4-byte field (purpose unknown, possibly the length of the following field)
- A null-terminated Unicode string representing the file type
- Eg. “bmp” = 62 00 6D 00 70 00 00 00
- 4-byte field (purpose unknown, always 02 00 00 00)
- 4-byte field representing the payload (bitmap) size in bytes
- 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.
Solution:
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 [HKEY_CURRENT_USER\Software\Sysinternals\PsExec] "EulaAccepted"=dword:00000001 |
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 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' CONST FORREADING = 1 CONST FORWRITING = 2 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" wscript.quit() 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 objRegFile.Close 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" wscript.sleep(500) objShell.run "CMD /C type %temp%\userindexes.reg > %temp%\userindexes.txt" wscript.sleep(500) 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 loop if objRegExport.AtEndOfStream then wscript.echo "Username not found." wscript.quit() else 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 loop 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." else 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.3 comments
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!
Xtreme Consulting at MMS 2010! April 8, 2010
Posted by keithga in Announcements, MDT 2010, Speaking Engagements.comments closed
Xtreme Consulting Group is going to have a great presence at the Microsoft Management Summit 2010 in Las Vegas Nevada on April 19th through April 23rd.
We have Speakers, Consultants, and Technical Professionals there to assist with Hands on Labs (Fun stuff).
Microsoft Deployment Toolkit Developer/Gurus Tim Mintner and Keith Garner will be there too. Get the inner workings of MDT 2010, and find out what’s going to be in MDT 2010 Update 1!
See you there!
Two “Birds-of-a-Feather” sessions you can count on us to be at:
OE01 Ask the Experts: Microsoft Deployment Toolkit (MDT) 2010
Tuesday, April 20 5:30 PM – 6:30 PM, Marco Polo 702
Speaker(s): Michael Niehaus
Track(s): Client Management Technologies, Solution Accelerators, Systems Management
Session Type(s): Birds-of-a-Feather
Products(s): Configuration Manager 2007, Microsoft Deployment Toolkit, Windows Client
Here’s your chance to sit down with the team who are building MDT 2010, troubleshooting customer scenarios, blogging about the latest and greatest solutions and planning the next generation of deployment tools. Bring your questions about MDT 2010 to this Birds-of-a-feather session and find out how to apply the latest solutions to your specific scenarios.
OE31 Open Forum Discussion: Microsoft Deployment Toolkit 2010 (MDT) and Configuration Manager 2007 OSD
Tuesday, April 20 6:45 PM – 7:45 PM, Marco Polo 702
Speaker(s): Chris Nackers
Track(s): Client Management Technologies, Solution Accelerators, Systems Management
Session Type(s): Birds-of-a-Feather
Products(s): Configuration Manager 2007, Microsoft Deployment Toolkit, Windows Client
Time to sit down with fellow OSD/MDT users and share your experiences around OSD/MDT in the real world. Come join Chris Nackers and others to discuss lessons learned around MDT and OSD in general in an Open Forum discussion.
Assign Drivers to Computer Makes and Models March 11, 2010
Posted by keithga in Uncategorized.7 comments
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:
PCI\VEN_1099&DEV_0242&SUBSYS_02429005&REV_01 PCI\VEN_1099&DEV_0242&SUBSYS_02429005 PCI\VEN_1099&DEV_0242&CC_010401 PCI\VEN_1099&DEV_0242&CC_0104 PCI\VEN_1099&DEV_0242&REV_01 PCI\VEN_1099&DEV_0242 PCI\VEN_1099&CC_010401 PCI\VEN_1099&CC_0104 PCI\VEN_1099 PCI\CC_010401 PCI\CC_0104
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:
| Common
Audio Networking Intel Marvel Storage Video Dell Common Audio Networking Storage D620 Hewlett-Packard Common DC7800 Audio Networking Storage WinPE |
Then in your CustomSettings.ini file, you can add the following:
DriverGroups001=Common DriverGroups002=%Make%\Common DriverGroups003=%Make%\%Model% DriverSelectionProfile=Nothing
(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
Keith Garner is a Deployment Specialist with Xtreme Consulting GroupKeith 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 GroupBitlocker Recovery Password saved to file January 22, 2010
Posted by keithga in MDT 2010.add a comment
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.
Either:
Comment out lines 722 – 724 in the ZTIBDE.wsf script. (MDT 2010 Only).
or:
Set the variable in your customsettings.ini file to point to a location that is cleaned at the end of the Task Sequence process:
BDEKeyLocation=%SystemDrive%\minint\
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
Keith Garner is a Deployment Specialist with Xtreme Consulting GroupHow MDT does Application Installation January 20, 2010
Posted by keithga in MDT 2010, Troubleshooting, VBscript, Windows 7.8 comments
Been working lately on adding some Application Installation packages to MDT, and I’ve seen some good, bad, and ugly packages.
So, what makes an application installation compatible with MDT (or other scripted installation methods, for that matter)?
Good question.
Microsoft Installer (MSI)
The good!
More and more products are being released lately as MSI packages. Most MSI Packages are easy to automate. For example, I typically use the following parameters:
Msiexec.exe /qb- /l*vx %LogPath%\<file>.log REBOOT=ReallySuppress UILevel=67 ALLUSERS=2 /i <File>.msi
MDT will easily handle this installation script, and install properly for most MSI packages. Yea!
Non-MSI Packages
However, it is possible that you are working with a custom installation package, or perhaps developing your own. What are the rules necessary to make the package work in MDT?
Rule 1: Provide unattended installs
The install program should have a way to install in a unattended manner. Typically this is done by some sort of command line switch to the installer program or script: /quiet /silent /q whatever. MDT 2010 is a fully automated installation system, and the automation will break if there are any user prompts blocking installation.
For example, if you have a MSI installer package, you can call MSIExec.exe with the /q[bn][-] parameter.
This also equally important for errors. If the install package generates errors, it should provide a method to log the errors to a file for analysis later, rather than prompting the user for interaction with a blocking Error Message Box.
Rule 2: Do not exit until done
The install programs should not exit until all setup tasks have finished. If the setup program returns, yet there are still installation tasks being performed in the background, MDT has no way to determine this. So MDT may continue with the next installation, or perhaps a reboot thus causing conflicts with the installation running in the background.
For example, say you have two installation packages, A.Exe and B.Msi. A.Exe is just a Self Extracting Executable that expands A.MSI to the %temp% folder, and kicks off msiexec.exe. However, A.Exe calls msiexec.exe and doesn’t wait, instead A.Exe promptly exits. MDT does not know what is running in the background, and instead continues installing the next package in the list B.Msi. However since A.Msi is running in the backgound, and MSIExec only allows one installation at a time, B.Msi will fail.
Instead A.Exe should wait until Msiexec.exe /i A.msi has finished.
Rule 3: No rebooting
Sometimes an install package will need to reboot to complete the installation. Reboots, for example, are required to update any file that is already open and in use. It’s a common misconception that you need to reboot to install a device driver, you don’t (unless the driver is in use).
However problems arise if the setup program, when running in an unattended matter, decides to reboot on it’s own, without letting the calling script (in this case MDT), know before hand. It may be a couple of seconds before all processes have a chance to shutdown. It’s possible that MDT may try to continue installing the next program in the order or other cleanup tasks, when it shouldn’t.
Instead, a program should return Windows.h Error Code: 3010 ( ERROR_SUCCESS_REBOOT_REQUIRED ).
This will let MDT know that a reboot is required, reboot the machine, and *then* continue with the rest of the installation packages.
Other Notes
There are a few other notes that I wish I could mention to the authors of installation packages:
- Be aware that the installation may be performed under one user account, but the program may be used under another account. When calling MSIExec.exe, I usually call it with the ALLUSERS=2 property.
- Please make it easy to determine what the unattended/silent installation procedures are. It’s not always easy to determine what the command line parameters are.
- If you have a Self Extracting Executable that calls msiexec.exe, please provide a way to pass logging and other properties (see above) to msiexec.exe.
- Speaking of Self Extracting Exe files that call *.msi packages. Please just provide the *.msi install package, it’s much easier.
- On your web site, please provide direct access to your install packages, rather than going through some web logic. Several populat sites, for example will attempt to offer you *only* the x86 or x64 binaries depending on which platform you are running on, even though I may need both for packaging.
- Please keep the desktop free from links/icons, or provide a property in MSI to disable shortcuts creation on the desktop (I’m talking to you Adobe Reader). I like keeping my desktop clean.
- Speaking of options, please provide ways on the command line to enable/disable most common features.
Keith
Keith Garner is a Deployment Specialist with Xtreme Consulting GroupGreat 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
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=dfafb346-97dd-4fca-947e-3d9149834da6
It’s a good introductory reference to MDT, SCCM, and Deployment in general.
Keith
Keith Garner is a Deployment Specialist with Xtreme Consulting GroupThis week in OS Deployment January 15, 2010
Posted by tmintner in Announcements, MDT 2010.Tags: MDT
1 comment so far
Hey Everyone!
There has been several items posted in the OS Deployment community this week that you should definitely know about:
New KB Article published on USMT and MDT 2010. This will fix issues when migrating items such as Operating System specific settings like network shares, printers, and EFS certificates. This is a must have fix for MDT 2010!
The Deployment Guys also posted two great stories this week:
802.1x Security for Windows PE! This has been a much requested feature to add 802.1x security to Windows PE.
They also posted a great article on how to migrate time zone settings from an existing computer during an installation:
Enjoy and have a great week!
Water Water everywhere… January 7, 2010
Posted by keithga in Uncategorized.comments closed
I was walking in through the Lobby of Microsoft building 18 yesterday, and someone came in to tell the receptionist that there was a burst water main leaking in the building. Unfortunately, this is a normal occurrence in Microsoft Building 18. And has happened every year since 2007, and typically localized arround the Microsoft Deployment Toolkit team members.
Luckily, this year, we found most Team Members, and we were disconnecting machines from power before the flood waters came. And were also able to get critical machines out before the moving crews came to cart the equipment out to an undisclosed location during repairs.
For example, here is what the office of Mike Niehaus looked like yesterday.
His entire office is now completely bare, and cleaning crews are doing their thing.
It’s been a crazy start to 2010! Hopefully things will calm down and I’ll start posting more soon.
Keith
Keith Garner is a Deployment Specialist with Xtreme Consulting Group