This Powershell script allows you to synchronize an Active Directory custom attribute with the SharePoint 2010 user profile service application PictureUrl property. Usefull for companies that store picture url information in a custom attribute and want to replicate that information into SharePoint 2010. Normally this should be feasible by customizing the ForeFront Identity Manager used by the SharePoint 2010 User Profile Synchronization service, but this is not supported. The script can be easily customized to use a different extension attribute in Active Directory

Use this script in combination with a scheduled task on one of the SharePoint servers in the farm.

 

PowerShell
Edit|Remove
#Define Variables 
 [bool]$Verbose = $false 
 [int]$GLOBAL:TotalUsersUpdated = 0 
 [int]$GLOBAL:TotalUsersWithoutPicture = 0 
 [int]$GLOBAL:TotalUsersNoUpdateNeeded = 0 
 [int]$GLOBAL:TotalUsersProcessed = 0 
 [string]$usage = "Usage: Sync-UserProfilePicture-with-AD-Attribute"  
[string]$webUrl = "http://mysites.contoso.com/" 
 [string]$domain = "LDAP://DC=CONTOSO,DC=COM" 
  
#Function to find user in Active Directory based on sAMAccountName 
 function Get-Users-From-ActiveDirectory([string]$domaincnx, [string]$Userlogin) 
 { 
     [object]$TempUser = $null 
     #Filter on User which only exists 
     $strFilter = "(&(objectCategory=user)(objectClass=user)(samAccountName="+ $Userlogin +"))" 
     $objDomain = New-Object System.DirectoryServices.DirectoryEntry($domaincnx) 
  
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher 
     $objSearcher.SearchRoot = $objDomain 
     $objSearcher.PageSize = 10000 
     $objSearcher.Filter = $strFilter 
     $objSearcher.SearchScope = "Subtree" 
  
    $colProplist = "name","samaccountname","objectsid","displayname","extensionAttribute13" 
     foreach ($i in $colPropList) 
     { 
         $objSearcher.PropertiesToLoad.Add($i)|out-null 
     } 
  
    $colResults = $objSearcher.FindAll() 
  
    if($colResults.Count -eq 0) 
     { 
         return $null 
     } 
     if($colResults.Count -gt 0) 
     { 
         # Write-host $colResults.Count," Users found in AD"  
        foreach($user in $colResults) 
         { 
             $TempUser = $user 
         } 
     } 
     return $TempUser 
 } 
  
function StartProcess() 
 { 
     # Create the stopwatch 
     [System.Diagnostics.Stopwatch] $sw; 
     $sw = New-Object System.Diagnostics.StopWatch 
     $sw.Start() 
      
    $mydomaincnx = $domain 
      
    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") > $null 
     #[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server") > $null 
     #[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles") > $null 
  
    $site = new-object Microsoft.SharePoint.SPSite($webUrl) 
      
    if ($site -eq $null) 
     { 
         Write-Host "Cannot get the site collection corresponding to $webUrl" -foregroundcolor red -backgroundcolor yellow 
         Write-Host "Check the Url and try again" -foregroundcolor red -backgroundcolor yellow 
         exit    
    } 
     $context = Get-SPServiceContext($site) 
   
     $profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context) 
  if ($profileManager -eq $null) 
     { 
         Write-Host "Cannot connect to the User Profile Service Application" -foregroundcolor red -backgroundcolor yellow 
         Write-Host "Check the Service and Permissions and try again" -foregroundcolor red -backgroundcolor yellow 
         exit    
    } 
      
 $count = $profileManager.Count 
  write-host "Profile Items to process: "$count 
  $i = 0 
   
  $profiles = $profileManager.GetEnumerator() 
   foreach ($userProfile in $profiles)  
 { 
    $i+=1 
       $GLOBAL:TotalUsersProcessed+=1 
    $UserName = $userProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::AccountName].Value 
    $Tablename = $UserName.split("\") 
       if($Verbose) { Write-Host "[$i/$count]SharePoint User: ",$UserName," --> AD User: " -nonewline } 
       [object]$myUserAD = Get-Users-From-ActiveDirectory $mydomaincnx $Tablename[1] 
       if ($myUserAD -ne $null) 
       { 
      [string]$UserPicture = $myUserAD.Properties.extensionattribute13 
         if($Verbose)  
        {  
            Write-Host "user found" -foregroundcolor green  
            Write-Host "  [AD-UserPicture] = "$UserPicture 
         } 
       
        if ($UserPicture -ne "")     
     { 
     #read the PictureUrl value from the User Profile 
           [string]$userProfilePictureUrl = $userProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PictureURL].Value 
           if($Verbose)  
          {  
               Write-Host "  [SPS-ProfilePicture] = "$userProfilePictureUrl 
           } 
        #check if value is different than the one on the User Profile 
           if ($userProfilePictureUrl -ne $UserPicture) 
           { 
               $userProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PictureURL].Value = $UserPicture 
         $userProfile.Commit() 
         if($Verbose) { Write-Host "  --> PictureURL = ",$UserPicture -foregroundcolor green } 
         $GLOBAL:TotalUsersUpdated += 1 
           } 
           else 
           { 
               if($Verbose) { Write-Host "  --> PictureURL already set" -foregroundcolor yellow } 
               $GLOBAL:TotalUsersNoUpdateNeeded += 1 
           } 
         } 
         else 
      { 
     if($Verbose) { Write-Host "  --> PictureURL = empty" -foregroundcolor red } 
           $GLOBAL:TotalUsersWithoutPicture += 1 
         } 
         if($Verbose -eq $false) { Write-Progress -id 1 -activity "Updating User Profiles" -status "Percent updated: " -percentComplete (($i / $count)  * 100) } 
    } 
  } 
  $site.Dispose() 
  
    write-host $GLOBAL:TotalUsersUpdated, " users updated" 
     write-host $GLOBAL:TotalUsersNoUpdateNeeded, " users without update required" 
     write-host $GLOBAL:TotalUsersWithoutPicture, " Users without Picture in Active Directory" 
   
     $sw.Stop() 
     # Write the compact output to the screen 
     write-host $GLOBAL:TotalUsersProcessed " Profiles processed in Time: "$sw.Elapsed.ToString() 
      
 } 
  
# START SCRIPT 
 cls 
 StartProcess