Author Posts

April 12, 2016 at 11:51 am

I'd like to be able to bulk import photos to user accounts via Exchange Online and so I tried this script. (The users photos are 10kb or smaller and are stored in the User images folder with their samAccountName as the file name)

*We have an on prem AD environment that is synced to O365 via DirSync*

After connecting to Exchange Online I tried this script

$path = 'C:\Temp\User images\'
$Images = Get-ChildItem $path
$Images |Foreach-Object{
$Identity = ($_.sAMAccountName.Tostring() -split "\.")[0]
$PictureData = $path+$_.sAMAccountName
Set-UserPhoto -Identity $Identity -PictureData ([System.IO.File]::ReadAllBytes($PictureData)) -Confirm:$false }

But when I hit enter I get this error message, "You cannot call a method on a null-valued expression. Referencing the 4th line $Identiy = ($.sAMAccountName.Tostring...

I have a feeling that sAMAccountName is not an O365 attribute and so that's why it's saying it's null. I tried userPrincipalName instead and got the same error message.

April 12, 2016 at 11:55 am

Last week there was a topic like that one. I think it will help you: https://powershell.org/forums/topic/daily-import-photos-into-active-directory/

April 12, 2016 at 12:31 pm

Thanks for the reply Richard. I'd like to store it using the Exchange Online module if possible and not import it directly to AD. I was able to get it to work with a single user but the bulk process is proving more tricky.

Someone suggested replacing samAccountName with $_.BaseName because files don't have a samAccountName attribute. I made that change and the null errors went away but now it's saying it can't find the image files in the User images folder. Even though it names them specifically.

April 12, 2016 at 2:33 pm

Hi Jeremy, I've change the code a bit that I provided in the previous post. Can you try it?
Please try it first on a few accounts, not on all accounts. 🙂

$imageFolder = 'C:\Temp\Images\'
$pictures = Get-ChildItem $imageFolder

foreach ($picture in $pictures){
    
    Try {
        $user = Get-User -Identity $($picture.BaseName) -ErrorAction Stop
        }
    
    Catch {
        Write-Warning "Warning: $_"
    }

    If ($user) {
        $user | Set-UserPhoto -PictureData ([System.IO.File]::ReadAllBytes("$imageFolder\$($picture.Name)"))
    }
}

April 13, 2016 at 6:10 am

That script worked like a charm Richard 🙂

I added -Confirm:$false at the end so it wouldn't prompt me Y or N since I'm planning on doing hundreds of these.

Thanks a ton.

April 13, 2016 at 6:32 am

Just change this line from:

$user | Set-UserPhoto -PictureData ([System.IO.File]::ReadAllBytes("$imageFolder\$($picture.Name)"))

To:

$user | Set-UserPhoto -PictureData ([System.IO.File]::ReadAllBytes("$imageFolder\$($picture.Name)")) -Confirm:$false

April 13, 2016 at 6:38 am

You beat me to it 🙂 I was just editing my post before to say I found the Confirm false method.

April 13, 2016 at 6:45 am

Do you mind walking me through the logic of this script?

I guess the part I'm not understanding is why is it $picture.Name in the last step instead of $picture.BaseName.

April 13, 2016 at 7:49 am

As a fun side note, once you upload a picture to Exchange 2013+/Office365 it does some additional editing and filter to the file. Basically it will take any file that doesn't match these resolutions

  • 48×48
  • 64×64
  • 96×96
  • 120×120
  • 240×240
  • 360×360
  • 432×432
  • 504×504
  • 648×648

and perform a center crop with a bias towards the top of the file (around 15%) along with down sampling it to the nearest supported resolution. Even if you give it a file that matches the supported resolution, Exchange performs some sort of compression on it to reduce the file size.
This makes it a pain to do a byte compare or file hash of the original image since they will always be different even though they look the same and are the same resolution. At my company we rely on a separate HR system to get our employee photos, so we had to automate the process of updating employee photos. Luckily in march of 2015 Ben Vierck presented his PSimaging module on the scripting guys blog https://blogs.technet.microsoft.com/heyscriptingguy/2015/03/19/psimaging-part-1-test-image/"scripting guys blog. This allowed us to properly judge if the image was dissimilar enough to flag it as a new image.

April 14, 2016 at 6:22 am

Thanks a ton Richard. Your blog post was very helpful.

April 20, 2016 at 5:59 am

I've been using the script and the more users I test in a batch the more errors I keep getting that say "the operation couldn't be found because object 'abrewerwl' couldn't be found on 'BY1PR04...NAMPR04...prod.outlook.com

I ran the script for a group of 50 users and received that error for 11 of the 50 accounts.

Is there a way to get these errors to go away? The 11 accounts it's referring to all do exist in our O365 and AD environment.

What I've also noticed is that the script keeps going down the list regardless of the errors. And so it's assigning the picture of the account that's displaying an error to the next user in the list. Of the 50, I had to change 7 of the accounts because they were assigned the wrong picture.

April 21, 2016 at 5:23 am

Any help would be greatly appreciated 🙂

April 21, 2016 at 7:24 am

Hmm, not sure what causes that. Maybe put in a Start-Sleep after the If scriptblock. Can you try that?

April 21, 2016 at 7:30 am

Like this?

$imageFolder = 'C:\Temp\Images\'
$pictures = Get-ChildItem $imageFolder

foreach ($picture in $pictures){

Try {
$user = Get-User -Identity $($picture.BaseName) -ErrorAction Stop
}

Catch {
Write-Warning "Warning: $_"
}

If ($user) {
$user | Set-UserPhoto -PictureData ([System.IO.File]::ReadAllBytes("$imageFolder\$($picture.Name)"))
}
Start-Sleep -s 5
}

April 21, 2016 at 9:26 am

Yep. try 2 seconds first, 5 seconds is quite long. 🙂

May 2, 2016 at 7:26 am

Turned out changing the image file name to the user's full email address (including the @domain) eliminated the "object not found" errors completely.

November 14, 2017 at 7:40 am

I'm in need of some help using this method to bulk import my users. I've been at a loss in trying to figure out how to do it. This script and the one Richard blogged about would be perfect, however, my photos come from a third party app (our Badging Department) and the employee photo image files get stored in separate sub-directories based on employee number. They are unwilling to change this behavior to "C:\EmpPhotos\". Instead this is an example what the directory structure looks like:

C:\Images\0000\0001.jpg
C:\Images\1000\1000.jpg
C:\Images\2000\2002.jpg

Is there a way to search all sub-folders for a match with the AD attribute "employeeNumber"?