How to import user photos in bulk via PS and Exchange Online?

This topic contains 16 replies, has 3 voices, and was last updated by Profile photo of Jeremy Miller Jeremy Miller 4 months, 3 weeks ago.

Viewing 15 posts - 1 through 15 (of 17 total)
  • Author
    Posts
  • #37593
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    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.

    #37595
    Profile photo of Richard Diphoorn
    Richard Diphoorn
    Participant

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

    #37600
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    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.

    #37604
    Profile photo of Richard Diphoorn
    Richard Diphoorn
    Participant

    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)"))
        }
    }
    
    #37651
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    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.

    #37657
    Profile photo of Richard Diphoorn
    Richard Diphoorn
    Participant

    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
    
    #37658
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

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

    #37661
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    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.

    #37673
    Profile photo of John Mello
    John Mello
    Participant

    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.

    #37703
    Profile photo of Richard Diphoorn
    Richard Diphoorn
    Participant

    Jeremy, I made a blog post about this. Hope it helps! http://powershell.org/2016/04/13/how-to-import-user-pictures-in-exchange-online/

    #37761
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

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

    #38053
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    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.

    #38104
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    Any help would be greatly appreciated 🙂

    #38111
    Profile photo of Richard Diphoorn
    Richard Diphoorn
    Participant

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

    #38112
    Profile photo of Jeremy Miller
    Jeremy Miller
    Participant

    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
    }

Viewing 15 posts - 1 through 15 (of 17 total)

You must be logged in to reply to this topic.