End-To-End SharePoint 2010 User Profile Photos & AD

During a recent WSS3 to SharePoint Server 2010 upgrade, the need arose to move roughly 600 user photos into Active Directory and implement user profile photos for SharePoint. While user photos existed, they were stored in a SharePoint library which would be sunset following the build of the new 2010 environment. Additionally, the user photos needed to be available for a custom web part solution highlighting employee birthdays and anniversaries. Initially, thoughts were to follow the old model and store the photos in a picture library, but with the organization's IT strategy and upcoming deployment of Lync 2010, using Active Directory as the central repository for photos made the most sense.

Step 1: Getting Photos into Active Directory

There's a ton of blog posts and articles available on how to use PowerShell and Exchange tools to import photos into Active Directory. Normally I'm the first person to jump on the PowerShell bandwagon, but in this instance there are a few constraints. Pushing thumbnails to AD with this method requires your images to be less than 10kb in size, and a fixed size of 96 x 96px. If you intend to reuse these images outside of Exchange or Lync, you likely want a higher quality of image. You certainly don't want to be using unedited photos right out of a digital camera, but something more along the lines of a 300px image is going to give you greater quality and reuse.

Due (primarily) to this constraint, we opted to use a third party tool that gave a little more control over the image format and size, while still offering a bulk load ability. We grabbed the trial version of AD Photo Edit from Chris Wright, and put it through it's paces. The trial lets you execute just 5 users at a time, but once we validated it would do what we wanted we purchased a copy of the full version. At just $49, it's significantly cheaper than the manual effort of doing this one at a time for 600 users. This particular tool had the ability to specify the naming format of our photos, so we could point it to the folder of 600 photos and it would automatically reconcile to the correct AD user and import the photo. Once you confirm that your photo names conform to a naming convention that matches an AD property, you're off to the races.

Step 2: Mapping the AD Photo into SharePoint 2010

Once you've pushed all of your photos into Active Directory, you must tell SharePoint they are there. By default, SharePoint will not just grab the photos from AD. Head into Central Administration and navigate to your User Profile Service Application. From the main UPS screen, pick "Manage User Properties" and edit the "Picture" property. Set the "Edit Settings" radio button to not allow users to change their photos, ensure that your "Source Data Connection" is set to your Active Directory UPS connection, set the "Attribute" to "thumbnailPhoto" and the "Direction" to "Import". Finally, click "Add", then "OK".

Since UPS didn't know about this field previously, we must now perform a synchronization and allow UPS to fetch the data for this newly mapped field. From your User Profile Service Application, click "Start Profile Synchronization", select a full synchronization, then click "OK". This first synchronization since the field was mapped must be done as a full sync because that field previously was un-mapped. During an incremental synchronization, UPS will update the values for all mapped fields, but it will not look for new ones. Once a full sync completes, incremental will be sufficient for future updating of the individual account details.

If you think back to step one, you'll remember that we chose this method because wanted a better quality of photo than a 10kb 96px image. As SharePoint will be looking for a specific size to use in thumbnails and user profile pages, we must now tell SharePoint to generate the appropriate sized images from the original in Active Directory. Before you can perform this task, ensure that the user you're logged in to the SharePoint server with has "Full Control" on the User Profile Service Application. From the main "Manage Service Applications Screen", select the User Profile Service Application, and then click "Administrators". Confirm that the account you're logged in as has the "Full Control" role, and if not add it now.

Next, open a new PowerShell session and run the following commands:

Add-PSSnapin Microsoft.SharePoint.PowerShell -EA 0
Update-SPProfilePhotoStore -CreateThumbnailsForImportedPhotos 1 -MySiteHostLocation http://mysites

The first command simply loads the SharePoint PowerShell Snapin if you haven't already loaded it. The second command physically tells UPS to iterate through the photos in the user profiles and generate the thumbnails if they don't already exist. Obviously if you've bulk loaded a large number of photos this will take a few minutes. Be sure to include the correct path to your MySite host. If you get an error about an object reference not set to an instance of an object, you likely have a permissions issue, and the user you're logged in as doesn't have full control of UPS.

Once this operation completes you should see user photos in SharePoint. Note that the best way to verify this is to look at the actual personal profile (person.aspx). Performing a search will still show you the old photo (or no photo) until the next incremental search crawl takes place on the content source with your sps3:// provider mapped.

Step 3: Ongoing Maintenance

This is fabulous, I've bulk loaded my 600, 1000, 2000 users and we're up and running; but what happens when I change 10 of my photos tomorrow, or process a new hire next week? First of all, you've got to put that user's photo in Active Directory. Many of the tools that offer bulk loading of photos will also allow you to manage one-off uploads or additions. Once the photo has been added to Active Directory, SharePoint User Profile Synchronization will grab that image on it's next incremental synchronization (typically nightly). The piece that is most easily forgotten is this: UPS may have that image, but it cannot use it until the thumbnails are generated by running the Update-SPProfilePhotoStore cmdlet in PowerShell. Essentially, that cmdlet needs to be run after every UPS sync completes to ensure that the current photo is actually displayed.

The easiest way to accomplish this is to setup a scheduled task that calls your PowerShell script. First things first, create a new PowerShell script containing the following commands, and save it (ie: C:SolutionsUpdate-SPProfilePhotoStore.ps1):

Add-PSSnapin Microsoft.SharePoint.PowerShell -EA 0
Update-SPProfilePhotoStore -CreateThumbnailsForImportedPhotos 1 -MySiteHostLocation http://mysites
$event = New-Object System.Diagnostics.EventLog("Application")
$event.Source = "SharePoint"
$infoEvent = [System.Diagnostics.EventLogEntryType]::Information
$event.WriteEntry("User Profile Service Photos Updated", $infoEvent, 5000)

Note that the first two lines of this script are identical to our previous execution of the Update-SPProfilePhotoStore cmdlet. The lines after it log the event to the system's application log using a source application of "SharePoint", an event description of "User Profile Service Photos Updated", and an event ID of 5000. We add this simply as a way of verifying that the PowerShell script is actually being run (and at the correct time).

With the script saved, head over to Windows Scheduled Tasks, and create a new task. Set the task to execute a program and provide the path to powershell.exe as well as your script: c:windowssystem32WindowsPowerShellv1.0powershell.exe "& 'C:SolutionsUpdate-SPProfilePhotoStore.ps1'"

Schedule your task to run even if the user is not logged on, and ensure it happens sometime after the nightly User Profile Synchronization Timer Job. For example, if your UPS job runs at 1:00 AM, you may want to schedule your task to fire at 3:00 AM, giving ample time for UPS to finish processing before user profile thumbnails get updated. You must also ensure that the task is executed by a user that has "Full Control" to the UPS service application, or the script will fail to process.