How to upload user profile pictures in SharePoint 2010 using PowerShell

I’ve created a script for uploading user pictures to the SharePoint 2010 User Profile system.

For me it worked very well. But: Use it at your own risk!

Here is the script:

#written by ingo karstein
#region Check x64 host
    if( [System.IntPtr]::Size -ne 8) {
      Write-Error "Please use a x64 PowerShell host!"
      return
    }
#endregion

#region Load SharePoint SnapIn and DLL
  Remove-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
  Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

  [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")  | out-null
  Add-Type -AssemblyName System.DirectoryServices
  Add-Type -AssemblyName System.DirectoryServices.AccountManagement 

  #Check available SharePoint Cmdlets
  if( (Get-Command -Noun SPWeb*) -eq $null ) {
    Write-Error "SharePoint SnapIn not loaded. SharePoint cmdlets missing!"
    return
  }
#endregion

cls

#region Config Section
    $dom = "sharepoint.local"
    $dom2 = "dc=sharepoint, dc=local"
    $ad = @("ou=Users,dc=sharepoint,dc=local",
            "ou=Users2,dc=sharepoint,dc=local")
    $ldapFilter = "(&(objectcategory=user))"
    $testuser = $null
    #for testing purpose uncomment the following line and add the sAMAccountName of the test user
    #$testuser="ikarstein"

    #the web application url of the profile system
    $personalSiteHost="http://sharepoint.farm/mysitehost"
    #the intranet/homepage web application url
    $intranetSiteHost="http://sharepoint.farm"

    #the path to the user profile pictures. the files must be named like the sAMAccountName and with extension .jpg
    #  example: ikarstein.jpg
    $imagePath="\servershareuser_pictures"
#endregion

$ErrorActionPreference = "Stop"

#This function reads name of the folder where the pictures will be stored
function GetProfilePicturesFolderName {
  try {
    $a = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Portal")
    $name = $a.GetName()
    $assemblyRef = new-object System.Reflection.AssemblyName
    $assemblyRef.Name = "Microsoft.Office.Server.intl"
    $assemblyRef.Version = $name.Version
    $assemblyRef.SetPublicKey($name.GetPublicKey())
    $assemblyRef.CultureInfo = [System.Globalization.CultureInfo]::InvariantCulture
    $assembly = [System.Reflection.Assembly]::Load($assemblyRef)
    $s_rmLocStrings = new-object System.Resources.ResourceManager ("Microsoft.Office.Server.Strings", $assembly)
    $s_rmLocStrings.GetString("UserProfile_UploadPicture_FolderName")
  } catch {
    $null
  }
}

#This function reads name of the SharePoint list where the pictures will be stored
function GetProfilePicturesListName {
  try {
    $a = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Portal")
    $name = $a.GetName()
    $assemblyRef = new-object System.Reflection.AssemblyName
    $assemblyRef.Name = "Microsoft.Office.Server.intl"
    $assemblyRef.Version = $name.Version
    $assemblyRef.SetPublicKey($name.GetPublicKey())
    $assemblyRef.CultureInfo = [System.Globalization.CultureInfo]::InvariantCulture
    $assembly = [System.Reflection.Assembly]::Load($assemblyRef)
    $s_rmLocStrings = new-object System.Resources.ResourceManager ("Microsoft.Office.Server.Strings", $assembly)
    $s_rmLocStrings.GetString("MyPage_MyLists_ProfilePicture_Text")
  } catch {
    $null
  }
}

#This function returns the object for the profile picture list
function GetProfilePicturesList {
  try {
    $profilePicturesListName = GetProfilePicturesListName
    $profileWeb.Lists | ? { $_.get_BaseTemplate() -eq 0x6d -and $_.get_Title() -ieq $profilePicturesListName }
  } catch {
    $null
  }
}

#This function removes special characters from the file name  
function ConvertToLegalFileName($inputName, $replacementChar) {
    $l = $inputName.Length;
    $builder = New-Object System.Text.StringBuilder($inputName)
    for ($i = 0; $i -lt $l; $i++) {
        if ([Microsoft.SharePoint.Utilities.SPUrlUtility]::IsLegalCharInUrl($inputName[$i]) -ne $true)
        {
            $builder.set_chars($i, $replacementChar)
        }
    }
    return $builder.ToString()
}

#This function returns the sub folder for pictures in the picture list
function GetSubfolderForPictures {
  $folderName = GetProfilePicturesFolderName
  try {
      $subfolder = $profilePicturesList.get_RootFolder().Get_SubFolders().get_Item($folderName)
      if( $subfolder -eq $null ) {
        $subfolder = $profilePicturesList.get_RootFolder()
      }
  } catch {
    try {
        $subfolder = $profilePicturesList.get_RootFolder().get_SubFolders().Add($folderName)
    } catch {
        $subfolder = $null
    }
  }
  return $subfolder
}

#this function returns *multiple* objects:
# 1. the MethodInfo object for "CreateThumbnail"
# 2. the size of the large thumbnal picture
# 3. the size of the medium thumbnail picture
# 4. the size of the small thumbnail picture
function InitializeUserProfilePhotosType {
    $a = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles")
    $userPofilePhotosType = $a.GetType("Microsoft.Office.Server.UserProfiles.UserProfilePhotos")

    #outputs:
    $userPofilePhotosType.GetMethod("CreateThumbnail", [System.Reflection.BindingFlags]([System.Reflection.BindingFlags]::Static -bor  [System.Reflection.BindingFlags]::NonPublic))
    $userPofilePhotosType.InvokeMember("LargeThumbnailSize", [System.Reflection.BindingFlags]([System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::GetProperty  ), $null, $null, @() )
    $userPofilePhotosType.InvokeMember("MediumThumbnailSize", [System.Reflection.BindingFlags]([System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::GetProperty  ), $null, $null, @() )
    $userPofilePhotosType.InvokeMember("SmallThumbnailSize", [System.Reflection.BindingFlags]([System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::GetProperty  ), $null, $null, @() )
}

#this function returns a MethodInfo object for "EnsureTrailingSlash" method
function InitializePersonalSpaceGlobalType {
    $a = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles")
    $personalSpaceGlobalType = $a.GetType("Microsoft.Office.Server.WebControls.UserProfileHelper.PersonalSpaceGlobal")

    #outputs:
    $personalSpaceGlobalType.GetMethod("EnsureTrailingSlash", [System.Reflection.BindingFlags]([System.Reflection.BindingFlags]::Static -bor  [System.Reflection.BindingFlags]::NonPublic))
}

#thid function returns a MethodInfo object for "GetSmallThumbnailUrl" method
function InitializeUserProfileGlobalType {
    $a = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles")
    $userProfileGlobalType = $a.GetType("Microsoft.Office.Server.UserProfiles.UserProfileGlobal")

    #outputs:
    $userProfileGlobalType.GetMethod("GetSmallThumbnailUrl", [System.Reflection.BindingFlags]([System.Reflection.BindingFlags]::Static -bor  [System.Reflection.BindingFlags]::NonPublic))
}

#this function creates an absolute url for the given relative url
function MakeFullUrl {
  param($url)
  return "$($temp)$($Url)"
}

#this function reads the users from the Active Directory
function GetUsers {
    $ctype = [System.DirectoryServices.AccountManagement.ContextType]::Domain
    $context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $dom, $dom2

    $l = @()

    $ad | % { $domain = [ADSI]("LDAP://"+$_)
              $filter = $ldapFilter
              $ds = new-object System.DirectoryServices.DirectorySearcher($domain,$filter)
              $users = $ds.Findall()
              $users | % { $l = $l + $_ }
        }

    $identityParam = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName

    $m = @()

    if( $testuser -ne $null ) {
      Write-Host "Test mode!" -ForegroundColor Red
    }

    $l | % {
      $san = $_.properties["samaccountname"]
      if( $testuser -eq $null -or $testuser -ieq $san) {
        $m = $m + $san[0]
      }
    }

    #output:
    $m
}

$profileSite = (Get-SPSite $personalSiteHost)
$profileWeb = $profileSite.RootWeb

$site=(Get-SPSite $intranetSiteHost)
$sc = [Microsoft.Office.Server.ServerContext]::GetContext($site)

$upm = New-Object "Microsoft.Office.Server.UserProfiles.UserProfileManager" -ArgumentList ($sc)
$mysiteHostUrl = $upm.MySiteHostUrl

$ensureTrailingSlashMethod = (InitializePersonalSpaceGlobalType)
$mysiteHostUrl = $ensureTrailingSlashMethod.Invoke($null, @(, $mysiteHostUrl))

$createThumbnailMethod, $largeThumbnailSize, $mediumThumbnailSize, $smallThumbnailSize = (InitializeUserProfilePhotosType)
$getSmallThumbnailUrlMethod = (InitializeUserProfileGlobalType)

$profilePicturesList = GetProfilePicturesList
$profilePicturesFolder = GetSubfolderForPictures

$users=(GetUsers)

$users | % {
  $currentUser = $_
  Write-Host "Processing user $($currentUser)"
  $up=$null
  try {
    $up = $upm.GetUserProfile($currentUser)
  } catch {
    #this exception handler may be called for "renamend" users: In the AD the account name was changed but not in the SharePoint User Profil system
    $e=$upm.GetEnumerator()
    $e.reset()
    while( $e.movenext() ) {
      $u = $e.current
      if( $u["UserName"] -ieq $currentUser ) {
        $up = $u
        break
      }
    }
  }

  if( $up -ne $null ) {
    Write-Host "  User found in profile store"
    $picture = [byte[]](Get-Content -path "$($imagePath)$($currentUser).jpg" -Encoding Byte -ErrorAction SilentlyContinue)
    if( $picture -ne $null ) {
        Write-Host "  User picture found: ""$($imagePath)$($currentUser).jpg"""

        $dstFileName = (ConvertToLegalFileName $up["AccountName"].Value.ToString() "_")

        $flag = $profileWeb.get_AllowUnsafeUpdates()
        try
        {
          $profileWeb.set_AllowUnsafeUpdates($true)

          try
          {
            $stream = $bitmap = $ps = $null

            try {
              $stream = New-Object System.IO.MemoryStream @(, $picture)
              if( $stream -ne $null ) {
                  $bitmap = New-Object System.Drawing.Bitmap ($stream, $true)
                  if( $bitmap -ne $null ) {
                     Write-Host "  starting photo creation..."
                     $p = ([System.Drawing.Bitmap]$bitmap, $largeThumbnailSize, $largeThumbnailSize, [Microsoft.SharePoint.SPFolder]$profilePicturesFolder, "$($dstFileName)_LThumb.jpg")
                     $file = $createThumbnailMethod.Invoke($null, $p )
                     Write-Host "    large thumnail created..."
                     $p = ([System.Drawing.Bitmap]$bitmap, $smallThumbnailSize, $smallThumbnailSize, [Microsoft.SharePoint.SPFolder]$profilePicturesFolder, "$($dstFileName)_SThumb.jpg")
                     $file = $createThumbnailMethod.Invoke($null, $p )
                     Write-Host "    small thumnail created..."
                     $p = ([System.Drawing.Bitmap]$bitmap, $mediumThumbnailSize, $mediumThumbnailSize, [Microsoft.SharePoint.SPFolder]$profilePicturesFolder, "$($dstFileName)_MThumb.jpg")
                     $file = $createThumbnailMethod.Invoke($null, $p )
                     Write-Host "    medium thumnail created..."

                     $mediumUrl = $profileSite.MakeFullUrl( (MakeFullUrl $file.get_Url()) )

                     $smallThumbUrl = $getSmallThumbnailUrlMethod.Invoke($null, ([string]$mediumUrl))

                     [Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges( {
                         $ps = $up.PersonalSite
                         if( $ps -ne $null ) {
                             $ps.get_RootWeb().set_SiteLogoUrl($smallThumbUrl)
                             $ps.get_RootWeb().Update()
                         }

                         $up["PictureURL"].Value = $mediumUrl
                         $up.Commit()
                     } );
                  }
              }
            } catch {
              Write-Host "    error occured!"
              Write-Error $Error[0]
            } finally {
              if( $bitmap -ne $null ) {
                $bitmap.dispose()
              }
              if( $stream -ne $null ) {
                $stream.close()
                $stream.dispose()
              }
            }
          } catch {
          }
        } finally{
          $profileWeb.set_AllowUnsafeUpdates($flag)
        }
    } else {
      Write-Host "  Users picture not found" -ForegroundColor Red
    }
  } else {
    Write-Host "  User not found in profile store" -ForegroundColor Red
  }
}

8 thoughts on “How to upload user profile pictures in SharePoint 2010 using PowerShell

    • Shorter: yes! – Cleaner: yes! – BUT… My solution doest the picture upload the same way as the SharePoint itself does this job!!! Your solution does not upload pictures to the dedicated pictures list and it does not create 3 versions of the picture: large, medium and small, e.g. used by “Organization Browser”.

      • My stores the pictures in the picture library and creates the 3 resized images for use within the org browser, outlook etc. That is what the command below does.
        Update-SPProfilePhotoStore –MySiteHostLocation “http:///”

        Did you try it and have trouble getting it to work?

  1. Pingback: Uploading user profile picture in SharePoint 2010 « Virtualize SharePoint, SharePoint Virtualization, Virtualization Tips

  2. Very very good script!!!!

    But i found a small issue with following two functions “GetProfilePicturesFolderName” and “GetProfilePicturesListName”. These returns in my case not the correct values. I think the reason is, that i installed SharePoint in German with English language packs. Our MySite is provisioned in english language. Therefor my list called “User Photos” and my Picture Folder “Profile Pictures”. But unfortunately your functions returns the german values 😉

  3. Pingback: Sharepoint 2010 Mass Uploading profile pictures programatically «sreesp sreesp

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.