The scripts upload a user 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.
1. Upload image stored in AD attribute
The script uploads a user photo stored in the specified property to Microsoft 365. In the script, the $propertyName variable specifies the LDAP name of the property from which to obtain user photo.
$propertyName = "thumbnailPhoto" # TODO: modify me
# Get user ID in Microsoft 365
try
{
$objectId = [Guid]$Context.TargetObject.Get("adm-O365ObjectId")
}
catch
{
return
}
# Get the photo
try
{
$pictureBytes = $Context.TargetObject.Get($propertyName)
}
catch
{
$Context.LogMessage("User %fullname% has no photo in property $propertyName.", "Warning")
return
}
# Connect to Exchange Online
$Context.CloudServices.ConnectExchangeOnline()
# Update the user's photo
Set-UserPhoto $objectId.ToString() -PictureData $pictureBytes -Confirm:$False
2. Upload image stored in AD attribute in bulk
The script uploads photos for multiple users located in a specific OU/container from an AD property to Microsoft 365. To execute the script, create a custom command or scheduled task configured for the Organizational Unit or Container object type. In the script, the $propertyName variable specifies the LDAP name of the property from which to obtain user photos.
$propertyName = "thumbnailPhoto" # TODO: modify me
# Search users
$searcher = $Context.TargetObject
$searcher.SearchFilter = "(&(sAMAccountType=805306368)($propertyName=*))"
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
$searcher.PageSize = 500
$searcher.SetPropertiesToLoad(@("adm-O365ObjectId", $propertyName))
try
{
# Execute search
$searchResultIterator = $searcher.ExecuteSearch()
$searchResults = $searchResultIterator.FetchAll()
# Connect to Exchange Online
$Context.CloudServices.ConnectExchangeOnline()
foreach ($searchResult in $searchResults)
{
if (!$searchResult.ContainsProperty("adm-O365ObjectId"))
{
continue
}
# Get user ID in Microsoft 365
$objectId = [Guid]$searchResult.Properties["adm-O365ObjectId"].Value
# Get user photo
$pictureBytes = $searchResult.Properties[$propertyName].Value
# Update user photo in Microsoft 365
Set-UserPhoto $objectId.ToString() -PictureData $pictureBytes -Confirm:$False
}
}
finally
{
# Release resources
if ($searchResultIterator){ $searchResultIterator.Dispose() }
}
3. Upload image from file
The script uploads a photo to Microsoft 365 from a file. The image will be optimized for the best viewing quality in Microsoft 365 (Office 365) (648x648 pixels, best JPEG compression quality).
Parameter:
- $picturePath - Specifies a path to the image file. You can use value references in the path (e.g. %username%). When the script is executed, they will be replaced with corresponding property values of the target user.
$picturePath = "\\SERVER\share\%username%.jpg" # TODO: modify me
function ResizePhotoForMicrosoft365 ($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)
# Set encoder settings for image quality
$encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
$encoder = [System.Drawing.Imaging.Encoder]::Quality
$encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($encoder, 100)
# Save file
$tmpFilePath = [System.IO.Path]::GetTempFileName()
$imageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | where {$_.MimeType -eq 'image/jpeg'}
$newPicture.Save($tmpFilePath, $imageCodecInfo, $($encoderParams))
[Byte[]]$pictureBytes = Get-Content -Path $tmpFilePath -Encoding Byte
[System.IO.File]::Delete($tmpFilePath)
return ,$pictureBytes
}
finally
{
# Release resources
if ($original) { $original.Dispose() }
if ($graph) { $graph.Dispose() }
if ($newPicture) { $newPicture.Dispose() }
}
}
# Get user ID in Microsoft 365
try
{
$objectId = [Guid]$Context.TargetObject.Get("adm-O365ObjectId")
}
catch
{
return
}
# Get the photo
if (-not(Test-Path -Path $picturePath))
{
$Context.LogMessage("File '$picturePath' does not exist.", "Warning")
return
}
$pictureBytes = ResizePhotoForMicrosoft365 $picturePath
# Connect to Exchange Online
$Context.CloudServices.ConnectExchangeOnline()
# Update the user's photo
Set-UserPhoto $objectId.ToString() -PictureData $pictureBytes -Confirm:$False
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.
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.
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?
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.
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 <ScriptBlock>, <No file>: line 17
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.