If you're reading this, my guess is that you're probably building an image. That image is based on Windows 8.x and it's on the cusp of perfection. However, there's one last tweak that has eluded you.

Setting the Account Picture for domain users based on their photo in Active Directory.

Every couple of months I'd come back to this issue, hoping for a solution.

Truth is: it can be done. Caveat: it's not perfect.

A couple of days ago, I stumbled across an awesome blog post (referenced here) that provided some VB.NET code to implement this method.

I prefer PowerShell over compiled code for tasks like this. So I re-wrote the VB.NET code as a PowerShell script.

Now, as I mentioned above, there is a catch with this solution: the user must log off and on again for the updated picture to be shown in Windows.

In a VDI scenario with non-persistent disks, unfortunately this solution is not for you. For others who predominantly use a single pc, this script will change your life!

It basically takes the image out of Active Directory and stores it on the local machine in a hidden folder, then sets the location for Windows to use in the registry.

Before this script will work, Full Control permissions must be given to: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users and all descendants. This change can be rolled out in your base GPO for all machines. For those who need detailed instructions, please refer to the blog post referenced above.

The script can be downloaded here.

# Title:    Set-AccountPictureFromAD.ps1
# Author:    Jourdan Templeton
# Email:    hello@jourdant.me
# Modified:    12/06/2014 3:27PM NZDT

function Test-Null($InputObject) { return !([bool]$InputObject) }

#get sid and photo for current user
$user = ([ADSISearcher]"(&(objectCategory=User)(SAMAccountName=$env:username))").FindOne().Properties
$user_photo = $user.thumbnailphoto
$user_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
Write-Verbose "Updating account picture for $($user.displayname)..."

#continue if an image was returned
If ((Test-Null $user_photo) -eq $false)
    Write-Verbose "Success. Photo exists in Active Directory."

    #set up image sizes and base path
    $image_sizes = @(40, 96, 200, 240, 448)
    $image_mask = "Image{0}.jpg"
    $image_base = $env:public + "\AccountPictures"

    #set up registry
    $reg_base = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\{0}"
    $reg_key = [string]::format($reg_base, $user_sid)
    $reg_value_mask = "Image{0}"
    If ((Test-Path -Path $reg_key) -eq $false) { New-Item -Path $reg_key } 

    #save images, set reg keys
    ForEach ($size in $image_sizes)
        #create hidden directory, if it doesn't exist
        $dir = $image_base + "\" + $user_sid
        If ((Test-Path -Path $dir) -eq $false) { $(mkdir $dir).Attributes = "Hidden" }

        #save photo to disk, overwrite existing files
        $file_name = ([string]::format($image_mask, $size))
        $path = $dir + "\" + $file_name
        Write-Verbose "  saving: $file_name"
        $user_photo | Set-Content -Path $path -Encoding Byte -Force

        #save the path in registry, overwrite existing entries
        $name = [string]::format($reg_value_mask, $size)
        $value = New-ItemProperty -Path $reg_key -Name $name -Value $path -Force

    Write-Verbose "Done."
} else { Write-Error "No photo found in Active Directory for $env:username" }

Often, it's the small changes that make a big difference for users.

To test the script you can run it with the -Verbose PowerShell flag to see what's happening.

I leave you with a caution. The general reaction from our users after implementing this script is one of shock and surprise. Either they're happy with their photo and all is well, or they hate it and will do anything to get rid of it! ;)

Here's another post I found helpful (referenced here).

Feel free to post your questions and comments below.

[UPDATE 18/07/2014]: Test-Null function fixed