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, genertad 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 oepration 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