May 24 2019 10:56 AM
Good Afternoon All,
I wanted to share a script I came up with to help keep our active directory profile photos in sync with Office 365 and SharePoint Online, but first a little background on WHY I did this and how the script works along with a few caveats. I attached the script to this post, the code in the post is only for reference.
Good Luck,
Andrew
Background:
For some unknown reason, user profile photos were missing from Delve and SharePoint Online. They may have removed them on their own, but they still existed in our on-prem AD and we wanted to make sure they were in sync. We are all aware that the photo sync process is messy and rarely works well. One issue is that the photo sync only works on the initial sync of the photo, if we update an AD photo the changed photo never reflected in SharePoint (unsure if this changed yet). An admin could use the Set-UserPhoto cmd to update the photo but it would take anywhere from 24-72 hours to update in SharePoint, if it even did it at all. I create this script to synchronize the pictures from our Active Directory on prem to SPO and EXO (this covers most of the services we use atm that pull the profile picture).
How it works:
Caveats:
function Resize-Image
{
<#
.SYNOPSIS
Resize-Image resizes an image file
.DESCRIPTION
This function uses the native .NET API to resize an image file, and optionally save it to a file or display it on the screen. You can specify a scale or a new resolution for the new image.
It supports the following image formats: BMP, GIF, JPEG, PNG, TIFF
.EXAMPLE
Resize-Image -InputFile "C:\kitten.jpg" -Display
Resize the image by 50% and display it on the screen.
.EXAMPLE
Resize-Image -InputFile "C:\kitten.jpg" -Width 200 -Height 400 -Display
Resize the image to a specific size and display it on the screen.
.EXAMPLE
Resize-Image -InputFile "C:\kitten.jpg" -Scale 30 -OutputFile "C:\kitten2.jpg"
Resize the image to 30% of its original size and save it to a new file.
.LINK
Author: Patrick Lambert - http://dendory.net
#>
Param([Parameter(Mandatory=$true)][string]$InputFile, [string]$OutputFile, [int32]$Width, [int32]$Height, [int32]$Scale, [Switch]$Display)
# Add System.Drawing assembly
Add-Type -AssemblyName System.Drawing
# Open image file
$img = [System.Drawing.Image]::FromFile((Get-Item $InputFile))
# Define new resolution
if($Width -gt 0) { [int32]$new_width = $Width }
elseif($Scale -gt 0) { [int32]$new_width = $img.Width * ($Scale / 100) }
else { [int32]$new_width = $img.Width / 2 }
if($Height -gt 0) { [int32]$new_height = $Height }
elseif($Scale -gt 0) { [int32]$new_height = $img.Height * ($Scale / 100) }
else { [int32]$new_height = $img.Height / 2 }
# Create empty canvas for the new image
$img2 = New-Object System.Drawing.Bitmap($new_width, $new_height)
# Draw new image on the empty canvas
$graph = [System.Drawing.Graphics]::FromImage($img2)
$graph.DrawImage($img, 0, 0, $new_width, $new_height)
# Create window to display the new image
if($Display)
{
Add-Type -AssemblyName System.Windows.Forms
$win = New-Object Windows.Forms.Form
$box = New-Object Windows.Forms.PictureBox
$box.Width = $new_width
$box.Height = $new_height
$box.Image = $img2
$win.Controls.Add($box)
$win.AutoSize = $true
$win.ShowDialog()
}
# Save the image
if($OutputFile -ne "")
{
$img2.Save($OutputFile);
}
}
# Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.UserProfiles.dll"
# Connect to AD
Import-Module ActiveDirectory
# Connect to EXO
$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/?proxymethod=rps -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking
# Connect to SPO
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserCredential.Username, $UserCredential.Password)
# Bind to SPO site collection and Library
$SiteURL = "https://YOURTENANT-my.sharepoint.com/"
$DocLibName = "User Photos"
$SubFolderName = "Profile Pictures"
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Context.Credentials = $Credentials
$List = $Context.Web.Lists.GetByTitle($DocLibName)
$FolderToBindTo = $List.RootFolder.Folders
$Context.Load($List)
$Context.Load($FolderToBindTo)
$Context.ExecuteQuery()
# Get all the employees with AD Pictures
$user = Get-ADUser -filter * -Properties * -SearchBase 'DC=yourdomain,DC=com' | Select UserPrincipalName, thumbnailPhoto | Where-Object {$_.thumbnailPhoto -ne $NULL}
# Export each users AD Pictures
Write-Host "`nExporting User AD Pictures"
foreach ($line in $user) {
$upn = $line.UserPrincipalName
Write-Host "`nProcessing" $line.UserPrincipalName -ForegroundColor Green
$FileName = "C:\scripts\Pics\"+$line.UserPrincipalName+".jpg"
Write-Host "Exporting AD Picture to" $FileName -ForegroundColor Yellow
[System.Io.File]::WriteAllBytes($Filename, $line.thumbnailphoto)
Write-Host "Creating SharePoint Photo Folder" -ForegroundColor Yellow
New-Item -Path "C:\Scripts\Pics\" -Name $line.UserPrincipalName -ItemType "directory" -Force | Out-Null
$FormattedName = $line.UserPrincipalName -replace "\.","_" -replace "@","_"
Write-Host "Resizing Image for SharePoint" -ForegroundColor Yellow
$NewFilePath = "C:\Scripts\Pics\" + $line.UserPrincipalName + "\" + $FormattedName + "_SThumb.jpg"
Resize-Image -InputFile $FileName -Width 48 -Height 48 -Outputfile $NewFilePath
Write-Host "Small Thumbnail Created" -ForegroundColor Cyan
$NewFilePath = "C:\Scripts\Pics\" + $line.UserPrincipalName + "\" + $FormattedName + "_MThumb.jpg"
Resize-Image -InputFile $FileName -Width 72 -Height 72 -Outputfile $NewFilePath
Write-Host "Medium Thumbnail Created" -ForegroundColor Cyan
$NewFilePath = "C:\Scripts\Pics\" + $line.UserPrincipalName + "\" + $FormattedName + "_LThumb.jpg"
Resize-Image -InputFile $FileName -Width 96 -Height 96 -Outputfile $NewFilePath
Write-Host "Large Thumbnail Created" -ForegroundColor Cyan
}
# Set each users 365 Photo using their exported AD photo
Write-Host "`nSetting User 365 Photos"
foreach ($line in $user) {
Write-Host "`nProcessing" $line.UserPrincipalName -ForegroundColor Green
Write-Host "Setting User Photo for" $line.UserPrincipalName -ForegroundColor yellow
Set-UserPhoto -Identity $line.UserPrincipalName -PictureData ([System.IO.File]::ReadAllBytes("C:\scripts\Pics\"+$line.UserPrincipalName+".jpg")) -Confirm:$false
write-host "Operation complete for" $line.UserPrincipalName -ForegroundColor green
# Upload file to SPO
Write-Host "Uploading pictures to SharePoint" -ForegroundColor Yellow
$Folder = "C:\Scripts\Pics\" + $line.UserPrincipalName
Write-Host "Source Picture Path is" $Folder -ForegroundColor Yellow
$FolderToUpload = $FolderToBindTo | Where {$_.Name -eq $SubFolderName}
Foreach($File in (dir $Folder -File))
{
$FileStream = New-Object IO.FileStream($File.FullName, [System.IO.FileMode]::Open)
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.ContentStream = $FileStream
$FileCreationInfo.URL = $File
$Upload = $FolderToUpload.Files.Add($FileCreationInfo)
$Context.Load($Upload)
$Context.ExecuteQuery()
}
Write-Host "SharePoint Picture Upload Complete" -ForegroundColor Cyan
Write-Host $line.UserPrincipalName "Photo Sync Complete" -ForegroundColor Green
}
#Close the EXO Session
Remove-PSSession $Session
Jun 11 2020 02:24 AM
After contacting Microsoft support I received the following feedback:
the user can log into o365 and browse to the following url to initiate the sync
https://yoursite.sharepoint.com/_layouts/15/userphoto.aspx?size=M&username=user@yourdomain
So I wrote some javascript code and included this on our main home page of our Sharepoint site.
<script type="text/javascript">
var loginName = _spPageContextInfo.userLoginName;
var url = "https://yoursite.sharepoint.com/_layouts/15/userphoto.aspx?size=M&username="+encodeURIComponent(loginName);
//alert(url);
document.writeln ("<img src='" + url + "' width='10'/>")
</script>
After this, the user profile pictures started to sync in our Sharepoint environment.
Since I have struggled with this issue many hours, days even, I thought it would be a good idea to share this. I hope someone will benefit from this solution.