An Introduction to perl-ldap

A beginner's guide to using Net::LDAP
Adding Entries - add()

If you have a large number of users on your system, it's likely you do not want to add each new user to the system one by one, through a GUI. So, one of the first things you write is a script that quickly adds lots of users in bulk. Or, perhaps you'll write a Web-based system where users can enter their personal information for themselves, with LDAP entries being added automatically. The add() method is used for adding an entry to the database:

$result = $ldap->add("uid=john,ou=People,dc=leapster,dc=org", 
                attr => [ 'cn' => 'John Smith',
                          'sn' => 'Smith',
                          'uid' => 'john',
                          'givenName' => 'John',
                          'homePhone' => '555-2020',
                          'mail' => '',
                          'objectclass' => [ 'person', 'inetOrgPerson']

The above snippet of code adds a user named John Smith to our database. As you can see, the attributes are provided as a list to the attr parameter. Any attribute to which you wish to give multiple values should have them supplied as a list (in our example above, objectclass is such an attribute).

We're now at the point where we can write a simple script to add our large number of users in bulk, a script I've called ldap_addusers.

use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
while(<>) {
        chomp $_;
        ($uid,$givenName,$sn,$mail) = split(/:/,$_);
        $cn="$givenName $sn";
        $result = $ldap->add($dn,
                attr => [ 'uid' => $uid,
                          'cn' => $cn,
                          'sn' => $sn,
                          'mail' => $mail,
                          'givenName' => $givenName,
                          'objectclass' => [ 'person', 'inetOrgPerson']
        $result->code && warn "error: ", $result->error;

The above script takes a colon-separated file of users, one per line, on stdin:

So, if these names are stored in a file called userlist, we can enter them into our LDAP database with the ldap_addusers script, as follows:

cat userlist | ./ldap_addusers

There's one line in the ldap_addusers script that we haven't seen before:

$result->code && warn "error: ", $result->error;

As mentioned earlier, the add() method returns an object of type Net::LDAP::Message. Here, this object is referenced by $result. $result->code is the code value returned from the LDAP server in the result message after a query (in this case, the request to add an entry). Generally, when the request is successful, a zero is returned. Hence, in our above statement, the warning is issued only when $result->code is not zero.

Some other useful methods in Net::LDAP::Message are:

$result->dn        The DN contained in the result message
$result->error     The error message in the result (only if there was an error)
$result->done      True if the request was completed
$result->is_error  True if the particular result is an error for the operation

For a full description of the other methods, see the perldoc manual for Net::LDAP::Message.

perl-ldap also provides the delete() method for deleting entries, when given their DN:


This would, for example, allow you to write a script to do a bulk delete of expired users from your system:

use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
while(<>) {
        chomp $_;

This script deletes all users whose uids are fed to it on standard input, one per line. Be careful when using this script on production servers; if you accidentally feed it the wrong file, you may find yourself with no users left in your directory.

Searching for Entries - search()

It wouldn't be much use only to be able to write to our LDAP server; we need to be able to read from it also. The perl-ldap search command is used to perform lookups on an LDAP server.

$mesg = $ldap->search(filter=>"(uid=paul)", base=>"dc=leapster,dc=org");

The base parameter specifies the base object entry from which the search will be made. In the example above, it searches the entire LDAP tree. The search could be confined to only the ou=People branch with:

$mesg = $ldap->search(  filter=>"(uid=paul)", 

A number of other optional parameters are available to the search() method:

  • scope : This can be one of the following:

    • base: Search the base object only

    • one: Search only the entries one level below the base

    • sub: Search the entire subtree below the base

    If I knew no subtrees were below ou=People--or if there were, but I didn't want results returned from them--I could use:

    $mesg = $ldap->search(  filter=>"(uid=paul)", 
  • timelimit: Set a limit in seconds on the amount of time that a request may take. The default is 0, which signals that the time is unlimited.

  • attrs: Set the attributes (as a reference to an array) that should be returned in the search. If not provided, the search returns all of the attributes. For example, the following search returns only the uid, sn and givenName attributes:

    $mesg = $ldap->search(  filter=>"(uid=paul)", 
                            attrs=> ['uid', 'sn', 'givenName'] );

  • filter: The filter may be a string, in standard LDAP filter format (see the ldap_search(3) man page for a description of this), or it may be a Net::LDAP::Filter object (see the Net::LDAP:Filter man page for further information).

The search() method returns Net::LDAP::Search objects. The easiest way to get at the contents of this object is to use its entries() method, which returns an array of Net::LDAP::Entry objects (see below):

@entries = $mesg->entries;

The Net::LDAP::Search object also has a number of other useful methods:

$mesg->count;           The number of entries returned in the search
$mesg->entry(n);        Returns the n'th entry (initial entry is 0)
$mesg->sorted([list])   Returns a list of entry objects sorted by attr list

Now, we can write a small script to list every entry in the directory:

use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
$mesg = $ldap->search(filter=>"(objectClass=*)", base=>"dc=leapster,dc=org");
@entries = $mesg->entries;
foreach $entry (@entries) {

I've cheated a little in the above script and used the dump() method of Net::LDAP::Entry, in order to make things clearer. dump() primarily is used for debugging; it merely dumps the DN and contents of an entry straight to standard output, without allowing for any manipulation of the results.

The most commonly used methods of the Net::LDAP::Entry object are:

  • attributes: returns the list of attributes contained in this entry.

    @attrs = $entry->attributes();

  • dn: Returns the DN of the current entry. If given with a parameter, it sets the DN of the entry:

    $dn = "uid=pbd,ou=Users,dc=leapster,dc=org";

  • get_value: obtains the value or values for the attribute, given as a parameter. If this method is used to assign to a scalar variable, it returns only the first value for the attribute; if used with an array, it returns all of the attributes.

    $phone = $entry->get_value("homePhone");  # returns only one phone number
    @phone = $entry->get_value("homePhone");  # returns all phone numbers for entry

  • add, delete, modify: These methods allow changes to be made to the entry and are discussed further in the next section.

  • update: pushes any changes made to the entry to the LDAP server (whose object is given as a parameter):

    $entry->add(homePhone => "555 3034");

Now that we've examined the Net::LDAP::Entry object, we can expand the above script further. We can write out the contents of the entries ourselves:

use Net::LDAP;
$ldap = Net::LDAP->new("localhost");
$ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");
$mesg = $ldap->search(filter=>"(objectClass=*)", base=>"dc=leapster,dc=org");
@entries = $mesg->entries;
foreach $entry (@entries) {
        print "dn: " . $entry->dn() . "\n";
        @attrs = $entry->attributes();
        foreach $attr (@attrs) {
                printf("\t%s: %s\n", $attr, $entry->get_value($attr));


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Awesome Work

Vineet Niranjan's picture

Awesome codes and illustrations.
Would have appreciated more if the Acronyms were clear.

How to add UserAccountControl

Anonymous's picture

How to add user ACcount Control Parameter ,it not working ?


Anonymous's picture

Cannot figure what the value of UID should be? I can dump all the contents with filter set as (objectclass=*) and get around 200 entries ; But, when I try to search for a particular UId, I do not get any results back. can anyone tell me what value should be put in for UID to search? I tried with filter=>"(uid=vinda)" filter=>"(uid=vinda norman)" filter=>"(uid=norman)" but no luck.

dn: CN=vinda Norman,OU=Users,OU=SysStaff,OU=SBCS,DC=ad,DC=cs,DC=sunysb,DC=edu
objectClass: top
cn: Vinda Norman
sn: Norman
givenName: Vinda
distinguishedName: CN=Vinda Norman,OU=Users,OU=SysStaff,OU=SBCS,DC=ad,DC=cs,DC=sunysb,DC=edu
instanceType: 4
whenCreated: 20080904132500.0Z
whenChanged: 20090906023318.0Z
displayName: Vinda norman
uSNCreated: 9019
memberOf: CN=System Staff Users,CN=Users,DC=ad,DC=cs,DC=sunysb,DC=edu
uSNChanged: 778762
name: Vinda Norman
objectGUID: *nÄsM¹lN_Aö
userAccountControl: 512
badPwdCount: 0
codePage: 0
countryCode: 0
badPasswordTime: 128975058670944212
lastLogoff: 0
lastLogon: 128975753602883244
pwdLastSet: 128965394154875698
primaryGroupID: 513
objectSid: ªl²p!Xù{®MZ
accountExpires: 9223372036854775807
logonCount: 267
sAMAccountName: vinda
sAMAccountType: 805306368

What happens if bind fails

Am's picture

$mesg = $ldap->bind("cn=admin,dc=leapster,dc=org", password=>"secret");

I tried this, but even with a wrong password, this line did not give an error.

you mean it still connects

Anonymous's picture

you mean it still connects to LDAP?

This tutorial has helped me

Ldap_guy's picture

This tutorial has helped me created a whole project in one week. This was exactly what i needed! Good job

use warnings; use strict;

Anonymous's picture

use warnings;
use strict;

perl-ldap vs. PerLDAP

Anonymous's picture

Not be be confused with another project, PerLDAP, which started back when Netscape was king. I had used PerLDAP for years, before perl-ldap even existed. It's now part of the Mozilla Foundation and is available here:

The one downside is that it requires the Netscape Directory SDK. But it's free and available for almost any platform.

PerLDAP came after perl-ldap

Anonymous's picture

PerLDAP was originally Net::LDAPapi by Clayton Donley. Netscape announced taking over the module and renaming it PerLDAP at the second perl conference, which was held in San Jose in August 1998.

perl-ldap (Net::LDAP) and Net::LDAPapi projects were both started in 1997 about the same time.

PerLDAP can be difficult to get working though!

Anonymous's picture

I've been trying to get Bugzilla to work with PerLDAP, and while I can successfully compile the Netscape directory code, I can't get PerLDAP itself to compile - too many miconfigurations it appears, maybe some problems with versioning b/w PerLDAP & Netscape's SDK. Anyway, I gave up, and I now use Paul's patch to bugzilla which allows bugzilla to work with Net::LDAP. Cheers Paul.