Syncing Windows User Tiles (Logon Screen / Start Menu) from AD

As you may know, Outlook allows you to store a picture for contacts, which will then be displayed in several places, such as mails, calendar entries, address book and others. A few years ago, shortly after our company migrated to Windows 7 and Outlook 2010, I stumbled across an article that explained how you would enable Outlook to also display a pictures for Active Directory accounts1. Much to my surprise, both management and users seemed to like that feature, but someone pointed out that there were other – rather obvious – places where stock pictures (or no pictures at all) were shown instead of the user’s photo: in the start menu, as well as in the logon and lock screens.

Asking Google revealed that there is no way Windows 7 would fetch those pictures automatically from the AD, you’d have to build a custom solution2. Fortunately for me, someone else did most of the research, so I didn’t have all that much work, except for extending the little C# example program to my needs in order to get the picture from AD and set it for the user tile:

Compile, store on your NETLOGON share and add a call to that program to your logon script. And voilà, user tiles are being displayed on the logon screen as well as the start menu.

All was good, and I started to forget about this program, until we decided to slowly migrate to Windows 10. All of a sudden, there were machines where no pictures would be displayed. From what I’ve read, starting with Windows 8, Microsoft decided that the User Tile API call was no longer necessary, so – while apparently not completely removing it – they just made it do nothing. So far, I have not found documentation on the API function that replaces/supercedes the SetUserTile function, even though it must exist3. However, some clever people found a workaround that we can actually implement rather easily. I started just like recommended in the article by creating the necessary GPOs4 for giving users Write permission on the necessary registry keys as well as storing the VB and the PowerShell scripts on the NETLOGON share. After adding a call to the VB script to the logon script, It worked rather well on x86 machines, but there were some problems on x64. Mainly the problem that sometimes the VB script would call the x86 version of the PowerShell, which then tried to write to the 32bit registry keys5, which a) wouldn’t do anything and b) were still having the „wrong“ permissions. After thinking extensively about how to make sure that I’d always call the correct PowerShell (and I did have some rather weird and complex ideas…). But then I suddenly remembered something…

There’s this call to ADPhotoSync.exe in my logon script. Took me a while to realize that I hadn’t downloaded that somewhere. That’s actually the program I was talking about earlier, self-built, still quietly and reliably doing it’s job on all our Win7 workstations. And, most importantly, it was built for „Any CPU“6, so I didn’t have to deal with x64/x86/WOW64 issues. Luckily, I still had the code laying around on one of my old Development VMs. So why not just port the PowerShell stuff over to that program? It’s only a couple of lines, it’s nothing fancy – just a little bit of registry and file access. Nothing I hadn’t done before.

All in all, it took maybe 15 minutes to slap together a rough draft, only to realise that it worked as expected on Win7, but didn’t do anything on my Win10 test machine. Turns out, Microsoft has once again managed to make life as a programmer and admin a living hell. With Windows 8.1, they’ve deprecated a certain function (Environment.OSVersion.Version) which I use to detect which OS I’m running on, only to later allow you to kinda „un-deprecate“ it for now by adding a manifest file with proper compatibility settings. The manifest file I used is this:

Of course, Microsoft has *not* yet provided an official „new“ way to detect the currently running Operating System to the .Net framework that will be both backward compatible and future proof. There is a bunch of stuff on how to do it C using native API calls. But I’m getting off track here…

Back to the point. After having solved the OSVersion stuff, things started working like a charm. So all there was left to do was tidying up the code, putting in a few lines of documentation and finally store things in our TFS so that I would eventually be able to find the code again even if I ever delete that particular old VM. And, of course, write this artice 🙂

For reference, here’s what the code looks like now:

For what it’s worth: since there are different files for different sizes, one *could* do something that can’t be done using the GUI: use different images for different display locations. You could have one picture in the logon screen (which is sourced by the 448x448px registry entry), but another in the start menu (sourced by the 32x32px entry).

Fußnoten / foot notes

  1. long story short: you store said picture in the user’s AD object’s attribute „thumbnailPhoto“ either by using PowerShell (Set-ADUser or Exchange’s (seemingly buggy) Set-UserPhoto) or software like CJWDEV’s ADPhotoEdit
  2. Fun fact: as of this writing, with Windows 10 v1803 being publicly available and v1809 in closed beta, Microsoft has still not managed to automate this process.
  3. There’s a GUI way to set your own tile, which eventually writes registry keys and files to locations a user has no access to, so there *must* obviously be some API call that delegates that task to the SYSTEM context. We just haven’t found it yet.
  4. I later extended those policies to actually (re-)create the main registry key HKLM\Software\Microsoft\Windows\CurrentVersion\AccountPicture\Users (just in case it gets deleted) as well as to create and set permissions to the Folder where we store the pictures
  5. HKLM:\Software\WOW6432Node\…
  6. overly simplified and not technically *fully* correct, that’s the .Net way of having code for both x86 and x64 in the same „binary“ und choosing the right code automatically based on the current OS architecture. More in-depth details here

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

%d Bloggern gefällt das: