You might be wondering why should I do this when Sharepoint can do this already though User Profile Synchronization, well that is partly true because in Sharepoint Foundation 2013 and even on 2010 it is not available. Having said that there will always be a way and that’s what are trying to achieve on this article, to synchronize Active Directory Information with Sharepoint User Profile by doing some coding. This can be achieved in multiple ways, you can do Powershell, you can create a Sharepoint feature or develop a standalone application, we will do the latter as for me its more flexible and manageable.
Now lets start but before doing so lets lay out some ground work and give some explanation on how to we go about on this one.
Did you know that Sharpoint stores user profiles in a list as well? It’s sort of hidden but in case you did not know you can locate it at:
http://{YourSharepointUrl}/_catalogs/users/simple.aspx
And it will look like this
But something is different, unlike normal lists it does not give you the tabs to manipulate the list or its view but that does not stop us in doing so as it is just a web part in a page, having said that you can still edit the page.
and edit the web part
then edit the current view
and there you are
free to change how your view looks like.
That does not end there, you can also edit the list by adding column of your choice. It’s not straightforward though as you need to know your List Id by checking it in the URL while you in the view property page.
Now copy that and combine it with the URL below to view your List Properties
http://{YourSharepointURL}/_layouts/listedit.aspx?List={YourListIdHere}
And whoalla! you can now edit your list.
So now lets say we add Description, Office and Company which is not there by default.
Once saved you can now see it on the view
Now that you know that the user profiles are saved on a list and you can modify the structure of it let’s do some coding and map our Active Directory with Sharepoint User Data. For this example we will map the common and the most logical fields we usually use.
Although it shows the proper name on the view the field names might not be the same, so if you run the codes below and investigate what the field names are it would look like this.
So make sure you map it to the correct Sharepoint and Active Directory fields/columns, for our example it will be like this.
View Labels | Sharepoint Name | Active Directory Name |
Name | Title | displayName |
Job Title | JobTitle | title |
Department | Department | department |
Mobile Number | MobilePhone | mobile |
DDI | DDI | telephoneNumber |
SIP Address | SIPAddress | ipPhone |
Description | Description | description |
Office | Office | physicalDeliveryOfficeName |
Company | Company | company |
Now lets start coding. What we will do first is a simple listing of all Site Users, to do that copy and paste the codes below.
private static void GetAllSiteUsers() { // Starting with ClientContext, the constructor requires a URL to the server running SharePoint. var sharepointContext = new ClientContext("http://yoursharepointurl/"); //Get Web Context var sharepointList = sharepointContext.Web.SiteUserInfoList; var camlQuery = new CamlQuery(); var userList = sharepointList.GetItems(camlQuery); sharepointContext.Load(userList); sharepointContext.ExecuteQuery(); foreach (ListItem user in userList) { } }
Run it and lets see your first data, the first user that will appear is the one who set up your Sharepoint Instance, which means you are safe to assume that it is a real user.
Place a breakpoint and investigate your results. If you notice when you run the code it will give you all the List Items and in our example we have 2399 items, we need to filter it further because the whole list contains Users, Active Directory Groups and Sharepoint Groups. What we need to sync only are the Users so we need to identify which are the users somehow.
After looking at my example I have two Field values that I can use, first is the ContentTypeId which if you further investigate is common for all Users other entities have a different ContentTypeId. Another is the Name where all users will have a Claims Authentication Code and Domain Prefix ie i:0#.w|YourDomainUserName other entities such as group is prefixed as c:0+.w| followed by and SID.
Now we know how to filter lets apply that to the CAML Query and further filter our results.
private static void GetAllSiteUsersFilteredByType() { // Starting with ClientContext, the constructor requires a URL to the server running SharePoint. var sharepointContext = new ClientContext("http://yoursharepointurl/"); //Get Web Context var sharepointList = sharepointContext.Web.SiteUserInfoList; //Prefix for all users string userType = "i:0#.w|"; var camlQuery = new CamlQuery(); camlQuery.ViewXml = @"<FieldRef Name='Name' />" + userType + "</Value></Contains></Where></Query></View>"; var userList = sharepointList.GetItems(camlQuery); sharepointContext.Load(userList); sharepointContext.ExecuteQuery(); foreach (ListItem user in userList) { } }
Now you will see we have lesser result count as we only get the real users.
Let’s do the synchronization Part!
By now you know how to get all that users now we need to search for the same user in Active Directory get its information and overwrite the ones in Sharepoint. The codes below is complete but I just created them in one class as a console application, it’s up to you how you want to use them.
The codes also takes into consideration that service user you will be using have full rights on Active Directory and is in the Site Collection Administrators Group on Sharepoint.
using Microsoft.SharePoint.Client; using System.DirectoryServices; using System.DirectoryServices.AccountManagement; namespace Sharepoint_User_and_Active_Directory_Sync { static class Program { //If you're using claims authentication, this defines a user on a domain const string userType = "i:0#.w|"; //If you're not using claims authentication, this defines a user on a domain //const string userType = @"YourDomain"; const string domain = "your.sharepoint.com"; const string rooOrganizationalUnit = "DC=your,DC=sharepoint,DC=com"; const string adDomain = "YOURDOMAIN"; const string adUserName = "YourServiceUser"; const string adPassword ="YourServiceUserPassword"; static void Main(string[] args) { // Starting with ClientContext, the constructor requires a URL to the server running SharePoint. var sharepointContext = new ClientContext("http://yoursharepointurl/"); sharepointContext.Credentials = new System.Net.NetworkCredential(adUserName, adPassword, adDomain); //Get Site Users Lists var sharepointList = sharepointContext.Web.SiteUserInfoList; //Filter User List by CAML Query var camlQuery = new CamlQuery(); camlQuery.ViewXml = @"<FieldRef Name='Name' />" + userType + "</Value></Contains></Where></Query></View>"; var userList = sharepointList.GetItems(camlQuery); sharepointContext.Load(userList); sharepointContext.ExecuteQuery(); //Loop Through Each User foreach (ListItem user in userList) { //Lets remove the Claims Authentication Prefix var userName = user["Name"].ToString().Replace(userType, ""); //Get the User Details from Active Directory UserPrincipal userPrincipal = GetUser(userName); //if User Principal Exists if (userPrincipal != null) { user["Title"] = userPrincipal.DisplayName; user["EMail"] = userPrincipal.EmailAddress; user["Description"] = userPrincipal.Description; user["DDI"] = userPrincipal.VoiceTelephoneNumber; //Unrepresented AD Attributes in User Principal, we have to use //our own extension method called GetProperty to retrieve AD Value user["JobTitle"] = userPrincipal.GetProperty("title"); user["Department"] = userPrincipal.GetProperty("department"); user["MobilePhone"] = userPrincipal.GetProperty("mobile"); user["SipAddress"] = userPrincipal.GetProperty("ipPhone"); user["Office"] = userPrincipal.GetProperty("physicalDeliveryOfficeName"); user["Company"] = userPrincipal.GetProperty("company"); //Update User user.Update(); //Commit Changes sharepointContext.ExecuteQuery(); } } } /// <summary> /// Gets the base principal context /// </summary> /// <returns>Retruns the PrincipalContext object</returns> private static PrincipalContext GetPrincipalContext() { PrincipalContext principalContext; principalContext = new PrincipalContext(ContextType.Domain, domain, rooOrganizationalUnit, ContextOptions.Negotiate, adDomain + @"" + adUserName, adPassword); return principalContext; } /// <summary> /// Gets a certain user on Active Directory /// </summary> /// <param name="userName">The username to get</param> /// <returns>Returns the UserPrincipal Object</returns> private static UserPrincipal GetUser(string userName) { try { var principalContext = GetPrincipalContext(); var userPrincipal = UserPrincipal.FindByIdentity(principalContext, userName); return userPrincipal; } catch { return null; } } /// <summary> /// Gets the AD Attributes not represented in UserPrincipal /// </summary> /// <param name="principal">The User Principal Object</param> /// <param name="property">The property name you want to retrieve</param> /// <returns></returns> private static string GetProperty(this UserPrincipal principal, string property) { var directoryEntry = principal.GetUnderlyingObject() as DirectoryEntry; if (directoryEntry.Properties.Contains(property)) { return directoryEntry.Properties[property].Value.ToString(); } else { //Property not exisiting return empty strung return string.Empty; } } } }
That’s it, now you can sync your Active Directory User Information to your Sharepoint Foundation 2013 regularly.
Hi please suggest do i need below line if i create a timer service?
sharepointContext.Credentials = new System.Net.NetworkCredential(adUserName, adPassword, adDomain);
And anything else i need to change to work it in a timer service?
Kind regards
Atiq zia
You can remove that but you need to make sure the service account have access to the list mentioned above
Thank you so much for the reply and its solve my issue. Now i need your suggestion does your Getuser code will work if there are multiple OU and also an OU further can have sub level OU. I actually need code which should work in finding user regardless how much OU and sub level OU are created in AD.
Best regards
Atiq zia
That OU there is the root meaning anything below it will be searched, so I suggest in your case use the topmost one.
Can you send me source files please, if possible.
I’m new to SharePoint, and not understanding how to write the code and where?
Which project type should I use in visual studio?
You can choose whatever you want depending on how you would run it, for me I just created a console application that is called by Task Scheduler on predefined schedule.
Thanks Raymund…
Nice tutorial hovewer I am not sure where and how to code. What application are you using?. What programing language and program are you using?
Other that that it is very good and simple to follow article.
I am using Visual Studio and the language is C#
Hi man, nice post about this. I was trying to search some informations about the sync using Sharepoint 2013 at a server stand alone.
Would you tell me if as possible, sync the AD to the Sharepoint Foundations 2013 in this kind of server? Or using your post methods looks more safety?
Thanks in advance,
Best regards,
I used the code on Sharepoint Foundation 2013 as the full version you dont need to do this
All right, thanks for you reply. So can I import all my Farm to the AD, to do this sync?
Thanks again,
Best regards,
This is for AD to Sharepoint Sync
Thanks mann !!!
Hi, i’m new in SP.
It’s not clear for me, how you connected VS(Console C#) with SP2013, can you explain your steps with VS? advance thanks!
Thanks you for posting this article. I am going to work on it today and I am sure it will prove very useful.
I have a quick question about one of the screen captures though. I don’t know where to go to generate the screen of Attribute-Syntax-Count that listed the active directory properties. It’s the right portion of the screen shot under the caption:
“Now that you know that the user profiles are saved on a list and you can modify the structure of it let’s do some coding and map our Active Directory with Sharepoint User Data. For this example we will map the common and the most logical fields we usually use.”
[Path: CN=Raymund Macaaaly]
I have dug around in Microsoft’s “Active Directory Users and Computers” and can’t find this sort of attribute breakdown.
Its called ADExplorer, you can download them free at Technet.
Here is the link https://technet.microsoft.com/en-us/library/bb963907.aspx
Nice post. Helped me to add additional columns in the User Information List
Thanks a lot, Raymund
Hi, Raymund. I see that this will allow me to sync profile data on sharepoint foundation from AD, which is one of the things I wanted to do, so thanks a lot. One thing I don’t know how to do is to display the user profile to the user. If I click “my settings”, nothing happnes. Is it possible to call the data from sharepoint foundation’s user profile for the specific logged user?
I’m completely new to coding, but I slogged through to running the first code (as a .cs file) I’d substituted our sharepoint foundation 2013 URL. The error I got was: SiteUsers.cs(1,21): error CS0116: A namespace cannot directly contain members such as fields or methods. The build failed.
Obviously, the .cs doesn’t contain namespaces. I’m confused!