Script repository
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
If there is no picture in AD but there is currently a picture in Office365, will this overwrite the Office365 picture?
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
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
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
Is there a script for the opposite of this script? We would like to sync 365 avatars to Adaxes.
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?
Jason
I would like to do this. Our users have uploaded their own photos in 365 and we would like them to be visible in AD/Adaxes.
Support
Hello Jason,
Have a look at the following script from our repository: https://www.adaxes.com/script-repository/set-user-photo-from-microsoft-365-in-ad-s581.htm.
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
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
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.
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
Hello Paul,
Unfortunately, this is a known issue. Upgrading to the latest version is exactly the way to resolve the issue.
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
Support
Hello Neil,
Please, specify whether you have your Microsoft 365 tenant registered in Adaxes with the credentials of a user account or an Entra app. The following article will help check it: https://www.adaxes.com/help/ChangeTenantServiceAccount.
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
Hello Boris,
For troubleshooting purposes, please, send us the following at support@adaxes.com:
The script you are using in TXT format with all the modifications you made.
A screenshot of the error you are facing.
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 screenshot of the custom command, business rule or scheduled task executing the script.
Any additional details will be much appreciated.
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
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.