Script repository

Upload user photo to Microsoft 365

Updated on: Jan 18, 2026, Views: 23538

Microsoft 365

The scripts upload a user’s photo to Microsoft 365 (Office 365). The photo will appear in client applications, such as Microsoft Outlook Web App, Lync, Skype for Business, and SharePoint.

Script 1: Upload image stored in AD attribute

In the script, the $propertyName variable specifies the name of the property to obtain user photo from. To execute the script, create a business rule, custom command or scheduled task configured for the User object type.

$propertyName = "thumbnailPhoto" # TODO: modify me

if ($NULL -eq $Context.TargetObject.AzureID)
{
    $Context.LogMessage("User %fullname% has no account in Microsoft 365.", "Warning")
    return
}

# Get the photo
try
{
    $userPhotoBytes = $Context.TargetObject.Get($propertyName)
}
catch
{
    $Context.LogMessage("User %fullname% has no photo in property $propertyName.", "Warning")
    return
}

# Connect to Microsoft Graph PowerShell
$accessToken = $Context.CloudServices.GetAzureAuthAccessToken()
Connect-MgGraph -AccessToken ($accessToken | ConvertTo-SecureString -AsPlainText -Force)

try
{
    # Create temp file
    $tempFile = New-TemporaryFile
    [System.Io.File]::WriteAllBytes($tempFile.FullName, $userPhotoBytes)
    
    # Update the user's photo
    Set-MgUserPhotoContent -UserId $Context.TargetObject.AzureID -InFile $tempFile.FullName
}
finally
{
    # Remove the temp file
    Remove-Item $tempFile -Force
}

Script 2: Upload image from file

In the script, the $picturePath variable specifies the path to the file to obtain the photo from. To execute the script, create a business rule, custom command or scheduled task configured for the User object type.

$userPhotoPath = "\\SERVER\share\%username%.jpg" # TODO: modify me

function ResizePhoto ($picturePath)
{
    try
    {
        # Calculate the new size, preserve ratio
        $original = [System.Drawing.Image]::FromFile($picturePath)
        $ratioX = 648 / $original.Width
        $ratioY = 648 / $original.Height
        $ratio = $ratioY
        if ($ratioX -le $ratioY)
        {
            $ratio = $ratioX
        }
        
        # Resize the picture
        [int]$newWidth = $original.Width * $ratio
        [int]$newHeight = $original.Height * $ratio

        $newPicture = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
        $graph = [System.Drawing.Graphics]::FromImage($newPicture)

        $graph.Clear([System.Drawing.Color]::White)
        $graph.DrawImage($original, 0, 0, $newWidth, $newHeight)

        $memoryStream = New-Object System.IO.MemoryStream
        $newPicture.Save($memoryStream, [System.Drawing.Imaging.ImageFormat]::Jpeg)
        $newPictureBytes = $memoryStream.ToArray()
        
        return ,$newPictureBytes
    }
    finally
    {
        # Release resources
        if ($original) { $original.Dispose() }
        if ($graph) { $graph.Dispose() }
        if ($newPicture) { $newPicture.Dispose() }
        if ($memoryStream) { $memoryStream.Dispose() }
    }
}

if ($NULL -eq $Context.TargetObject.AzureID)
{
    $Context.LogMessage("User %fullname% has no account in Microsoft 365.", "Warning")
    return
}

# Get the photo
if (-not(Test-Path -Path $userPhotoPath))
{
    $Context.LogMessage("File '$picturePath' does not exist.", "Warning")
    return
}

$userPhotoBytes = ResizePhoto $userPhotoPath

# Connect to Microsoft Graph PowerShell
$accessToken = $Context.CloudServices.GetAzureAuthAccessToken()
Connect-MgGraph -AccessToken ($accessToken | ConvertTo-SecureString -AsPlainText -Force)

try
{
    # Create temp file
    $tempFile = New-TemporaryFile
    [System.Io.File]::WriteAllBytes($tempFile.FullName, $userPhotoBytes)
    
    # Update the user's photo
    Set-MgUserPhotoContent -UserId $Context.TargetObject.AzureID -InFile $tempFile.FullName
}
finally
{
    # Remove the temp file
    Remove-Item $tempFile -Force
}

Comments 19

You must be signed in to comment.

  • Manuel

    Manuel

    If there is no picture in AD but there is currently a picture in Office365, will this overwrite the Office365 picture?

    • Support

      Support

      Hello,

      If there is no picture in the proeprty (first script) or there is no file by the specified path (second script), the script will exit and not perform any updates in Office 365.

  • Ethan

    Ethan

    When I ran the second command, I received an error: "Error on proxy command 'Set-UserPhoto -PictureData:... The WinRM client cannot process the request. The connection string should be of the form...." I changed the connection URI from "https://ps.outlook.com/powershell/?proxymethod=rps" to "https://outlook.office365.com/powershell-liveid/" and it started working. Source: https://docs.microsoft.com/en-us/powershell/exchange/connect-to-exchange-online-powershell?view=exchange-ps

    • Support

      Support

      Hello Ethan,

      Thank you for your feedback. The error you faced is a known issue with the Set-UserPhoto cmdlet. It occurs randomly and might depend on temporary network inconsistencies. Changing the connection URI does not fix the issue permanently.

      For your information, Basic authentication used in the script will no longer be supported by Microsoft from October 2020. So, we would recommend you to update your script to connect to Exchange Online using the method described in the Exchange Online using EXO V2 module section of the following article in our repository: https://www.adaxes.com/script-repository/connect-to-exchange-with-powershell-s506.htm.

  • Ben

    Ben

    Is there a script for the opposite of this script? We would like to sync 365 avatars to Adaxes.

    • Support

      Support

      Hello Ben,

      Sorry for the confusion, but we are not sure what exactly you need to achieve. Do you want to get user pictures from Microsoft 365 and save to the thumbnailPhoto property in on-premises Active Directory?

  • Richard

    Richard

    Hi Team

    I have run the Upload image stored in AD attribute in bulk script but it is failing with the following error:You cannot call a method on a null-valued expression. Stack trace: at , : line 17

    • Support

      Support

      Hello Richard,

      For troubleshooting purposes, please, specify what version of Adaxes you are currently using. For information on how to check it, have a look at the following help article: https://www.adaxes.com/help/CheckServiceVersion.

      Also, post here or send us (support@adaxes.com) the script you are using with all the modifications in TXT format.

  • Cory Fiala

    Cory Fiala

    Just wanted to share my modification that will be useful for others as well I'm sure. We use GAM (command line tool for Google Workspace) and wanted to incorporate it to update both Google and O365 photos.

    $propertyName = "thumbnailPhoto" # TODO: modify me
    
    if ($NULL -eq $Context.TargetObject.AzureID) {
      $Context.LogMessage("User %fullname% has no account in Microsoft 365.", "Warning")
      return
    }
    
    # Get the photo
    try {
      $userPhotoBytes = $Context.TargetObject.Get($propertyName)
    }
    catch {
      $Context.LogMessage("User %fullname% has no photo in property $propertyName.", "Warning")
      return
    }
    
    # Connect to Microsoft Graph PowerShell
    $accessToken = $Context.CloudServices.GetAzureAuthAccessToken()
    Connect-MgGraph -AccessToken ($accessToken | ConvertTo-SecureString -AsPlainText -Force)
    
    try {
      # Create temp file for Microsoft 365
      $tempFile365 = New-TemporaryFile
      [System.IO.File]::WriteAllBytes($tempFile365.FullName, $userPhotoBytes)
    
      # Update the user's photo in Microsoft 365
      Set-MgUserPhotoContent -UserId $Context.TargetObject.AzureID -InFile $tempFile365.FullName
    
      # Create temp file for Google
      $userEmail = $Context.TargetObject.Get("mail") # Assuming 'mail' attribute has the email
      $tempFileGoogle = New-TemporaryFile
      $googlePhotoFileName = "$($tempFileGoogle.DirectoryName)\$userEmail.jpg"
      [System.IO.File]::WriteAllBytes($googlePhotoFileName, $userPhotoBytes)
    
      # Update the user's photo in Google
      gam user $userEmail update photo $googlePhotoFileName
    }
    finally {
      # Remove the temp files
      Remove-Item $tempFile365 -Force
      Remove-Item $googlePhotoFileName -Force
    }
    
  • Paul

    Paul

    This does not work with Adaxes version 3.16.21627.0 (and possibly earlier).
    It will throw an error "“Authentication needed. Please call Connect-MgGraph. Stack trace: at Set-MgUserPhotoContent,..."

    I had to upgrade to 3.16.21906.0 to get it to work.

    • Support

      Support

      Hello Paul,

      Unfortunately, this is a known issue. Upgrading to the latest version is exactly the way to resolve the issue.

  • Neil

    Neil

    When running the to "Upload image stored in AD attribute" script above, we get the error below. We are on version 3.16.21906.0 (64 bit)

    [UnknownError] : Stack trace: at Set-MgUserPhotoContent, C:\Program Files\WindowsPowerShell\Modules\Microsoft.Graph.Users\2.10.0\exports\ProxyCmdletDefinitions.ps1: line 47802 ↲ at , : line 31

  • Boris

    Boris

    If I try the script: 1. Upload image stored in AD attribute,

    I get the error message:

    One or more errors occured.

    without any further details

    • Support

      Support

      Hello Boris,

      For troubleshooting purposes, please, send us the following at support@adaxes.com:

      1. The script you are using in TXT format with all the modifications you made.

      2. A screenshot of the error you are facing.

      3. A screenshot of the Multi-server environment dialog. The dialog displays how many Adaxes services you have and what their versions are. For information on how to view it, see https://www.adaxes.com/help/MultiServerEnvironment.

      4. A screenshot of the custom command, business rule or scheduled task executing the script.

      Any additional details will be much appreciated.

  • Stewart

    Stewart

    I have started having failures with this one as well. It runs only when the source photo is updated so randomly. We have made two changes since it would have last run.
    1. Updated Adaxes to 3.17.23627.0
    2. Added our Entra domain as a managed domain as well as a cloud service.

    The error I am getting looks to be related to the authentication process. This is where it fails for me.
    " # Update the user's photo
    Set-MgUserPhotoContent -UserId $Context.TargetObject.AzureID -InFile $tempFile.FullName
    "
    with this error
    Could not load type 'Microsoft.Graph.Authentication.AzureIdentityAccessTokenProvider' from assembly 'Microsoft.Graph.Core, Version=1.25.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. Stack trace: at Set-MgUserPhotoContent, C:\Program Files\WindowsPowerShell\Modules\Microsoft.Graph.Users\2.19.0\exports\ProxyCmdletDefinitions.ps1: line 52179 ↲ at , : line 223

    • Support

      Support

      Hello Stewart,

      For troubleshooting purposes, please send us the following at support@adaxes.com:

      • The script you are executing with all the modifications you made in TXT format.

      • A screenshot of the Multi-server environment dialog. The dialog displays how many Adaxes services you have and what their versions are. For information on how to view it, see https://www.adaxes.com/help/MultiServerEnvironment.

      • A list of PowerShell modules installed on all the computers where the Adaxes service runs. To get the list, execute the below script in Windows PowerShell running with elevated privileges (as administrator) on each computer where the Adaxes service runs. In the script, the $outputFilePath variable specifies the full path to the output file generated by the script.

      $outputFilePath = "C:\temp\InstalledModules.txt" #TODO: modify me
      Get-Module -ListAvailable | Out-File $outputFilePath
      

Got questions?

Support Questions & Answers

We use cookies to improve your experience.
By your continued use of this site you accept such use.
For more details please see our privacy policy and cookies policy.