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
  }
}