Programmatically Set Windows 7 User Account Picture
So in a previous pair of posts, I provided a VBScript which would allow you to standardize your corporate email presence with a corporate Outlook signature script. On the same principal in this post, I’m going to cover programmatically setting your WindowsÂ 7 User Account Picture.
Whilst it may not sound important or useful, you can be certain that almost every user in your business has gone to the lengths of setting one of the many default Windows 7 pictures for their user account such as the robot or the dog, but does that really say professional if they were to connect their laptop to a projector in-front of a room of partners, suppliers or customers or would it be more professional for their company issued picture to be shown, which is already shown throughout the business?
If you are using SharePoint or Lync, chances are you will have invested time in getting user pictures imported into Active Directory thumbnailPhoto attribute which in turn allows you to see these pictures in Outlook 2010 and the Exchange Global Address List (if not, you’re either not using pictures at all, or you’re allowing your users to set their own pictures – BYOP culture). Failing that, you could have staff door cards which double and photo ID cards and you’re using AD as your repository for the images (and if not, you should do).
Sadly, Microsoft went to the lengths of adding the concept of user account pictures in Windows Vista, but even in Windows 7 provided no Group Policy method for programmatically setting this to their corporate photo, so we’re left to the hands of the community. Thankfully one fellow found that hidden in the Shell32.dll functions was an undocumented function for just this: http://joco.name/2010/12/06/i-discovered-the-new-windows-user-tile-api/, then another community fellow at http://msitpros.com/?p=1036Â wrote a PowerShell script to take the work from the first site and script it.
Problem is that his script just doesn’t work and doesn’t handle users with no thumbnailPhoto attribute set, causing the whole thing to come crashing down, so here’s how to plumb it all together.
Step 1: Download the Components Using My Handy .zip File
Head on over to my Public SkyDrive folder at https://skydrive.live.com/redir.aspx?cid=97ff1cc1e797489a&resid=97FF1CC1E797489A!2525&parid=97FF1CC1E797489A!194&authkey=! and download the Set-UAPicture.zip file and extract it. It contains three files: A .cs C# code file, a PowerShell .ps1 script and a VBScript .vbs file.
Step 2: Compile the .cs Script
I could have provided this as a .exe to download, however no IT Pro likes to download an untrusted .exe from the Internet, so why not compile your own using any Windows 7 machine?
Open a command prompt and set the directory to the path where you extracted the .cs file. Next, type the command %Windir%Microsoft.NETFrameworkv3.5csc.exe <Path to the .cs File>Set-UAPicture.cs. This will output a .exe file called Set-UAPicture.exe based on the code content of the .cs file.
Step 3: Review the Scripts (Optional but Recommended)
So if you want to know what the script is doing (and you should if you plan to use this for a production environment) here is the explaination. I’ll explain the VBScript first because it’s the simple part.
The VBScript simply copies the .ps1 and the .exe files from the GPO Scripts folder that you would create down in Step 5 to the %Temp% folder on the local machine using a FileSystemObject. Done
The PowerShell script is more complicated, but as PowerShell scripts go, is still extremely simple and compact. First thing’s first, the script checks to see if it is running in Single Threaded mode. For some reason I had problems with the script executing in Multi-Threaded mode, so I had to apply this conditional If statement.
If you are in STA mode, then the script continues, otherwise it will relaunch the PowerShell instance with this script in STA mode.
Next is the variable setup. The script simply defines three variables: The users username, the users domain name and the Temp folder path.
Next, we use an ADSISearcher object in PowerShell which is a short-hand reference to a System.DirectoryServices.DirectorySearcher object. Using this, we search AD for the current users account name, and then pull the thmbnailPhoto attribute into a variable called $Photo. After this, the thumbnailPhoto is written in Binary encoding to a .jpg file in the users Temp directory with the naming convention DOMAIN+username.jpg, so for me for instance, it would be TESTCORP+rgreen.jpg.
Next is where the previous version of the script from the Internet failed even after I patched the holes in it: If a user had no thumbnailPhoto attribute set, the script would halt causing an error to appear in-front of the user reporting that Set-UAPicture.exe had encountered a problem and needed to close. This is because the .exe expects two variables and if no picture is present then it doesn’t get one of them. To cure this, I setup an If statement which prevents the execution of the .exe is the $Photo variable is equal to $Null.
If you have a thumbnailPhoto attribute value set and all is good, the Set-UAPicture.exe is called with two variables as I said previously: First is the username in DOMAINusername format, followed by the path and filename of the .jpg picture which was generated.
Step 4: Deployment Options
In the .zip, I provide two deployment methods. You can either go native PowerShell, however this has some requirements. PowerShell uses a feature called ExecutionPolicy which determines where your local computer can execute PowerShell scripts from and the security level associated with this. The default is Restricted which means that only code signed PowerShell scripts can be executed. A normal behaviour is to change this to RemoteSigned which means that unsigned scripts can be ran locally, allowing your administrators to cook their own PowerShell scripts and run them for themselves, however it then requires that all scripts which are ran from the network must be signed with a code signing certificate.
If you have a CA internally which can produce Code Signing certificates then you can sign the .ps1 file (which The Scripting Guys have a good post on here http://blogs.technet.com/b/heyscriptingguy/archive/2010/06/17/hey-scripting-guy-how-can-i-sign-windows-powershell-scripts-with-an-enterprise-windows-pki-part-2-of-2.aspx). You could change your ExecutionPolicy to Unrestricted, however you may not last too long in your job if it turns out that your infrastructure gets brought down by a malicious or badly written script which was able to propagate the network. The next option is to set the ExecutionPolicy in conjunction with a Bypass (which is explained in the TechNet article here http://technet.microsoft.com/en-us/library/dd347628.aspx) however this option requires you to have a means of updating the ExecutionPolicy on your network clients in the wild with the new .ps1 path.
The easiest option then, unless you have a CA capable of Code Signing is to use the VBScript. The script is actually extremely short as it still relies on the PowerShell script. The VBScript simply copies the .ps1 and the .exe files to the users %Temp% directory and launches the PowerShell script from there. This then allows you to execute the script using the safe option of RemoteSigned ExecutionPolicy.
Step 5: Deploy
Deploying this is simple. Create a GPO in AD DS, disable the Computer Configuration section to reduce GPO processing time and define the Setup-UAPicturePS1.vbs (or Set-UAPicture.ps1 if you are going native) as a logon script.
Once done, you need to add a WMI Filter to the GPO so that only machines matching or exceeding Windows NT 6.1 can execute it. You need this if you have a mixed Windows XP and Windows Vista/7 environment because Windows XP doesn’t support the User Account Picture feature and unless you are deploying it via Configuration Manager or another means, you don’t know if your XP machines are all going to have PowerShell installed.