Skip to main content

Recently I needed to do a bit of coding for a project which needed a lookup system into Active Directory written in Perl, this post explorers the excitement (read: pain) that I had with this bit of coding. I haven't touched Perl since for a few years but development is like riding a bike, isn’t it?

Setup

First problem is I used to “ride” a Linux bike where Perl is part and parcel of the world, now this Windows 2008 Server which I use on my laptop it’s another story. So step one is getting Perl for Windows, and thankfully the parts of my memory not (yet) destroyed by beer remembered about Active Perl from Active State which had a Windows version (this takes me back almost a decade to when I last used Active Perl, go strong brain cells, go!). The download and install of it was very painless, it just did it’s thing.

Next up was getting an IDE in place just to get up and running, so I found Perl Express which is a free, small IDE for Windows. It has a built in debug environment which is all I really needed. When I get to the later stuff I was confusing it with my “small” script. Lots of people have told me to get Slick Edit instead as it is much better, but it costs money and I need this for a short while. If I had to do this daily I would invest in a real one.

Install NET:LDAP Attempt 1

Next up was finding out how Perl can talk to AD. I decided that there must be LDAP support so I would look for that and found a great series on using NET::LDAP (the link goes to Bundle::NET::LDAP, but I’ll explain that later) when working with AD, you can read part 1 of that here. So I fired up Perl Express and tried the first sample and it died saying I do not have NET::LDAP. So off to find out how to get the module, which turns out needs to be obtained via the command line by typing

perl -MCPAN -e "install Net::LDAP"

However since it has dependencies it is better to get the bundle pack with everything in it so you type

perl -MCPAN -e "install Bundle::Net::LDAP"

This will download, compile and install the module. The first time I ran the command it failed because my proxy at work was blocking me, so time for the trusty 3G connection.

Install NET:LDAP Attempt 2

Run the command for a second time and it starts to fail with this pop-up:

image

See it needs to do a compile and when it can’t find NMake it downloads it from Microsoft. However it downloads the old 16bit version and that doesn’t run on 64bit Windows 2008 :( The internet suggested I download the Windows SDK since it had a 32bit and I found it at: Windows SDK for Windows Server 2008 and .NET Framework 3.5 but it is huge (min of a couple hundred Megs of download). But I thought I have Visual Studio installed, surely that has a copy?

Thankfully, my search kung fu (I have to stop calling skills Kung Fu - watching X-Files is doing this to me) is not limited to the in#ternets and I found a local version. To use it I needed to run the following at the command prompt PRIOR to running my perl install:

"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"

Install NET:LDAP Attempt 3

Right, attempt number 3. This failed due to missing dependencies, you’d think when it comes up and asks you

==> Auto-install the 1 optional module(s) from CPAN? [n]

for said dependency it would do that when you say yes? Doesn’t seem so. It seems it is just a test to how many times you will run the tool.

Install NET:LDAP Attempt 4 & 5

*sigh* So now I will try and install each dependency manually first, so the commands look like this:

"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"

perl -MCPAN -e "install GSSAPI"

perl -MCPAN -e "install IO::Socket::SSL"

perl -MCPAN -e "install XML::SAX::Writer"

perl -MCPAN -e "install Bundle::Net::LDAP"

This still complained about issues, so I run each one again (ignoring errors - ignorance is bliss) and when prompted I choose the default of no, and somehow even with errors (I think they are errors, maybe I just not used to reading these message) there in C:\Perl\site\lib\ appeared a NET folder with an LDAP.pm file in it!

5 Attempts and it is installed - yeah just like riding a bike :/

Finding my Domain Controller

The first thing I need to provide to NET::LDAP is my domain controller, which I don’t know so instead of logging a call with IT and waiting for a response I decided to figure it out myself. Thankfully I found a page on Microsoft’s site detailing How Domain Controllers are located in Windows which included directions on troubleshooting problems. One of those was manually testing with the command line

nltest /dsgetdc:<your domain>

which provided exactly what I needed!

Control Construction Step 1 - Getting the details

The first part of  building my lookup control was to be able to query AD for all users and just list it back. I defined a user as someone with a surname greater than zero and with a username, if you had those you are a user and not a machine or system account (yes, SkyNet I’m looking for you).

The script in the end looked like this:

#!/usr/bin/perl
 
use strict;
use Net::LDAP;
use Net::LDAP::Control::Sort;
 
#Start of user configurable settings
my $DomainController = "<DOMAIN CONTROLLER>";
my $Username = "<USERNAME@DOMAINNAME>";
my $Password = "<PASSWORD>";
my $BaseOU = "<BASE OU>";
#End of user configurable settings
#Start of system settings
my $Attributes = "sAMAccountName,sn,displayName";
my $Filter = "(objectCategory=User)" ;
#End of system settings
 
print "Going to attempted to connect to ".$DomainController;
 
my $ad = Net::LDAP->new($DomainController)
                or die "Could not connect!";
 
$ad->bind($Username, password=>$Password);
 
my $sort = Net::LDAP::Control::Sort->new(order => "displayName");
 
 
my $results = $ad->search(base=>$BaseOU, filter=>$Filter, attrs=>$Attributes, control=>[$sort]);
if ($results->count == 0)
{
  die "No results returned";
}
else
{
    for (my $counter=0; $counter<$results->count; $counter++)
        {
            my $user = $results->entry($counter);
                if (defined($user->get_value("sn")) && length($user->get_value("sn")) > 0 && defined($user->get_value("sAMAccountName")))
                {
                    print "\nUser Found:".$user->get_value("displayName")." (".$user->get_value("sAMAccountName").")";
                }
        }
}
 
$ad->unbind;
 
print "\nDone";

Since I don’t deserve to be called a Perl programmer (I have no skills here), this Perl Hacker did rely on the internet for help. The sites that helped me were:

Control Construction Step 2 - The HTML

Since this will be a web page what I would like to do is have a page popup when you click on a text box which will let you select a user. When ever I have these scenarios I like to simply the development by creating an HTML file or two on my desktop and getting the JavaScript to work on a very tightly built scenario. This way I get the thinking and structure right without worrying about bugs being caused because of data or the perl code. Since I am going to use JavaScript it means I need to grab a copy of everyone’s (maybe not everyone, but at least me, the guys at End User SharePoint and Microsoft) favorite JS library, jQuery. That is followed by a wander through the plug-in section on jQuery to see if anyone has done what I need already (hey, I’m a busy man and don’t have time to reinvent every wheel), besides the WOW factor of some of those demo’s is just so high I get all geeked out by it. Thankfully I found Eric Martin’s wonderful Simple Modal plug-in which allowed me to put together a nice mashup of the HTML and “pop-ups”.

 

You can grab the source code for that mashup by clicking download below (you’ll need 7-zip to open it).