Forum Discussion

Robert Woods's avatar
Robert Woods
Iron Contributor
Oct 20, 2016

SharePoint Online User Photo Sync

We are running Ex2010 Hybrid scenario with Office 365, using SharePoint Online only. No hybrid for SP. We are on the latest version of AAD Sync. When setting user photos we use the commands below. 

 

$user = 'blah.blah@blah.com'
$userphoto = "\\someFileShare\blah_blah.jpg"
Set-UserPhoto -Identity $user -PictureData ([System.IO.File]::ReadAllBytes($userphoto)) -Confirm:$false

Photos show up in AD/EXOL No Problem. Photos do not sync up to delve or SharePoint.

 

Are we using the right commands for this? Is there another command to run to ensure the same photo is uploaded to Delve/SharePoint??

 

Any help would be appreciated. 

11 Replies

  • I had a customer who required a similar solution. They have an investment in GApps and (for now) are only using SPO, so no hybrid in this case. The customer needed a solution for populating custom UPSA props from Active Directory as well as upload user photos. Ended up using the UPA PnP sample and extending it heavily to support various custom props, work with how they structured their photos today on-prem, and other requirements like make it a Windows Service leveraging the Event Log. It's a bit convoluted compared to just being able to use something like AAD Sync to grab photos from thumbnailPhoto or a chosen attribute and get them to AAD for eventual consumption by the service. That said, since Exchange [Online] is the source of truth for high quality pictures, there's not a great story here.
  • We had to come up with a script to manually push them through to SharePoint because of this bug, as it was inconsistent.
    • Deleted's avatar
      Deleted
      Same here. This is one of those things you'd assume works, but doesn't. Ultimately the script works, but we shouldn't have to depend on it.
      • AdamHarmetz's avatar
        AdamHarmetz
        Icon for Microsoft rankMicrosoft

        I have an internal thread with the owners of this component and have asked them to weigh in on this thread.

         

        Thanks,

        Adam

    • SanthoshB1's avatar
      SanthoshB1
      Bronze Contributor

      Following PowerShell script is used to instantly process and display profile picture in Exchange Online and SharePoint Online using “Set-UserPhoto” cmdlet and CSOM.

       

      Before executing the following script, you need to modify the input parameters in the script with your own values as follows,

       

      Input Parameters:

      $siteUrl= "https://tenantname-my.sharepoint.com/"

      $username= "admin@tenantname.onmicrosoft.com"

      $password= "xxxxxxxx"

      $folderpath= "E:\photo"

      $SPOAdminPortalUrl= “https://tenantname-admin.sharepoint.com/”

       

      #Add references to SharePoint client assemblies and authenticate to Office 365 site – required for CSOM
      Add-Type -Path ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client").location)
      Add-Type -Path ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.runtime").location)
      Add-Type -Path ([System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles").location)
      Add-Type -AssemblyName System.Drawing
      
      Function UpdateUserProfile()
      {
      Param(
        [Parameter(Mandatory=$True)]
        [String]$targetAcc,
      
        [Parameter(Mandatory=$True)]
        [String]$PropertyName,
      
        [Parameter(Mandatory=$False)]
        [String]$Value, 
      
        [Parameter(Mandatory=$True)]
        [String]$SPOAdminPortalUrl,
      
        [Parameter(Mandatory=$True)]
        [System.Net.ICredentials]$Creds
      
      )
      $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SPOAdminPortalUrl)
      $ctx.Credentials = $Creds
      $peopleManager = New-Object Microsoft.SharePoint.Client.UserProfiles.PeopleManager($ctx)
      $targetAccount = ("i:0#.f|membership|" + $targetAcc)
      $peopleManager.SetSingleValueProfileProperty($targetAccount, $PropertyName, $Value)
      $ctx.ExecuteQuery()
      }
      
      Function UploadImage ()
      {
      Param(
        [Parameter(Mandatory=$True)]
        [String]$SiteURL,
      
        [Parameter(Mandatory=$True)]
        [String]$User,
      
        [Parameter(Mandatory=$False)]
        [String]$Password, 
      
        [Parameter(Mandatory=$True)]
        [String]$Folder,
      
        [Parameter(Mandatory=$True)]
        [String]$SPOAdminPortalUrl
      
      )
      
      #Defualt Image library and Folder value 
      $DocLibName ="User Photos"
      $foldername="Profile Pictures"
      
      #Connect Exchange online 
      
      $secstr = New-Object -TypeName System.Security.SecureString
      $password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
      $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $User, $secstr
      $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/?proxyMethod=RPS -Credential $cred -Authentication Basic -AllowRedirection
      Import-PSSession $Session
      
      $Securepass = ConvertTo-SecureString $Password -AsPlainText -Force
      $Creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($User,$Securepass)
      
      #Bind to site collection
      $Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
      $Context.Credentials = $Creds
      
      #Retrieve list
      $List = $Context.Web.Lists.GetByTitle($DocLibName)
      $Context.Load($List)
      $Context.Load($List.RootFolder)
      $Context.ExecuteQuery()
      $ServerRelativeUrlOfRootFolder = $List.RootFolder.ServerRelativeUrl
      
      $uploadFolderUrl=  $ServerRelativeUrlOfRootFolder+"/"+$foldername
      $spoimagename = @{"_SThumb" = "48"; "_MThumb" = "72"; "_LThumb" = "200"}
      #Upload file
      Foreach ($File in (dir $Folder -File))
      {
      
      #Upload image into Exchange online 
      $PictureData = $File.FullName
      $Identity=$File.BaseName
      Set-UserPhoto -Identity $Identity -PictureData ([System.IO.File]::ReadAllBytes($PictureData)) -Confirm:$false
      Write-Host "Exchange online image uploaded successful for" $Identity  
      
      $username=$File.BaseName.Replace("@", "_").Replace(".", "_")
      $Extension = $File.Extension
      Foreach($imagename in $spoimagename.GetEnumerator())
      {
      #Covert image into different size of image
      $img = [System.Drawing.Image]::FromFile((Get-Item $PictureData))
      [int32]$new_width = $imagename.Value
      [int32]$new_height = $imagename.Value
      $img2 = New-Object System.Drawing.Bitmap($new_width, $new_height)
      $graph = [System.Drawing.Graphics]::FromImage($img2)
      $graph.DrawImage($img, 0, 0, $new_width, $new_height)
      
      #Covert image into memory stream
      $stream = New-Object -TypeName System.IO.MemoryStream
      $format = [System.Drawing.Imaging.ImageFormat]::Jpeg
      $img2.Save($stream, $format)
      $streamseek=$stream.Seek(0, [System.IO.SeekOrigin]::Begin)
      
      #Upload image into sharepoint online
      $FullFilename=$username+$imagename.Name+$Extension
      $ImageRelativeURL="/"+$DocLibName+"/"+$foldername+"/"+$FullFilename
      $ModifiedRelativeURL=$ImageRelativeURL.Replace(" ","%20")
      [Microsoft.SharePoint.Client.File]::SaveBinaryDirect($Context,$ModifiedRelativeURL, $stream, $true)
      
      }
      Write-Host "SharePoint online image uploaded successful for" $Identity 
      #Change user Profile Property in Sharepoint onlne 
      $PictureURL=$SiteURL+$DocLibName+"/"+$foldername+"/"+$username+"_MThumb"+$Extension
      
      UpdateUserProfile -targetAcc $Identity  -PropertyName PictureURL  -Value $PictureURL -SPOAdminPortalUrl $SPOAdminPortalUrl -Creds $Creds
      
      UpdateUserProfile -targetAcc $Identity  -PropertyName SPS-PicturePlaceholderState -Value 0 -SPOAdminPortalUrl $SPOAdminPortalUrl -Creds $Creds
      
      UpdateUserProfile -targetAcc $Identity  -PropertyName SPS-PictureExchangeSyncState -Value 0 -SPOAdminPortalUrl $SPOAdminPortalUrl -Creds $Creds
      
      UpdateUserProfile -targetAcc $Identity  -PropertyName SPS-PictureTimestamp  -Value 63605901091 -SPOAdminPortalUrl $SPOAdminPortalUrl -Creds $Creds
      
      Write-Host "Image processed successfully and ready to display for" $Identity
      }
      
      }
      #Input parameter
      $siteUrl="https://tenantname-my.sharepoint.com/"
      $username= "admin@tenantname.onmicrosoft.com" 
      $password= "pass@word1"
      $folderpath= "E:\photo"
      $SPOAdminPortalUrl = "https://tenantname-admin.sharepoint.com/"
      
      uploadimage -SiteURL $siteUrl -User $username -Password $password -Folder $folderpath -SPOAdminPortalUrl $SPOAdminPortalUrl 
  • Robert Woods's avatar
    Robert Woods
    Iron Contributor

    AdamHarmetz Could you get the appropriate person to help with this envolved? It seems this is a big issue for others as well after doing further research on it. 

     

    TonyRedmond Does a great job outlining the issue further in his blog post

Resources