File to LDIF

Presentation

This script can map any CSV field or LDIF entries attributes to one or more LDIF entries attributes, with specific rules for DN generation and multi-values management.

This is written in Perl and uses:

  • Net::LDAP::LDIF

  • Text::CSV (only if CSV as input file)

  • Text::Unaccent (only if (fmail) macro is used)

Configuration

You have to edit the script to modify the configuration:

$ vi file2ldif.pl

Parameters are:

  • csv_delimiter: separator of CSV fields

  • csv_strip_headers: set to 1 if CSV file has columns name at first line

  • beginc: begin delimiter for field container

  • endc: end delimiter for field container

  • map: all task and mapping rules (see below)

You can define tasks to manage different CSV file structures. For example you can have one CSV file for users and another for groups. So the task name is the first level in the hash map, such as “person” and “group” in this example:

my $map => {
    person => { ... }
    group => { ... }
};

The task name is important because it will be used as a command line parameter to the script.

Then, in each task, you will define the mapping with the Net::LDAP::Entry syntax.

You will map fields with {i}, i being the field number for CSV, and the attribute name for LDIF (you can adjust begin and end delimiter in parameters $beginc and $endc).

CSV example

If you have a CSV file with 3 columns, you can use {0}, {1} and {2} to map them. Here are two CSV file examples:

  • example-users.csv:

    Login;"First Name";"Last Name"
    coudot;Clément;OUDOT
    jclarke;Jonathan;CLARKE
    
  • example-groups.csv:

    Group name;Default member
    group1;coudot
    group2;jclarke
    

Below is a sample configuration to parse those files:

my $map = {
    person => {
        dn => 'uid={1},ou=users,dc=example,dc=com',
        objectClass => [ 'top', 'person', 'organizationalPerson', 'inetOrgPerson' ],
        uid => '{1}',
        givenName => '{2}',
        sn => '{3}',
        cn => '{2} {3}',
    },
    group => {
        dn => 'cn={1},ou=groups,dc=example,dc=com',
        objectClass => [ 'top', 'groupOfUniqueNames' ],
        cn => '{1}',
        uniqueMember => 'uid={2},ou=users,dc=example,dc=com',
    },
};

LDIF example

Here is a sample entry from an LDIF extract of users:

dn: cn=clide,ou=users,dc=acme,dc=com
objectClass: top
objectClass: person
cn: clide
sn: barrow
employeeType: gangster

You can create one task to reformat the user entry, and another to add the user in a group (whose name depends on employeeType attribute):

my $map = {
    person => {
        dn => 'uid={cn},ou=users,dc=example,dc=com',
        objectClass => [ 'top', 'person', 'organizationalPerson', 'inetOrgPerson' ],
        uid => '{cn}',,
        sn => '{(uc)sn}',
        cn => '{cn}',
    },
    group => {
        change_op => 'add',
        dn => 'cn={employeeType},ou=groups,dc=example,dc=com',
        uniqueMember => 'uid={cn},ou=users,dc=example,dc=com',
    },
};

Change operations

By default, generated LDIF uses a changetype: add, this means you want to add complete entries.

You can set a third command line parameter to use other changetypes:

  • modify

  • delete

  • modrdn

In modify case, you can add the change_op field inside a task to determine which change operation will be performed:

  • add

  • delete

  • replace

Macros

You can use macros in mapping, for example to lower-case values.

Available macros are:

  • lc: lower case

  • lcfirst: lower case first letter

  • uc: upper case

  • ucfirst: upper case first letter

  • ucfirstlc: lower case all and upper case fisrt letter

  • fmail: format string to fit mail address (lower case, replace spaces by - and remove accents)

Example:

$map => {
    person => {
        change_op => replace,
        dn => 'uid={(lc)uid},ou=users,dc=example,dc=com,
        mail => '{(fmail)givenname}.{(fmail}sn}@example.com',
    }
};

Run

You can run the script like this:

$ perl file2ldif.pl <taskname> <file> <changetype>

It will produce a taskname.ldif file.

For our CSV examples, this will be:

$ perl file2ldif.pl person example-users.csv
$ perl file2ldif.pl group example-groups.csv

And resulting LDIF are:

  • person.ldif:

    dn: uid=coudot,ou=users,dc=example,dc=com
    uid: coudot
    cn:: Q2zDqW1lbnQgT1VET1Q=
    sn: OUDOT
    objectClass: top
    objectClass: person
    objectClass: organizationalPerson
    objectClass: inetOrgPerson
    givenName:: Q2zDqW1lbnQ=
    
    dn: uid=jclarke,ou=users,dc=example,dc=com
    uid: jclarke
    cn: Jonathan CLARKE
    sn: CLARKE
    objectClass: top
    objectClass: person
    objectClass: organizationalPerson
    objectClass: inetOrgPerson
    givenName: Jonathan
    
  • group.ldif:

    dn: cn=group1,ou=groups,dc=example,dc=com
    cn: group1
    uniqueMember: uid=coudot,ou=users,dc=example,dc=com
    objectClass: top
    objectClass: groupOfUniqueNames
    
    dn: cn=group2,ou=groups,dc=example,dc=com
    cn: group2
    uniqueMember: uid=jclarke,ou=users,dc=example,dc=com
    objectClass: top
    objectClass: groupOfUniqueNames
    

Real life use cases

Create test entries

Imagine you want to create 1000 test entries with different login and password.

First, use a simple loop to generate a CSV file:

$ echo "login;password" > test-users.csv
$ for i in `seq 1 1000`; do echo "user$i;password$i" >>  test-users.csv; done

Then configure the mapping in file2ldif.pl script:

my $map = {
    test => {
        dn => 'uid={1},ou=users,dc=example,dc=com',
        objectClass => [ 'top', 'person', 'organizationalPerson', 'inetOrgPerson' ],
        uid => '{1}',
        sn => '{1}',
        cn => '{1}',
        userPassword => '{2}',
    },
};

And run:

$ perl file2ldif.pl test test-users.csv

Add the users in you directory:

$ ldapadd -D cn=manager,dc=example,dc=com -W -f test.ldif

Replace an attribute for all users

Imagine you want to change to employeeType value of some users from “gangster” to “cop”.

First you will get an LDIF of concerned users:

$ ldapsearch -b ou=users,dc=example,dc=com '(employeeType=gangster)' -LLL > gangsters.ldif

Then configure the mapping in file2ldif.pl script:

my $map = {
    justice => {
        change_op => 'replace',
        dn => '{dn}',
        employeeType => 'cop',
    },
};

And run:

$ perl file2ldif.pl justice gangsters.ldif modify

Apply changes in your directory:

$ ldapmodify -D cn=manager,dc=example,dc=com -W -f justice.ldif

Download

This script, along with all ldap scripts from this site, can be downloaded from the LTB repository.