Wednesday, 1 December 2010

Upload multiple user profile pictures/photos to SharePoint using PowerShell

This is one of those scripts that has been a while in the making, and it was only when a colleague of mine pointed something out today (thanks Rob!) that I realised where I was going wrong – more on that later.

In summary, the script performs the following tasks:

  • Uploads multiple image files from a local or network folder into the “User Photos” document library of the My Site host site. You could leave this part of the script out and upload them directly to this library using the SharePoint UI if you wanted to.
  • Configures the user profile for users associated with each image file. The profile is initially set to reference the image file uploaded to the “User Photos” document library.
  • Runs the Update-SPProfilePhotoStore cmdlet included in SharePoint 2010. This will create the three thumbnail images in the “Profile Pictures” folder of the “User Photos” document library and change each user profile to reference the medium sized thumbnail image. I have decided to leave the original image file in the root of the “User Photos” library for central storage and management.

There are a couple of pre-requisites to note here:

  • The image files must be in the format domainname_username.extension for the script to work properly. For example, a .jpg file for the user Phil.Childs in domain TESTDOMAIN will have to be named testdomain_phil.childs.jpg. This is important, as the script will use the file name to establish which user the image file relates to and update the user profile accordingly.
  • Ensure you have full permissions to manage the user profile service application. The user running the script (which I assume is some sort of SharePoint Administrator) must have been granted the Manage Profiles right in the Administrators section and Full Control rights in the Permissions section of the User Profile Service Application (pictured below).

image

Before running the script, I created a C:\Install\Photos folder containing my images on the local hard drive of the SharePoint server:

image

I then run the PowerShell script below – Note that running this script this will just set up the function in PowerShell and will not actually do anything until I call it with a command later on:

function Upload-PhotosToSP
{
    Param (
           [parameter(Mandatory=$true)][string]$LocalPath,
           [parameter(Mandatory=$true)][string]$MySiteUrl,
           [parameter(Mandatory=$false)][switch]$Overwrite
           )
   
    #Get site, web and profile manager objects
    $mySiteHostSite = Get-SPSite $MySiteUrl
    $mySiteHostWeb = $mySiteHostSite.OpenWeb()
    $context = Get-SPServiceContext $mySiteHostSite
    $profileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
    try
    {  
        #Get files from local folder
        $localPhotosFolder = Get-ChildItem $LocalPath
        #Get User Photos document library in the My Site host site
        $spPhotosFolder = $mySiteHostWeb.GetFolder("User Photos")
       
        #Upload each image file and configure user profiles
        $localPhotosFolder | ForEach-Object {
       
            #Generate file path for upload into SharePoint
            $spFullPath = $spPhotosFolder.Url + "/" + $_.Name
               
            #Check if the file exists and the overwrite option is selected before adding the file
            if ((!$mySiteHostWeb.GetFile($spFullPath).Exists) -or ($Overwrite)) {
                #Add file to the User Photos library
                write-host "Copying" $_.Name "to" $spFullPath.Replace("/" + $_.Name,"") "in" $mySiteHostWeb.Title "..." -foregroundcolor Green
                $spFile = $spPhotosFolder.Files.Add($spFullPath, $_.OpenRead(), $true)
                $spImagePath = $mySiteHostWeb.Url + "/" + $spFile.Url
               
                #Get the domain and user name from the image file name
                $domainName = $_.Name.Split("_")[0]
                $userName = $_.Name.Split("_")[1].Replace($_.Extension, "")
                $adAccount = $domainName + "\" + $userName
               
                #Check to see if user profile exists
                if ($profileManager.UserExists($adAccount))
                {
                    #Get user profile and change the Picture URL value
                    $up = $profileManager.GetUserProfile($adAccount)
                    $up["PictureURL"].Value = $spImagePath
                    $up.Commit()
                }
                else
                {
                    write-host "Profile for user"$adAccount "cannot be found"
                }
            }
            else
            {
                write-host "`nFile"$_.Name "already exists in" $spFullPath.Replace("/" + $_.Name,"") "and shall not be uploaded" -foregroundcolor Red
            }
        }
       
        #Run the Update-SPProfilePhotoStore cmdlet to create image thumbnails and update user profiles
        write-host "Waiting to update profile photo store - Please wait..."
        Start-Sleep -s 60
        Update-SPProfilePhotoStore –MySiteHostLocation $MySiteUrl
        write-host "Profile photo store update run - please check thumbnails are present in Profile Pictures folder."
    }
    catch
    {
        write-host "The script has stopped because there has been an error: "$_
    }
    finally
    {
        #Dispose of site and web objects
        $mySiteHostWeb.Dispose()
        $mySiteHostSite.Dispose()
    }
}

At this point you will need to know the URL of your My Site host site – this is typically the root site collection of a dedicated Web Application (e.g., http://MySite), but could differ depending on your environment. On my development environment it is http://portal/personal/mysite, and so I can call the function above with the following command:

Upload-PhotosToSP -LocalPath "C:\Install\Photos" -MySiteUrl "http://portal/personal/MySite" -Overwrite

When you run the function, you will get an output to the console similar to the one shown below:

image

The script will copy the image files to the root of the “User Photos” document library…

image

…and providing enough time had passed before the Update-SPProfilePhotoStore cmdlet ran, thumbnails will be present in the “Profile Pictures” folder:

image

If you need to update the image for a user, copy the file into the local or network folder you used before and just run the script again with the Overwrite switch specified.

Now to where I was going wrong. As mentioned earlier, I run the Update-SPProfilePhotoStore cmdlet at the end of the script to create the required image thumbnails and re-configure user profiles to reference the thumbnail instead of the originally uploaded image. When I originally ran this cmdlet to test the script, nothing happened – no errors and no changes to the images. However, and I’m not sure why (can anyone enlighten me?), but I have found that you need to wait a while after initially configuring the user profiles before running the cmdlet.

Therefore, in the script, I have added a sleep command of 60 seconds before running Update-SPProfilePhotoStore, which worked on my development machine with only a few images. This may or may not be enough in your environment, and so you will need to check that the thumbnail images have been successfully created in the “Profile Pictures” folder of the “User Photos” document library to ensure the script has run successfully. If not, you can always increase the amount of time to wait in the script by altering the number in the Start-Sleep -s 60 line, or run the cmdlet manually with the following syntax:

Update-SPProfilePhotoStore –MySiteHostLocation <MySiteHostURL>

49 comments:

  1. Can also do it with this app
    http://spc3.codeplex.com/wikipage?title=ProfileImageUpload

    ReplyDelete
  2. another good writeup, free powershell script and documented process on how to do this can be found here:
    http://licomputersource.com/Blog/2010/12/uploading-user-profile-pictures-programmatically/

    ReplyDelete
  3. Hi Phil,
    Just wanted to say thank you for this script.
    Also to point out to you and others that for this script to work (in my case anyway) the user running the script needs to be the site owner.

    ReplyDelete
  4. Thank you so much for this script, it works perfectly. I struggled with exporting the thumbnailphoto attribute to AD and never did get it to work. This script is even better.

    ReplyDelete
  5. Thanks for the comments - Glad the script helped :-)

    ReplyDelete
  6. I get the following error:

    PS C:\Users\spsadmin> Upload-PhotosToSP -LocalPath "C:\Install\Photos" -MySiteUrl "http://mysites:2011/" -Overwrite
    New-Object : Exception calling ".ctor" with "1" argument(s): "No User Profile Application available to service the request. Contact your farm administrator."
    At line:13 char:33
    + $profileManager = New-Object <<<< Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    Copying pta_domain-abezuidenhout.jpg to User Photos in ...
    The script has stopped because there has been an error: Exception calling "Add" with "3" argument(s): "The system cannot find the file specified. (Exception from HRESULT: 0x80070002)"

    Any ideas?

    ReplyDelete
  7. On a quick glance, it sounds like the User Profile Service Application hasn't been set up correctly or try taking the slash off the end of "http://mysites:2011/"

    ReplyDelete
  8. @Anonnymous: It's most likely a permissions issue. I got the same error, googl... sorry, binged it, and found: http://blogs.technet.com/b/speschka/archive/2010/02/22/no-user-profile-application-available-mystery-in-sharepoint-2010.aspx
    This fixed the error for me.

    ReplyDelete
  9. Hmm... I'm stuck with the next problem: I'm getting the error: "Cannot open database "WSS_Content_MySites" requested by the login. The login failed. Login failed for user '[domain]\[myUserName]'." (where [domain] and [myUserName] are using my account. I'm a Farm Admin and can access mysites/mysite settings without issues. Using PowerGui, I can see that the exception is initially raised when the script reaches the line with Get-SPSite. After this line, just trying to display e.g. the PortalName or AllWebs property will raise that exception.

    I am wondering whether this article is relevant:
    http://stackoverflow.com/questions/440847/console-application-object-model-database-persmission

    In short - if I make myself db_owner on the content db for mysites, it should work.
    Has anyone else seen this error?

    ReplyDelete
  10. Yes, making the user running the script db_owner of the MySite content DB "solves" that issue.

    Now the next problem: Thumbnails are generated fine, but the image in my user profile displays a broken link. If I navigate to the "User Photos" library, the image is there, both the original and the thumbnails. However, if I click all the way through to the base image (ie just continue to click the image thumbnail, until you reach the base image, with a base URL), the image link is broken.

    Anyone seen this?

    ReplyDelete
  11. Ok, this is what works for me - for now at least: I modified the script above to set each profile picture URL to the large thumbnail generated by SharePoint. Ie. the "User Photos/Profile Pictures/t/[filename]_LThumb_jpg.jpg" file. I noticed that this image displays correctly, even though I cannot drill all the way down to the original image. That image link, is still broken, but all thumbnails generated just fine.

    Since I'm out of time for messing around with user pictures, I have to stop investigating, so I cannot explain all details of WHY this works. It's a feature, I guess ;-)

    ReplyDelete
  12. This is AMAZING! Thank you for taking the time to write such an incredibly thoughtful and user-friendly piece of work. It is now a very valuable part of creating our employee experience. Thank you again!

    ReplyDelete
  13. Hi Phil, thank you for sharing your script. Can i delete original images from root folder of "User Photos"? Seems to be that they are useless.

    ReplyDelete
  14. Pavel - I haven't tested it with them removed, but you're welcome to try

    ReplyDelete
  15. Thanks A lot.....Saved lot of time :)

    ReplyDelete
  16. Followed exactly what Phil said on the article and tried to run the script, but no output on powershell. Any idea what's going on?

    ReplyDelete
  17. This comment has been removed by the author.

    ReplyDelete
  18. Hao Ngo - the script has been viewed a fair amount of times and no one has complained of that one yet

    ReplyDelete
  19. the script works great for me. here is the problem. i can't update the user profile image until the user first visits mysites and mysites is created for them.

    should i keep this pattern and just schedule my job daily? or is there a way to recreate the mysite for them and then update their profile picture?

    ReplyDelete
  20. Hello, the script does not work on my SP Server.
    i do the following:
    1. start run
    2. insert "powershell.exe -noexit "& 'c:\Upload-PhotosToSP.ps1' " "
    3. type in power shell: "Upload-PhotosToSP -LocalPath "C:\Install\Photos" -MySiteUrl "http://portal/personal/MySite" -Overwrite"
    4. the power shell skips to a new line, but doesn't bring any message that he has copied something or something like that...

    I also changed the MySiteUrl to our MySite Store.

    I also checked the permissions, so everything should be fine.

    Do i make something wrong?

    Thanks in advance.

    Regards

    ReplyDelete
  21. Hi,
    Thank you so much for this script! Its working great except I have a couple of users that its telling me their profile cannot be found.
    Any ideas why not?
    thanks,
    Jennie

    ReplyDelete
  22. Awesome script, saved me a ton of time...

    ReplyDelete
  23. Im getting the following error when i run the Upload-PhotosToSp command
    "Method Invocation failed becuase [System.IO.DirectoryInfo] doesnt contain a method 'openread"

    Any idedas?

    ReplyDelete
    Replies
    1. It is throwing an error because you have are trying to upload a folder. Make sure you don't have any folder where you have all the pictures.

      Delete
  24. Thanks for the script Phil, it had been working great for me until I installed the December 2011 Cumulative Update. Now I get:

    New-Object : Exception calling ".ctor" with "1" argument(s): "Object reference not set to an instance of an object."
    At D:\User Profile Pictures\Upload-PhotosToSP_script.ps1:13 char:33
    + $profileManager = New-Object <<<< Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    Copying domain_user.jpg to User Photos in MySite ...
    The script has stopped because there has been an error: You cannot call a method on a null-valued expression.

    ReplyDelete
  25. This script looks great Phil. I also get this error when up-to-date with all the Sharepoint updates:

    New-Object : Exception calling ".ctor" with "1" argument(s): "Object reference not set to an instance of an object."
    At D:\User Profile Pictures\Upload-PhotosToSP_script.ps1:13 char:33
    + $profileManager = New-Object <<<< Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    Copying domain_user.jpg to User Photos in MySite ...
    The script has stopped because there has been an error: You cannot call a method on a null-valued expression.




    Is this script now useless? :(

    ReplyDelete
    Replies
    1. Sorry - I had to permissions and administrators on the Sharepoint admin interface and it worked again!

      Delete
  26. Hi Mike, thanks for posting the solution - glad you got it working!

    ReplyDelete
  27. Absolutely fantastic! I’m a basic PowerShell user and this script worked first time. Thank you.

    ReplyDelete
  28. Hi Phil

    Can you confirm if existing user profile pics will be removed if I want to upload multiple new user photo's?

    Thanks

    Mike

    ReplyDelete
  29. abdul raoof k20 July 2012 08:22

    Copying domain_abdul.jpg to User Photos in ...
    The script has stopped because there has been an error: Exception calling "Add" with "3" argument(s): "The system cannot find the file specified. (Exception from HRESULT: 0x80070002)"

    I am stuck with the above error.anybody please help me

    ReplyDelete
    Replies
    1. Having the same issue as abdul

      Delete
    2. In my case i'm dealing with a 2007 to 2010 upgraded environment, using the SP2010 SP1 + June 2011 CU. I tried to login and just upload a picture into one of the user profiles to test it, and i wasn't able to, said there was an error saving the picture. From some of the research I found online (http://social.technet.microsoft.com/Forums/en-US/sharepoint2010setup/thread/5ce9b4ec-1bd4-44b6-ae3c-823aa30caa93/), looks like this is supposed to be resolved by the December 2011 CU, installing now, I'll post a follow up if it doesn't resolve it. Thanks for your work on this Phil.

      Delete
  30. abdul raoof k30 July 2012 08:04

    But for me i am able to upload the user profile picture if i login. but I am getting this error while running this powershell script.Can anybody suggest me some solution for this. I need to bulk update user profile photos. Please suggest me a possible solution.

    Thanks

    ReplyDelete
  31. abdul raoof k31 July 2012 08:03

    Thak god.My issue got solved..I was able to figure it out at last.It was the permission issue which created all the troubles.I was getting the error as I was not having the content DB access.

    Thanks Phil for this fantastic script.

    ReplyDelete
  32. Do I need to load a module inorder to not get this error? Followed your instructions to the T.

    PS C:\Users\scholtep> cd\
    PS C:\> cd c:\PowerShellScripts
    PS C:\PowerShellScripts> .\UploadUserPhotos.ps1
    PS C:\PowerShellScripts> Upload-PhotosToSP -LocalPath "D:\UserProfilePictures-Active" -MySiteUrl "http://ch-web-sps-d01:23171/sites/mysite
    e
    The term 'Upload-PhotosToSP' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of
    r if a path was included, verify that the path is correct and try again.
    At line:1 char:18
    + Upload-PhotosToSP <<<< -LocalPath "D:\UserProfilePictures-Active" -MySiteUrl "http://ch-web-sps-d01:23171/sites/mysite/" -Overwrite
    + CategoryInfo : ObjectNotFound: (Upload-PhotosToSP:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    ReplyDelete
    Replies
    1. figured it out.. needed to add an extra "." shown below.

      PS C:\powershellscripts> . .\uploadUserPhotos.ps1
      PS C:\powershellscripts> Upload-PhotosToSP -LocalPath "D:\UserProfilePictures-Active" -MySiteUrl "http://ch-web-sps-d01:23171/sites/mysite"

      next question, do users need to already have a profile for thier photo to be added? the majority of the users, their profile couldn't be found and the photo was dumped in User Photo's and not processed into Profile Pictures. However I'm doing this in development and no one has signed into their My Site, yet some pitcures were processed. any help would be greatly appreciated.

      Delete
    2. figured this out too.. for some reason profiles are being imported with two different domains.. domain1\, domain2\ .. anyone have this issue?

      Delete
  33. HI Phill

    I am very new in powershell is there a way that we can change the script so that the user picture name will be just username.jpg instead of domainname_username.jpg

    ReplyDelete
  34. I really love you right now :)

    Thanks a lot for the script!

    ReplyDelete
  35. Thank you for it. Good job! :D

    ReplyDelete
  36. Robin Lawrie6 March 2013 15:11

    Brilliant script and well explained, worked a treat for me. Thank you!

    ReplyDelete
  37. worked on my dev environment, but pushing it to stage saw some weird behavior. all of the thumbnails update, can be indexed, org chart works etc... except for the main picture on the user profile. defaults to the generic image. ../_layouts/images/O14_person_placeHolder_192.png

    any ideas?

    ReplyDelete
  38. This is great, but what if I want to check and see if the picture I am going to upload already exists on the mysites/user profile library?

    Any idea how I can do that?

    Thanks a lot!

    ReplyDelete
  39. I get this error when I try to run the script, I have double checked the permissions and they are fine, any ideas?
    The script has stopped because there has been an error: Exception calling "Add" with "3" argument(s): "0x80070005Access denied."

    ReplyDelete
  40. This comment has been removed by the author.

    ReplyDelete
  41. Please Phillip! i need your guidance a little bit , when i run the script , am facing error like this , please any idea what i've done wrong , where i'd been misstaked, please help me on it !!! will gr8 appriciate.!
    {
    Upload-PhotosToSP : The term 'Upload-PhotosToSP' is not recognized as the name of a cmdlet, function, script file, or o
    perable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try aga
    in.
    At C:\Users\sharepoint.demo\Desktop\PicUploadScript\Upload-PhotosToSP.ps1:1 char:1
    + Upload-PhotosToSP -LocalPath "C:\Users\sharepoint.demo\Desktop\PicUploadScript\p ...
    + ~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (Upload-PhotosToSP:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
    }

    ReplyDelete
  42. The script has stopped because there has been an error: Exception calling "Add" with "3" argument(s): "The following file(s) have been blocked by the administrator: User Photos/mysite_pictures.ps1"

    What am I doing wrong?

    ReplyDelete
  43. I got he uri is empty while running Update-SPProfilePhotoStore, how can I fix it? Thanks :)

    ReplyDelete