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>