Showing posts with label User Profile. Show all posts
Showing posts with label User Profile. Show all posts

Sunday, June 5, 2016

Update User Profile properties in SharePoint online

I had a requirement from our client that we need to update some of the user AD properties with the SharePoint user profile properties regularly.

So, for that i have build a console application to update the properties and i have scheduled it as a Windows scheduler.

I have kept all the settings (LDAP connection string, AD admin username, password etc.. ) in the app.config file so that i don't have to specify them in the code.

Below is the App.Config file code.

<appSettings>
    <add key="tenantAdministrationUrl" value=""/>
    <add key="tenantAdminLoginName" value=""/>
    <add key="tenantAdminPassword" value=""/>
    <add key="UserProfileProperties" value="WorkPhone|CellPhone"/>   
    <add key="UserProfilePrimeProperty" value="WorkEmail"/>
    <add key="LDAP" value="LDAP://OU="/>
    <add key="ADAdminUserName" value="" />
    <add key="ADAdminPassword" value="" />
    <add key="Properties" value="telephoneNumber|mobile"/>
    <add key="PrimeProperty" value="mail"/>
  </appSettings>
 


Below is the code to retrieve the settings from the app.config file.
Consts._UserProfilePrimeProperty = ConfigurationManager.AppSettings["UserProfilePrimeProperty"];
Consts.ADAdminUserName = ConfigurationManager.AppSettings["ADAdminUserName"];
Consts.ADAdminPassWord = ConfigurationManager.AppSettings["ADAdminPassWord"];
Consts._ProfileProperties = ConfigurationManager.AppSettings["Properties"];
Consts._PrimeProperty = ConfigurationManager.AppSettings["PrimeProperty"];
Consts._tenantAdministrationUrl = ConfigurationManager.AppSettings["tenantAdministrationUrl"];
Consts._tenantAdminLoginName = ConfigurationManager.AppSettings["tenantAdminLoginName"];
Consts._tenantAdminPassword = ConfigurationManager.AppSettings["tenantAdminPassword"];
Consts._UserProfileProfileProperties = ConfigurationManager.AppSettings["UserProfileProperties"];

Below method will retrieve the AD information.

 
public static void getADInformation()
        {
            string ldap = ConfigurationManager.AppSettings["LDAP"];
            Console.WriteLine(ldap);
            if (!string.IsNullOrEmpty(ldap))
            {
                try
                {
                    using (DirectoryEntry ou = new DirectoryEntry(ldap))
                    {
                        DirectorySearcher searcher = new DirectorySearcher(ou);
                        searcher.Filter = "(&(objectClass=user)(objectCategory=person))";
                        searcher.SearchScope = SearchScope.Subtree;
                        searcher.PageSize = 1000;//This has to be set to return all the results from AD, if not set then it will limit the results to 1000 only

                        string[] tempProp = Consts._ProfileProperties.Split('|');
                        string[] properties = new string[tempProp.Count() + 1];
                        properties[0] = Consts._PrimeProperty;
                        for (int i = 1; i <= tempProp.Count(); i++)
                        {
                            properties[i] = tempProp[i - 1];
                        }

                        foreach (var property in properties)
                        {
                            searcher.PropertiesToLoad.Add(property);
                        }

                        SearchResult result;
                        List<string[]> columnValues = new List<string[]>();

                        searcher.PropertiesToLoad.Add("userPrincipalName");
                        searcher.PropertiesToLoad.Add("samAccountName");

                        SearchResultCollection resultCol = searcher.FindAll();
                        Console.WriteLine("Find all");
                        if (resultCol != null)
                        {
                            Console.WriteLine(resultCol.Count + " Results found in AD");
                            for (int i = 0; i < resultCol.Count; i++)
                            {
                                result = resultCol[i];
                                Console.WriteLine(result.Properties["samAccountName"][0] + "-" + result.Properties["userPrincipalName"][0]);

                                string[] tempPropVal = new string[properties.Count()];
                                int propCnt = 0;
                                //get each property value to array
                                foreach (var property in properties)
                                {
                                    if (result.Properties.Contains(property))
                                    {
                                        tempPropVal[propCnt] = Convert.ToString(result.Properties[property][0]);
                                    }
                                    else
                                    {
                                        tempPropVal[propCnt] = "";
                                    }
                                    propCnt++;
                                }
                                columnValues.Add(tempPropVal);
                            }
                        }

                        string[] tempUserProfileProp = Consts._UserProfileProfileProperties.Split('|');
                        string[] userProfileProperties = new string[tempUserProfileProp.Count() + 1];
                        userProfileProperties[0] = Consts._UserProfilePrimeProperty;
                        for (int i = 1; i <= tempUserProfileProp.Count(); i++)
                        {
                            userProfileProperties[i] = tempUserProfileProp[i - 1];
                        }

                        Consts.UserProdDataCol = new List<Dictionary<string, ExpandoObject>>();

                        foreach (string[] columnValue in columnValues)
                        {
                            dynamic data = new ExpandoObject();
                            //get the first object which is the account
                            string account = columnValue[0];
                            Dictionary<string, ExpandoObject> userProfData = new Dictionary<string, ExpandoObject>();
                            if (!string.IsNullOrEmpty(account))
                            {
                                for (int j = 1; j < userProfileProperties.Count(); j++)
                                {
                                    ((IDictionary<String, Object>)data).Add(userProfileProperties[j], columnValue[j]);
                                }
                                userProfData.Add(account, data);
                                Consts.UserProdDataCol.Add(userProfData);
                            }
                            else
                            {
                                Console.WriteLine(account + " is empty");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.StackTrace);
                }
            }
        }

Below is the method which will import AD data to user profile.

public static void ImportDatatoCloud(TextWriter writer)
        {
            List<Dictionary<string, ExpandoObject>> dataCollection = Consts.UserProdDataCol;
            int count = 1;
            int totalUsers = dataCollection.Count;
            foreach (var dataCol in dataCollection)
            {
                Generic.LogMessage(string.Format("processing user '{0}' of {1}...", count, totalUsers), Consts.LogLevel.Information);
                var userName = dataCol.Keys.FirstOrDefault();
                dynamic propertySet = dataCol.Values.FirstOrDefault();

                string UserAccountName = Consts._sPOProfilePrefix + userName;

                try
                {
                    using (ClientContext clientContext = new ClientContext(Consts._tenantAdministrationUrl))
                    {
                        SecureString passWord = new SecureString();

                        foreach (char c in Consts._tenantAdminPassword.ToCharArray()) passWord.AppendChar(c);

                        clientContext.Credentials = new SharePointOnlineCredentials(Consts._tenantAdminLoginName, passWord);

                        // Get the people manager instance for tenant context
                        PeopleManager peopleManager = new PeopleManager(clientContext);

                        foreach (var property in propertySet)
                        {
                            string propName = property.Key;
                            string propVal = property.Value;
                            Console.WriteLine(userName + " - " + propName + "->" + propVal);

                            if (propVal.Contains('|'))
                            {
                                // List Multiple values
                                List<string> propValues = propVal.Split('|').ToList<string>();

                                // Update the SPS-Skills property for the user using account name from profile.
                                peopleManager.SetMultiValuedProfileProperty(UserAccountName, propName, propValues);
                            }
                            else
                            {
                                // Update the AboutMe property for the user using account name.
                                peopleManager.SetSingleValueProfileProperty(UserAccountName, propName, propVal);
                            }
                        }

                        clientContext.ExecuteQueryRetry();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Issue in updating the profile properties for :" + UserAccountName);
                    writer.WriteLine("Issue in updating the profile properties for :" + UserAccountName);
                    writer.WriteLine(ex.ToString());
                }

                count++;
            }
            Generic.LogMessage("Processing finished for " + totalUsers + " user profiles. Import Complete!", Consts.LogLevel.Warning);
        }

Points to remember: 
Remember to add the sharepoint online client dll's - Microsoft.SharePoint.Client.dll, Microsoft.SharePoint.Client.Runtime.dll, Microsoft.SharePoint.Client.UserProfiles.dll

Hope this code helps you.

Happy Coding :)



Sunday, September 28, 2014

Clean up SharePoint User Profile Store using PowerShell

Today i had a problem in my development environment for my SharePoint Projects regarding User Profile Store. My user profile was a messed up and was not available for editing in central administration. When i searched for my profile it still there but not shown for administration.  This was a really strange behavior because a couple of days it worked well. I added various new profile properties to my user profile service application. So the recreation of User Profile Service 
Application was not an option. Deletion of orphan or corrupted user profiles is not possible using Central Administration or even using avaliable PowerShell commands.

But there is a solution using PowerShell without compiled code. Using PowerShell everything what is avaliable in the server object model is avaliable. First of all two assemblies must be referenced.
These assemblies are:

These assemblies are:

Script:

/* Load required Assemblies for SharePoint Server and User Profile */
[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.Office.Server”)
[System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.Office.Server.UserProfiles”)
/* Central Adminstration URL or any Web Application*/
$url = "http://myserver:Port"
/* Create a new Context Object */
$contextWeb = New-Object Microsoft.SharePoint.SPSite("http://servername");
/* Get the right Service Context */
$ServerContext = [Microsoft.Office.Server.ServerContext]::GetContext($contextWeb);
/* create a new connection to the UserProfileManager */
$UserProfileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServerContext);
/* Ger all User Profiles */
$Profiles = $UserProfileManager.GetEnumerator();
/* Loop through user profile */
foreach ($oUser in $Profiles ) {
/* Remove Profile */
$UserProfileManager.RemoveUserProfile($oUser.item("AccountName"));
}

This script can be extended to delete only specific users from user profile information. At the end I was able to solve my problem. My User Profile was deleted and recreated on first access and due profile import. Everything worked fine.

In the old days you had to write some custom command line tool to address those batch update and deletion task. With the introduction of PowerShell to SharePoint any possible administrative task could be accomplished by just use scripting.

Those assemblies could be referenced using System.Reflection and the rest of the script is quite simple SharePoint Development. So get a context object, open profile service application get all use and delete them.

Update Mysites/UserProfile user image url

I have recently had reason to update the PictureURL property value via PowerShell in SharePoint 2013 for all users in the system.
As a result, I wrote the following PowerShell script to update the property value using string replace.

You will ideally need to run this on the server with an account with the appropriate permissions to update all user profiles.

Script:

#Set up default variables
  
  #My Site URL
  $mySiteUrl = "http://mysite/"
  
  #The part of the picture URL you are trying to find
  $currentURLValue = "http://mypersonalsite"
  
  #The value that will replace the above
 $newURLValue = "http://mysite:80"
 #The internal name for PictureURL
 $upPictureURLAttribute = "PictureURL"
 #Get site objects and connect to User Profile Manager service
 $site = Get-SPSite $mySiteUrl
 $context = Get-SPServiceContext $site
 $profileManager = New-Object   Microsoft.Office.Server.UserProfiles.UserProfileManager($context) 
 $profiles = $profileManager.GetEnumerator()
 foreach ($userProfile in $profiles) {
  if ($userProfile[$upPictureURLAttribute] -ne '') {
    
    $newPictureURL = $userProfile[$upPictureURLAttribute].toString()
    $newPictureURL  = $newPictureURL.Replace($currentURLValue, $newURLValue)
    
    write-host "Before: " $userProfile[$upPictureURLAttribute].toString() " | After: " $newPictureURL 
    
    #Get user profile and change the value - uncomment the lines below to commit the changes
    #$userProfile[$upPictureURLAttribute].Value = $newPictureURL
    #$userProfile.Commit()
  }
 }