Script Repository


Upload user photo to Microsoft 365

April 30, 2020
4773

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.

The 1st script uploads the photo stored in AD in the Picture (thumbnailPhoto) attribute, and the 2nd one uploads a photo from a file. When uploading from a file, the image will be optimized for the best viewing quality in Microsoft 365 (Office 365) (648x648 pixels, best JPEG compression quality).

To upload pictures to Microsoft 365 (Office 365), you can create a Scheduled Task that updates user photos on a regular basis. If you are uploading the AD photo, you can also create a Business Rule that runs the 1st script after updating the Picture attribute.

Sample Business Rule


1. Upload Image Stored in AD Attribute Picture (thumbnailPhoto)

Edit Remove
PowerShell
# Get user ID in Microsoft 365
try
{
    $objectId = [Guid]$Context.TargetObject.Get("adm-O365ObjectId")
}
catch
{
    return
}

# Get the photo
try
{
    $pictureBytes = $Context.TargetObject.Get("thumbnailPhoto")
}
catch
{
    $Context.LogMessage("The user doesn't have a photo", "Warning")
    return
}

try
{
    # Connect to Exchange Online
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell/?proxymethod=rps" `
    -Credential $Context.GetOffice365Credential() -Authentication Basic -AllowRedirection -WarningAction SilentlyContinue

    Import-PSSession $session -AllowClobber -DisableNameChecking
    
    # Update the user's photo
    Set-UserPhoto $objectId.ToString() -PictureData $pictureBytes -Confirm:$False
}
finally
{
    # Release resources
    Remove-PSSession $session
}

2. Upload Image from File

Parameter:

  • $picturePath - specifies a path to the picture file. You can use value references in it (e.g. %username%). When the script is executed, they will be replaced with property values of the target user. For example, if you specify the following path: \\SERVER\share\%username%.jpg, and the script is executed on a user with username jdoe, the file path will be \\SERVER\share\jdoe.jpg.
Edit Remove
PowerShell
$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

try
{
    # Connect to Exchange Online
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell/?proxymethod=rps" `
    -Credential $Context.GetOffice365Credential() -Authentication Basic -AllowRedirection -WarningAction SilentlyContinue

    Import-PSSession $session -AllowClobber -DisableNameChecking -CommandName Set-UserPhoto
    
    # Update the user's photo
    Set-UserPhoto $objectId.ToString() -PictureData $pictureBytes -Confirm:$False
}
finally
{
    # Release resources
    Remove-PSSession $session
}

Comments ( 4 )
avatar
MG
January 15, 2020

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

avatar
Support
January 15, 2020

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.

avatar
Ethan
June 16, 2020

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

avatar
Support
June 17, 2020

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.

Leave a comment