Re: [Net::LDAP] Re: [Net::LDAP] LDAP and AD sync

Peter Karman <[email protected]>
Newsgroups gmane.comp.lang.perl.modules.ldap
Message-ID <[email protected]>
Peter Karman wrote on 12/10/09 5:48 PM:
> Prashanth Sundaram wrote on 12/10/09 4:59 PM:
>>    Folks,
>>
>> I am a n00b to perl scripting and need help to start building my own. 
>> I am
>> currently working on a project where the LDAP(389-ds) and Active 
>> Directory
>> are always in sync.  I have a very minimal set of attributes and 
>> conditions
>> to keep them in sync.
>>
>> Can anyone share their code, so that I can build around it? Here¹s my
>> requirement:
>> * Sync New users from AD to LDAP with attributes: sAMAccountName, sn,
>> givenName, description, userAccountControl(disable/enable),
>> * Delete LDAP accounts which are not present in AD and vice versa.
>> * Generate the next available uidnumber by parsing thru ldap, so new 
>> users
>> can be created * Check memberOf for 2 groups and if true add them to 
>> corresponding groups
>> in LDAP
>>
>> If you have any of these modules written already, that would be great 
>> help.
>> I am digging through the archive looking for related code.
> 
> Net::LDAP::Class should make most if not all of those requirements 
> pretty easy to implement. It was written to ease keeping a rdbms, LDAP 
> and AD all in sync.
> 
> 

I suppose I should back that up with some actual code.

I would set it up like this. These files:

  lib/MyLDAP/User.pm
  lib/MyLDAP/Group.pm
  lib/MyAD/User.pm
  lib/MyAD/Group.pm

Containing code like this. (NOTE the code is *NOT* tested).

lib/MyLDAP/User.pm:

  package MyLDAP::User;
  use base qw( Net::LDAP::Class::User::POSIX );

  __PACKAGE__->metadata->setup(
      base_dn             => 'dc=yourcompany,dc=com',
      attributes          => __PACKAGE__->POSIX_attributes,
      unique_attributes   => __PACKAGE__->POSIX_unique_attributes,
  );

  sub init_group_class { 'MyLDAP::Group' }

  1;

lib/MyLDAP/Group.pm:

  package MyLDAP::User;
  use base qw( Net::LDAP::Class::Group::POSIX );

  __PACKAGE__->metadata->setup(
      base_dn             => 'dc=yourcompany,dc=com',
      attributes          => __PACKAGE__->POSIX_attributes,
      unique_attributes   => __PACKAGE__->POSIX_unique_attributes,
  );

  sub init_user_class { 'MyLDAP::User' }

  1;

lib/MyAD/User.pm:

  package MyAD::User;
  use base qw( Net::LDAP::Class::User::AD );

  __PACKAGE__->metadata->setup(
      base_dn             => 'dc=yourcompany,dc=com',
      attributes          => __PACKAGE__->AD_attributes,
      unique_attributes   => __PACKAGE__->AD_unique_attributes,
  );

  sub init_group_class { 'MyAD::Group' }

  1;

lib/MyAD/Group.pm:

  package MyAD::Group;
  use base qw( Net::LDAP::Class::Group::AD );

  __PACKAGE__->metadata->setup(
      base_dn             => 'dc=yourcompany,dc=com',
      attributes          => __PACKAGE__->AD_attributes,
      unique_attributes   => __PACKAGE__->AD_unique_attributes,
  );

  sub init_user_class { 'MyAD::User' }

  1;



Now you have your .pm class files set up, and you can write a script (or more 
likely, multiple scripts, one per action) to use them to handle all your 
requirements:

  # example to sync between AD and LDAP based on uid/sAMAccountName (username)
  use strict;
  use warnings;
  use lib 'lib';
  use Net::LDAP;
  use MyLDAP::User;
  use MyLDAP::Group;
  use MyAD::User;
  use MyAD::Group;

  # create all the ldap connections and bind
  my $ldap_dn   = 'your-authenticated-user-dn';
  my $ldap_pass = 'your-authenticated-user-password';
  my $ldap_host = 'ldap://yourldapserver';
  my $ldap      = Net::LDAP->new($ldap_host);

  my $ad_dn     = 'your-authenticated-user-dn';
  my $ad_pass   = 'your-authenticated-user-password';
  my $ad_host   = 'ldap://youradserver';
  my $ad        = Net::LDAP->new($ad_host);

  # could use ssl cert here too instead of password
  my $mesg = $ldap->bind( $ldap_dn, password => $ldap_pass );
  $mesg->code and die Net::LDAP::Class->get_ldap_error($mesg);
  $mesg    = $ad->bind( $ad_dn, password => $ad_pass );
  $mesg->code and die Net::LDAP::Class->get_ldap_error($mesg);

  # iterate over all LDAP users, checking if they are in AD
  my $num_ldap_checked = MyLDAP::User->act_on_all(
      \&check_ldap_users,
      { ldap => $ldap }
  );

  sub check_ldap_users {
      my $ldap_user = shift;
      my $ad_user = MyAD::User->new( ldap => $ad, username => "$ldap_user" );
      if (!$ad_user->read) {
          warn "$ldap_user is not in AD!";

          # decide what action should be taken. Delete from ldap?
          $ldap_user->delete;

          # or create a AD user?
          # add them with whatever attributes you need
          # see the perldoc for Net::LDAP::Class::User::AD
          # ...
          # then save to AD
          $ad_user->create or die "can't create AD user $ad_user";
      }
  }

  # now do the reverse, checking AD
  my $num_ad_checked = MyAD::User->new(
      \&check_ad_users,
      { ldap => $ad }
  );

  sub check_ad_users {
      my $ad_user = shift;

      # similar to above in check_ldap_users.
  }

  print "Checked $num_ldap_checked LDAP users and $num_ad_checked AD users\n";
  exit(0);



The basic idea is that you can set up .pm classes to handle all the dirty work 
of managing user/group relationships, setting attributes, reading/writing from 
the server, etc, and then use those classes over and over in your management 
scripts.

-- 
Peter Karman  .  http://peknet.com/  .  [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.