Office 365 Photo Conversion and Import

This post is Part 2 in the series on converting and importing user photos for availability in Office 365.  This post will cover the generation of thumbnail photos for on-prem AD, import of photos to Exchange Online as well as commands that can be used to extract and view the photos from both locations. To review Part 1, please click here.

Office 365 Photo Workflow

In the previous post, we walked through how to take a base64-encoded string and convert it to a JPG for import into AD and Exchange Online.  Next, we’ll generate the on-prem AD photo thumbnails and import them to Exchange Online.

Generate 96x96 AD thumbnailPhotos and import to on-prem Active Directory

User photos in the on-prem Active Directory are stored in the thumbnailPhoto attribute for each user.  This attribute has a size limit of 100Kb, so all photos imported must be smaller than this.  The photos we’ve just converted from base-64 to JPG may be high-resolution photos, so some will be much larger than this and will fail to import.

It’s typically easier to use PowerShell to resize a photo according to a pixel ratio rather than a file size, so after some experimentation, it appears that resizing to 96x96 pixels will keep the file sizes under 100Kb.

To resize the photos, we’ll need the PowerShell Image Module located here:

After following the instructions to set up the module, we can “activate” it by using  Import-module Image.

The next thing we’ll do is create an array out of all the JPGs in the c:\photos directory, and add that to a variable we can manipulate, like so:

[Array] $LGPhotos = get-childitem c:\photos | select-object name | where-object {$ -like "*.jpg"}

Then we just iterate through the array with a ForEach loop, passing each photo through the image filter and adding *_AD.JPG to the name of the photo.  This way we can differentiate between the thumbnail photos we will import to the on-prem AD and the hi-res photos we’ll import to Exchange Online.

      ForEach ($CurLGPhoto in $LGPhotos)


             $image = get-image $CurLGPhoto.Name

             $imagename = $CurLGPhoto.Name -replace ".jpg$", ""

             $image = $image | Set-ImageFilter -filter (add-scalefilter -width 96 -height 96 -passthru) -passthru

             $p = "c:\photos\" + $imagename + "_AD.jpg"



And now we have 96x96 thumbnails of all our hi-res photos!

To import the photos to the on-prem AD, we re-use a bit of the script with the {If…Else} statement that checks for photo content in the CSV file, set our user variables, and follow that up with the commands to read the JPG into memory and import it into AD:.

In this case $CurADUserName is the primary email address from the CSV file, and $CurADPhotoPath is the full name of the JPG that has *_AD.JPG in the name for that user.

$CurrentADUser = Get-ADUser -f {samAccountName -eq $CurADUserName} -Properties SamAccountName

$PhotoData = [System.IO.File]::ReadAllBytes($CurADUserPhotoPath)

Set-ADUser $CurrentADUser.SamAccountName –replace @{thumbnailphoto=$PhotoData}

We can see from the above that we’re matching each user’s on-prem AD SamAccountName with their primary email address from the CSV file minus “”.

And presto!  All users now have photos imported to Active Directory.

Import high-res photos to Exchange Online

The last stop on our adventure tour is to import the high-resolution photos we converted earlier to Exchange Online.  As specified before, Exchange Online is the authoritative source for Office 365 photos.

By now you’re familiar with the code we’re using in the {If…Else} statement earlier to iterate through the CSV file and look for user photo data, so we won’t repeat that here, except to say that the $CurExUserName variable is the user’s primary email address from the CSV file minus “”.

The command to import the user photos to Exchange Online is Set-UserPhoto.  First we get the UPN for the current user by doing a Get-ADUser, we select the UPN for use in the import, and we read in the photo content as follows:

$CurrentADExUser = Get-ADUser -f {samAccountName -eq $CurExUserName} -Properties SamAccountName,UserPrincipalName

$Exphoto = ([Byte[]] $(Get-Content -Path $CurExUserPhotoPath -Encoding Byte -ReadCount 0))

Set-UserPhoto $CurrentADExUser.UserPrincipalName -PictureData $Exphoto -Confirm:$False

Photos are immediately available via OWA and Outlook in Online mode (Windows only), usually within 30 minutes for Lync (2013 for PC only), and within 24-48 hours for other clients.

Some final thoughts…

Importing photos is all well and good, but let’s say for some reason that you want to be able to view the existing photos in Exchange or AD.  Isn’t there a facility for doing that?

Well, the answer is “yes, but…” (you knew that was coming…)

User photos cannot be directly viewed in on-prem AD or Exchange Online except through the clients mentioned above.  However, with a little reverse-Kung-Fu we can take the commands we used above to import the pictures and read them back out to the file system.

So, to export the existing photo for an on-prem AD user, we can use this bit of script. Just replace “USER” with the SamAccountName of the user whose photo you want to export.

$User=GET-ADUser “USER” –properties thumbnailphoto, SamAccountName

$Filename='C:\Photos\' + $User.SamAccountName + '_ADExport.jpg'

[System.Io.File]::WriteAllBytes($Filename, $User.Thumbnailphoto)

If a photo exists for that user, you should see it in the C:\Photos directory.  If a photo does not exist, you’ll get an error that the “value cannot be null”.

Likewise, to export a photo for a user stored in their Exchange Online mailbox, we can use this.  Just replace USER@DOMAIN.COM with the email address of the user:

$user = Get-UserPhoto USER@DOMAIN.COM

$user.PictureData |Set-Content "C:\Photos\ExPhoto_$($user.Identity).jpg" -Encoding byte

Once again, if a user has a photo, it will now show up in the C:\Photos directory.  If a user does not have a photo in their Exchange Online mailbox, you’ll get a PowerShell error that “there is no photo stored here”.

I hope this information is helpful!

<The above is informational in nature. ZAG does not warrant the above for the reader's specific environment. Please contact us with questions or if you would like to engage us to implement this solution in your environment.>


Loraine Treadwell


ZAG Technical Services, Inc.