In preparation for some upcoming posts related to LINQ (what else?), Windows PowerShell and Rx, I had to set up a local LDAP-capable directory service. (Hint: It will pay off to read till the very end of the post if you’re wondering what I’m up to...) In this post I’ll walk the reader through the installation, configuration and use of Active Directory Lightweight Directory Services (LDS), formerly known as Active Directory Application Mode (ADAM). Having used the technology several years ago, in relation to the LINQ to Active Directory project (which as an extension to this blog series will receive an update), it was a warm and welcome reencounter.
What’s Lightweight Directory Services anyway?
Use of hierarchical storage and auxiliary services provided by technologies like Active Directory often has advantages over alternative designs, e.g. using a relational database. For example, user accounts may be stored in a directory service for an application to make use of. While Active Directory seems the natural habitat to store (and replicate, secure, etc.) additional user information, IT admins will likely point you – the poor developer – at the door when asking to extend the schema. That’s one of the places where LDS comes in, offering the ability to take advantage of the programming model of directory services while keeping your hands off “the one and only AD schema”.
The LDS website quotes other use cases, which I’ll just copy here verbatim:
Active Directory Lightweight Directory Service (AD LDS), formerly known as Active Directory Application Mode, can be used to provide directory services for directory-enabled applications. Instead of using your organization’s AD DS database to store the directory-enabled application data, AD LDS can be used to store the data. AD LDS can be used in conjunction with AD DS so that you can have a central location for security accounts (AD DS) and another location to support the application configuration and directory data (AD LDS). Using AD LDS, you can reduce the overhead associated with Active Directory replication, you do not have to extend the Active Directory schema to support the application, and you can partition the directory structure so that the AD LDS service is only deployed to the servers that need to support the directory-enabled application.
• Install from Media Generation. The ability to create installation media for AD LDS by using Ntdsutil.exe or Dsdbutil.exe.
• Auditing. Auditing of changed values within the directory service.
• Database Mounting Tool. Gives you the ability to view data within snapshots of the database files.
• Active Directory Sites and Services Support. Gives you the ability to use Active Directory Sites and Services to manage the replication of the AD LDS data changes.
• Dynamic List of LDIF files. With this feature, you can associate custom LDIF files with the existing default LDIF files used for setup of AD LDS on a server.
• Recursive Linked-Attribute Queries. LDAP queries can follow nested attribute links to determine additional attribute properties, such as group memberships.
Obviously that last bullet point grabs my attention through I will retain myself from digressing here.
If you’re running Windows 7, the following explanation is the right one for you. For older versions of the operating system, things are pretty similar though different downloads will have to be used. For Windows Server 2008, a server role exists for LDS. So, assuming you’re on Windows 7, start by downloading the installation media over here. After installing this, you should find an entry “Active Directory Lightweight Directory Services Setup Wizard” under the “Administrative Tools” section in “Control Panel”:
LDS allows you to install multiple instances of directory services on the same machine, just like SQL Server allows multiple server instances to co-exist. Each instance has a name and listens on certain ports using the LDP protocol. Starting this wizard – which lives under %SystemRoot%\ADAM\adaminstall.exe, revealing the former product name – brings us here:
After clicking Next, we need to decide whether we create a new unique instance that hasn’t any ties with existing instances, or whether we want to create a replicate of an existing instance. For our purposes, the first option is what we need:
Next, we’re asked for an instance name. The instance name will be used for the creation of a Windows Service, as well as to store some settings. Each instance will get its own Windows Service. In our sample, we’ll create a directory for the Northwind Employees tables, which we’ll use to create accounts further on.
We’re almost there with the baseline configuration. The next question is to specify a port number, both for plain TCP and for SSL-encrypted traffic. The default ports, 389 and 636, are fine for us. Later we’ll be able to connect to the instance by connecting to LDP over port 389, e.g. using the System.DirectoryServices namespace functionality in .NET. Notice every instance of LDS should have its own port number, so only one can be using the default port numbers.
Now that we have completed the “physical administration”, the wizard moves on to a bit of “logical administration”. More specifically, we’re given the option to create a directory partition for the application. Here we choose to create such a partition, though in many concrete deployment scenarios you’ll want the application’s setup to create this at runtime. Our partition’s distinguished name will mimic a “Northwind.local” domain containing a partition called “Employees”:
After this bit of logical administration, some more physical configuration has to be carried out, specifying the data files location and the account to run the services under. For both, the default settings are fine. Also the administrative account assigned to manage the LDS instance can be kept as the currently logged in user, unless you feel the need to change this in your scenario:
Finally, we’ve arrived at an interesting step where we’re given the option to import LDIF files. And LDIF file, with extension .ldf, contains the definition of a class that can be added to a directory service’s schema. Basically those contain things like attributes and their types. Under the %SystemRoot%\ADAM folder, a set of out-of-the-box .ldf files can be found:
Instead of having to run the ldifde.exe tool, the wizard gives us the option to import LDIF files directly. Those classes are documented in various places, such as RFC2798 for inetOrgPerson. On TechNet, information is presented in a more structured manner, e.g revealing that inetOrgPerson is a subclass of user. Custom classes can be defined and imported after setup has completed. In this post, we won’t extend the schema ourselves but we will simply be using the built-in User class so let’s tick that one:
After clicking Next, we get a last chance to revisit our settings or can confirm the installation. At this point, the wizard will create the instance – setting up the service – and import the LDIF files.
Congratulations! Your first LDS instance has materialized. If everything went alright, the NorthwindEmployees service should show up:
Inspecting the directory
To inspect the newly created directory instance, a bunch of tools exist. One is ADSI Edit which you could already see in the Administrative Tools. To set it up, open the MMC-based tool and go to Action, Connect to… In the dialog that appears, specify the server name and choose Schema as the Naming Context.
For example, if you want to inspect the User class, simply navigate to the Schema node in the tree and show the properties of the User entry.
To visualize the objects in the application partition, connect using the distinguished name specified during the installation:
Now it’s possible to create a new object in the directory using the context menu in the content pane:
After specifying the class, we get to specify the “CN” name (for common name) of the object. In this case, I’ll use my full name:
We can also set additional attributes, as shown below (using the “physicalDeliveryOfficeName” to specify the office number of the user):
After clicking Set, closing the Attributes dialog and clicking Finish to create the object, we see it pop up in the items view of the ADSI editor snap-in:
Programmatic population of the directory
Obviously we’re much more interested in a programmatic way to program Directory Services. .NET supports the use of directory services and related protocols (LDAP in particular) through the System.DirectoryServices namespace. In a plain new Console Application, add a reference to the assembly with the same name (don’t both about other assemblies that deal with account management and protocol stuff):
For this sample, I’ll also assume the reader got a Northwind SQL database sitting somewhere and knows how to get data out of its Employees table as rich objects. Below is how things look when using the LINQ to SQL designer:
We’ll just import a few details about the users; it’s left to the reader to map other properties onto attributes using the documentation about the user directory services class. Just a few lines of code suffice to accomplish the task (assuming the System.DirectoryServices namespace is imported):
static void Main()
var path = "LDAP://bartde-hp07/CN=Employees,DC=Northwind,DC=local";
var root = new DirectoryEntry(path);
var ctx = new NorthwindDataContext();
foreach (var e in ctx.Employees)
var cn = "CN=" + e.FirstName + e.LastName;
var u = root.Children.Add(cn, "user");
u.Properties["employeeID"].Value = e.EmployeeID;
u.Properties["sn"].Value = e.LastName;
u.Properties["givenName"].Value = e.FirstName;
u.Properties["comment"].Value = e.Notes;
u.Properties["homePhone"].Value = e.HomePhone;
u.Properties["photo"].Value = e.Photo.ToArray();
After running this code – obviously changing the LDAP path to reflect your setup – you should see the following in ADSI Edit (after hitting refresh):
Now it’s just plain easy to write an application that visualizes the employees with their data. We’ll leave that to the UI-savvy reader (just to tease that segment of my audience, I’ve also imported the employee’s photo as a byte-array).
A small preview of what’s coming up
To whet the reader’s appetite about next episodes on this blog, below is a single screenshot illustrating something – IMHO – rather cool (use of LINQ to Active Directory is just an implementation detail below):
Note: What’s shown here is the result of a very early experiment done as part of my current job on “LINQ to Anything” here in the “Cloud Data Programmability Team”. Please don’t fantasize about it as being a vNext feature of any product involved whatsoever. The core intent of those experiments is to emphasize the omnipresence of LINQ (and more widely, monads) in today’s (and tomorrow’s) world. While we’re not ready to reveal the “LINQ to Anything” mission in all its glory (rather think of it as “LINQ to the unimaginable”), we can drop some hints.
Stay tuned for more!