Importing photos into Active Directory with PowerShell is a very easy way to make technology more personable. These photos, which appear in Outlook 2010+/Outlook Web Access 2013+, enable remote workers to see people they always email. They allow new hires to connect faces to names. In every way, they make digital communication more human. In this post, we are will explore three scripts that make importing photos into Active Directory automatic.
Set User Thumbnail Photo Attribute with PowerShell
Start by grabbing the packaged up scripts from here. You will need a machine that can regularly run a script as a scheduled task. Open the ZIP and save the entire ADPhotos folder to the machine that will be running the task. To make the process easier, save the ADPhotos folder to C:\Users\Public\Scripts\.
We will use two scripts to import pictures into Active Directory. Our first script (SetUserThumbnailPhoto.ps1) calls a conversion process, links a picture to a user, and imports the picture. Our second script (GALBatchConvert.ps1) converts the picture to an AD friendly format and prevents Active Directory bloat. This script was written by Steve Goodman. You will also have four folders within the ADPhotos root folder:
- Converted: Contains a converted picture while being imported into AD.
- Input: An optional place to store pictures before conversion. In the script, you will see that I actually input from a network share.
- Logs: Records when a user’s picture is imported.
- Output: Stores the AD friendly pictures until they match a user.
Let’s take a look at our first script, SetUserThumbnailPhoto:
$InputFolder = "\\Server\Share\Email Photo\" $OutputFolder = "C:\Users\Public\Scripts\ADPhotos\Output\" $ConvertedFolder = "C:\Users\Public\Scripts\ADPhotos\Converted\" $ScriptLocation = "C:\Users\Public\Scripts\ADPhotos\" Add-PSSnapin Quest.ActiveRoles.ADManagement $Date = Get-Date -Format o | foreach {$_ -replace ":", "."} $Pictures = Get-ChildItem $InputFolder Set-Location $ScriptLocation .\GALBatchConvert.ps1 -InputFolder $InputFolder -OutputFolder $OutputFolder foreach ($Picture in $Pictures){ $User = $Picture | Get-Acl | select Owner -ExpandProperty Owner $PictureName = $Picture.Name $PictureLength = $PictureName.Length $PictureNamewithoutExt = ($PictureName.Substring(0,$PictureLength-3)) $ConvertedPicture = $OutputFolder + $PictureNamewithoutExt + "jpg" Copy-Item -Path $ConvertedPicture -Destination $ConvertedFolder $PictureContent = [byte[]](Get-Content $ConvertedFolder\* -Encoding byte) Get-QADuser $User | Set-QADUser -ObjectAttributes @{thumbnailPhoto=$PictureContent} | out-file ".\logs\log$date.txt" -Append <# Use the line below if you want your users to name the picture after their username. Get-QADuser $PictureNamewithoutExt | Set-QADUser -ObjectAttributes @{thumbnailPhoto=$PictureContent} | out-file ".\logs\log$date.txt" -Append #> sleep 5 get-childitem $ConvertedFolder | Remove-Item } Remove-Item $InputFolder\* -Recurse Remove-Item $OutputFolder\* -Recurse
Launch PowerShell ISE and edit the SetUserThumbnailPhoto script. At line 1, configure an input folder. If you want users to be able to save their own photos, this should be a network share where users have write access. This script does use the Quest AD cmdlets. If you don’t have these, they can be found under the PowerShell section on this page. I use these cmdlets often and found it extremely helpful to add them to my PowerShell profile.
SetUserThumbnailPhoto grabs the names of all files in our InputFolder (line 10) . Line 13 calls our second script, GalBatchConvert. This script uses our Input and Output folder variables to optimize pictures for Active Directory. Users may save multi-megabyte pictures in your input folder. After optimization, the photo should be less than 100 KB. Pictures will also be cropped down. It is important to specify that only headshots be saved in the input folder or users might end up with some odd photos.
Our original script then jumps into a foreach loop. It grabs the Owner attribute of each picture and matches that owner to an Active Directory user (line 18). The picture is moved to the ConvertedPicture folder, saved as a variable, and imported with the Get-QADUser cmdlet. At the end of the loop, the convertedpicture folder is cleaned up. At the end of the script, the input and output folders are cleaned as well.
On a dedicated machine, configure a new scheduled task that calls powershell.exe as the command and “-noprofile path-to-SetUserThumbnailPhoto-script” as the argument. Ensure that the user running the script has read access to the input folder and write access to the other three folders. The user running the script also needs permission to modify the thumbnailPhoto attribute of users.
Get User Thumbnail Photo Attribute with PowerShell
If the script runs successfully, you can launch Active Directory Users and Computers (ADUC) to view the thumbnailPhoto attribute. You will need to open the user’s properties directly in ADUC – you cannot search for the user and then open properties. You will also need advanced features enabled. If you need to remove the thumbnailPhoto, edit the attribute and select clear.
Included in the ZIP is a third script, Get AD Photo. This script was written by Peter Rossi. Open this script in PowerShell ISE and run it once. This allows you to use two new functions: Get-ADThumbnailPhoto and Set-ADThumbnailPhoto
The Get-ADThumbnailPhoto function retrieves the thumbnailPhoto attribute data and saves it as a picture. The Set-ADThumbnailPhoto is useful when you need to add or modify the picture for a single user.
These three scripts allow me to completely manage photos in Active Directory and Outlook. The first automates the photo import process, the second prevents AD bloat, and the third gives me granular viewing and control of the thumbnailPhoto attribute.
Do you import photos into Active Directory? Why or why not?
How would you change the script to use the username instead of the owner (as James was talking about)?
The only way that I know to do this is to have the user name the file after their username.
As Joseph said, I had to name the photos to be the same as the username. So for us, we use:
joe.bloggs
So the picture is named:
joe.bloggs.jpeg
The script then gets the name of the file and tries to find a matching user, if it does then it processes the image. If not, then I get a message to say so in my logs.
I’m assuming you need to change the script to do this? If so, could you provide an example? Thanks.
A little, yes. So the next few lines from line 16 (original script, above) are:
foreach ($Picture in $Pictures) {
$PictureName = $Picture.Name
$PictureLength = $PictureName.Length
$User = ($PictureName.Substring(0,$PictureLength-4))
if (Get-QADUser $User) {
$LogData = “User ” + $User + ” found.`n”
Write-Output $LogData
$LogData | out-file “$LogsLocation$date.txt” -Append
if ($ValidExt -contains $Picture.Extension) {
I’ve omitted getting the owner, and then creating a username by knocking off the extension of the picture (lenght-4). QADUser finds me a match to the username and I log that. Finally it checks the extension is a valid one and if so process the image. I’ve got some further tweaks in regards to image size and what I do with the images, but the above explains what you need I think.
No worries Joseph 🙂 you share enough with us, happy to share a little as well!
Would you be able to post what the completed script would look like? My apologies, I don’t do much powershell scripting (obviously). 🙂
I’ve gone back and forth on pasting it in… I’m still learning PS myself, so it’s a mash up of experiments and bits of (bad) code. But, I’d encourage you to do the same before just copying the script out right!
Here’s a pastebin link to preserve formatting: http://pastebin.com/Lhhn1Q3Y
Only look if you need to! haha
Thank you for the details on that!
I have a question about this process that I can’t find an answer to else-ware. Our environment uses employee photos that are held in a network. These files are not named the way I would prefer. The photos are names “Last Name, First Name.jpg”. For example Joe Smiths ad user may be jsmith but his photo is called “Smith, Joe.jpg”. To further complicate these photos are being referenced by several homegrown apps that are not in my wheelhouse so changing the naming convention would not be an option. The photos are already formatted and smaller than 100kb per.
So my question is what would a script look like that uses the Set-ADThumbnail function, queries the last and first name fields in AD to connect which picture to use, then outputs to the AD users corresponding user name?
To clarify what would it look like to:
Get AD user Last Name, First Name, User Name
For each user go look for JPG named “Last Name, First Name.jpg” in the network share
Use Set-ADThumbnailPhoto to assign the found Jpeg to the User name for that Last Name and First Name combination.
Sure – that solution would work fine. Just build the JPG name and store it as a variable.
Any tips on changing the ownership of the photos? I want to implement them into Outlook and our intranet software should also pick up the images. I have them all in a folder named as the person. My powershell fu is still a little lacking so a nudge in the right direction would be grand!
Keep up the good work though, always find these articles interesting – even if I don’t use them!
See the Get-Owner function in the script on this page: https://deployhappiness.com/saving-space-by-shrinking-pictures-reduce-picture-size-with-folder-redirection/
Let me know if you have any questions!
To share what I’ve done in case anyone else is interested – I’ve used the Set-Owner function used in the linked script from the TechNet library. All the photos that are required need to be named with the username (first.secondname for us) and I’ve dropped a few lines into the above script’s Foreach loop:
$destination = ($InputFolder+$picture)
$account = (‘yourdomain\’+$UserName)
Set-Owner -Path $destination -Account $account -Verbose
And removed this one:
$User = $Picture | Get-Acl | select Owner -ExpandProperty Owner
Oh and, I found that one of my sites wanted to the pictures removed so here’s what I used to remove the thumbnail on a whole OU at once:
Get-QADUser -SearchRoot ‘OU=TroubleSite,OU=Users,DC=yourdomain’ | ForEach-Object {
Set-QADUser $_ -ObjectAttributes @{thumbnailPhoto=””}
}
Thanks for the nudge and the inspiration Joseph 🙂
I’m just going to update my post with a comment of how dense I was being! With the image names being the user name, you don’t need to set the owner or even grab the owner. You can use the file name as the username to attach the image to… which is probably a better idea if you can get your images named correctly!
Using the owner of the file is only useful if you want your users to supply their own image, but if it’s HR or someone else providing the images then going by file name is better.
Amazing what perspective lunch can bring 🙂
I have that happen all of the time! I love when I can spend all night working on an issue and then wake up with the solution. 🙂
Excellent article once again. However, I could point you over to https://www.exclaimer.co.uk/outlook-photos/
Its a free product and so simple to use, we just give it to the HR team and let them get on with it!
Thanks Jeff – I haven’t seen that product before but it looks very simple to use!!