How To Import User Pictures in Exchange Online

In a recent thread on the forum, Jeremy Miller asked how to import user pictures in bulk in Exchange Online. I was providing some answers to Jeremy, and he asked me to explain the logic behind the solution (the one that worked for him actually). So I will try to explain it here.

The Script
$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)"))


  • Line 1: The path to the pictures is assigned to the variable $imageFolder
  • Line 2: The contents of the directory in the $imageFolder variable is assigned to the variable $pictures. This variable is of the type System.Array:
  • Lines 4-17: An foreach loop is being used here to iterate through each picture. This because on every picture an action will be performed.
  • Lines 6-12: I'm using a Try/Catch construction here, because I want to keep the foreach loop going; even when a user does not exists in Exchange Online. If a user exists, then the resulting user object is being assigned to the variable $user. If not, the the Try/Catch construct is throwing a error and continues with the next user.
  • Line 7: Here I'm searching for a user in Exchange Online. I feed the parameter Identity the BaseName of the $picture variable. BaseName is just the filename, without the extension. Jeremy was having picture files in his folder names like the SamAccountNames. So $($picture.BaseName) will give back the SamAccountName in Jeremy's situation. Get-User takes in the SamAccountName as value for the Identity parameter, and will therefore find (or not) find the user in Exchange Online.
  • Line 11: If a user cannot be found, the error message from the Get-User Cmdlet will be shown on in the shell. $_ means, 'current object in the pipeline'.
  • Line 14-16: If the $user variable contains something, we will execute Set-UserPhoto to assign a picture to the user. $user is being piped to Set-UserPhoto, so that I don't have to specify the -Identity parameter of the Set-UserPhoto Cmdlet. -Identity takes in values by property name. So where the piped object contains any value accepted by the parameter Identity, the Set-UserPhoto will execute the action on the user in Exchange Online.

Sample Usage

From the examples of Set-UserPhoto it can be seen that you can use the Set-UserPhoto as following:
Set-UserPhoto "Paul Cannon" -PictureData ([System.IO.File]::ReadAllBytes("C:\Users\Administrator\Desktop\PaulCannon.jpg"))
What I have done in the script is piping $user to Set-UserPhoto, so I don't have to use the parameter -Identity.
I also used a subexpression in the code: ("$imageFolder\$($picture.Name). PowerShell always executes this code first before executing the rest of the code. So this subexpression translates to 'C:\Temp\Images\PaulCannon.jpg'.
For all code I made use of Get-Help and Get-Member to put together the code. These are the single most important Cmdlets to know, to discover how to use the Cmdlet (in this case Set-UserPhoto.
To resumate:
  • Get-Help Set-UserPhoto -Full
  • Get-Help about_Try_Catch_Finally
  • Get-Help about_ForEach
  • Get-Help about_If

I hope this helps!

About the Author

Richard Diphoorn

Profile photo of Richard Diphoorn

I'm a autobot. DevOps & Automation are the future. PowerShell Addict. Make it so lover.

One Comment

  1. I'm wet behind the ears with powershell, to be sure, but my SamAccountName field isn't very intuitive. How would I modify the code if I wanted to use UserPrincipalName instead, which would essentially be the email addresses of each of my users? It would be much easier on my HR department if they just named each image file according to the user's email address.