Connect to an Active Directory and retrieve Users and their information

This post will explain how you would connect to an local Active Directory (AD), and also how to retrieve the AD information about each user such as the user SID, and how we can update each user in a SQL table with this information.

I have a class that looks like this containing some properties. Properties like UserId is something I would like to update from the AD, and UserName is a the property I want to use to retrieve data from the AD, each person in my table contains the correct DisplayName under the UserName field.

So imagine you have a SQL table of persons (or users) and have a class like this with an UserName property and an UserId property along with other properties you might want to update from the AD, this is how you could go about it.

using System;
using System.Collections.Generic;

namespace MyApp.Model
{
    [IncludeInTSGeneration]
    public class Person
    {
        public string UserId { get; set; }
        public string UserName { get; set; }

    }
}

Now let’s build a method for getting users from the Active Directory but first, take a look at the administrator account I have here from the Active Directory.

Nothing special properties-wise about this user, and most fields are empty. Let’s see how we build up a method to get the administrator data though!

public List < Model.Person > UpdateUsersFromAD() {
  // Get all users here and map them
  List < Model.Person > users = myPersonRepository.GetAllUsers();

  using(var ctx = new PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain)) {
    // find a user called administrator
    UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "administrator");

    if (user != null) {
      // Get the SID
      var usersSid = user.Sid;

      // DisplayName should be null since it is empty in our AD.
      var username = user.DisplayName;
    }

  }

  return users;
}

Now we’ve created a method called UpdateUsersFromAD. Now I will not go into the GetAllUsers method but it’s a simple Entity Framework method along with a map method to make us have all Model.Persons into a list called users. We do nothing yet with this List but we will, then we will using the PrincipalContext and pass in the AD DS store, see the different options of the ContextTypes here.

Then we will use UserPrincipal.FindByIdentity where we pass the context (ctx) and also pass along “administrator”. This is the login name used in the AD.

Then we have access to the user in the AD and his or hers properties like so user.Sid or user.DisplayName for example. If you debug you can see the administrator indeed has a Sid and NULL in DisplayName.

While in debug mode

If you add a DisplayName for the AD user it will appear under the DisplayName property as well (see the image).

Now how can we achieve this with a whole list of users? Let’s take a look.

public List<Model.User> UpdateUsersFromAD()
{
    // Get all users here and map them
    List<Model.User> users = myPersonRepository.GetAllUsers();

    using (var ctx = new PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain))
    {
        // Loop through users
        for(int x = 0; x < users.Count; x++)
        {
            // find a user
            UserPrincipal user = UserPrincipal.FindByIdentity(ctx, users[x].UserName);

            if (user != null)
            {
                // Set SID
                users[x].UserId = user.Sid.ToString();
            }
        }

    }

    return users;
}

What we did now is that we’ve added a for loop where we basically do the same thing as before where we fetch the user with username, but this time we use the actual users[x].UserName property from our object, and then we set the Sid as a string to the UserId property. Then in the end return users as before. You could now do a debug and see if our users got a match and now have SIDs updated. And there you go, you have successfuly updated your users with the data of the AD properties.

To make it more complete, lets add a database column in SQL for our UserId property. Note that your SQL column for the SID should be at least 256 characters.

SID is a string with a maximum length of 256 characters.

MS About SID

This SQL column is going to be mapping to our UserId Person property, remember the above statement that the SQL column for SID should be at least 256 character long. Example of a SID: “S-1-5-21-3796585825-3479577636-1793717433-4013”

I have now created a UserId column of the type nvarchar(256) in my persons SQL table. Let’s add some more C# code.

public List<Model.User> UpdateUsersFromAD()
    {
        // Get all users (method returns mapped Model.User objects)
        List<Model.User> users = myPersonRepository.GetAllUsers();

        using (var ctx = new PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Domain))
        {
            for (int x = 0; x < users.Count; x++)
            {
                // find a user on their logon name
                UserPrincipal user = UserPrincipal.FindByIdentity(ctx, users[x].UserName);

                if (user != null)
                {
                    // Set SID
                    users[x].UserId = user.Sid.ToString();
                }
            }

        }
        // Get only users that was matched with their username in the AD
        var usersWithSid = users.Where(u => u.UserId != null).ToList();
        
        // Pass the users to your update method
        myPersonRepository.UpdateUsers(usersWithSid);
        return users;
    }

What I leave to you is the UpdateUsers method other than that we have just took out the users we found having the same logon name in the AD as in our UserName. Then we pass this list of users into the UpdateUsers method (this does what it says… it updates users). What is the result? Users will have their UserId value changed from NULL to SID.

is now…

not null anymore.

Print out all properties and their values in C# from an Object

I stumbled upon a problem where I wanted to copy a WCF response and share it with someone, sounds easy enough but when you want to whole hierarchy of an objects properties in a clear view it can get tricky. Just like the WCF Test Client I want to get all properties and copy them but you can’t do that from the WCF Test Client unfortunately. So the best way I found is to utilize the ObjectDumper class we will create here.

So let’s say you have a bunch of classes like this

namespace MyNamespace
{
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Address Address { get; set; }
        public IList<Hobby> Hobbies { get; set; }
    }

    public class Hobby
    {
        public string Name { get; set; }
    }

    public class Address
    {
        public string Street { get; set; }
        public int ZipCode { get; set; }
        public string City { get; set; }    
    }
}

Then you’ll get a text like this basicly descripting the object

{MyNamespace.User}
  FirstName: "Arnold"
  LastName: "Schwarzenegger"
  Address: { }
    {MyNamespace.Address}
      Street: "6834 Hollywood Blvd"
      ZipCode: 90028
      City: "Hollywood"
  Hobbies: ...
    {MyNamespace.Hobby}
      Name: "body building"

You will get “…” telling that it is a list-object, otherwise simply the the object { } characters indicating it is indeed a single object, then it says what class it is.

The code you need:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

public class ObjectDumper
{
    private int _level;
    private readonly int _indentSize;
    private readonly StringBuilder _stringBuilder;
    private readonly List<int> _hashListOfFoundElements;

    private ObjectDumper(int indentSize)
    {
        _indentSize = indentSize;
        _stringBuilder = new StringBuilder();
        _hashListOfFoundElements = new List<int>();
    }

    public static string Dump(object element)
    {
        return Dump(element, 2);
    }

    public static string Dump(object element, int indentSize)
    {
        var instance = new ObjectDumper(indentSize);
        return instance.DumpElement(element);
    }

    private string DumpElement(object element)
    {
        if (element == null || element is ValueType || element is string)
        {
            Write(FormatValue(element));
        }
        else
        {
            var objectType = element.GetType();
            if (!typeof(IEnumerable).IsAssignableFrom(objectType))
            {
                Write("{{{0}}}", objectType.FullName);
                _hashListOfFoundElements.Add(element.GetHashCode());
                _level++;
            }

            var enumerableElement = element as IEnumerable;
            if (enumerableElement != null)
            {
                foreach (object item in enumerableElement)
                {
                    if (item is IEnumerable && !(item is string))
                    {
                        _level++;
                        DumpElement(item);
                        _level--;
                    }
                    else
                    {
                        if (!AlreadyTouched(item))
                            DumpElement(item);
                        else
                            Write("{{{0}}} <-- bidirectional reference found", item.GetType().FullName);
                    }
                }
            }
            else
            {
                MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
                foreach (var memberInfo in members)
                {
                    var fieldInfo = memberInfo as FieldInfo;
                    var propertyInfo = memberInfo as PropertyInfo;

                    if (fieldInfo == null && propertyInfo == null)
                        continue;

                    var type = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType;
                    object value = fieldInfo != null
                                       ? fieldInfo.GetValue(element)
                                       : propertyInfo.GetValue(element, null);

                    if (type.IsValueType || type == typeof(string))
                    {
                        Write("{0}: {1}", memberInfo.Name, FormatValue(value));
                    }
                    else
                    {
                        var isEnumerable = typeof(IEnumerable).IsAssignableFrom(type);
                        Write("{0}: {1}", memberInfo.Name, isEnumerable ? "..." : "{ }");

                        var alreadyTouched = !isEnumerable && AlreadyTouched(value);
                        _level++;
                        if (!alreadyTouched)
                            DumpElement(value);
                        else
                            Write("{{{0}}} <-- bidirectional reference found", value.GetType().FullName);
                        _level--;
                    }
                }
            }

            if (!typeof(IEnumerable).IsAssignableFrom(objectType))
            {
                _level--;
            }
        }

        return _stringBuilder.ToString();
    }

    private bool AlreadyTouched(object value)
    {
        if (value == null)
            return false;

        var hash = value.GetHashCode();
        for (var i = 0; i < _hashListOfFoundElements.Count; i++)
        {
            if (_hashListOfFoundElements[i] == hash)
                return true;
        }
        return false;
    }

    private void Write(string value, params object[] args)
    {
        var space = new string(' ', _level * _indentSize);

        if (args != null)
            value = string.Format(value, args);

        _stringBuilder.AppendLine(space + value);
    }

    private string FormatValue(object o)
    {
        if (o == null)
            return ("null");

        if (o is DateTime)
            return (((DateTime)o).ToShortDateString());

        if (o is string)
            return string.Format("\"{0}\"", o);

        if (o is char && (char)o == '\0') 
            return string.Empty; 

        if (o is ValueType)
            return (o.ToString());

        if (o is IEnumerable)
            return ("...");

        return ("{ }");
    }
}

Usage

var dump = ObjectDumper.Dump(user);

Credit goes to ms007 for finishing this great method. So imagine you have a User object which contains a bunch of properties just like above, you will then pass the object into the Dump method then what you get is a string which describes the object structure, and with this I like to save into a text file since you’ll have \r and \n characters for tab and line breaks. Note that you will need to import System.IO to use the File methods.

var dump = ObjectDumper.Dump(proj);
// Create a file to write to
string createText = dump;
File.WriteAllText("C:\\thing.txt", createText);

And there you go, now you have saved a text file of the object structure and their values in your C disk drive called thing.