Active Directory‎ > ‎

Ajout d'étudiants dans AD

Contexte :

  • Je dispose d'une base de données MySQL avec une table dénormalisée d'enregistrements représentant des étudiants avec des champs renseignants sur l'OU ou le groupe windows dans lequel il faudra les ranger dans AD et une table contenant les noms des diplômes qui vont servir à nommer les groupes windows.
  • J'ai aussi accès à Active Directory sur le port 636 (ldaps), chose possible après installé un certificat sur le serveur Windows.

Objectif :

  • Je veux mettre à jour à partir des enregistrements de ma base les entrées Active Directory qui existeraient déjà et créer celles qui manqueraient, le tout dans les OU adéquates. Je veux ensuite mettre à jour les groupes Windows, déjà existants sur le serveur car des stratégies de groupe y sont associées, avec les entrées traitées.
Validé : avec ruby 1.8.6 et 1.8.7 sur Ubuntu 8.04 et 9.10

require 'mysql'
require 'ldap'
require 'iconv'

# Pour sortir le résultat en unicode
$KCODE = 'u'

# paramètres LDAP et attributs pour le domaine AD
$HOST =    'mon.serveur-ActiveDirectory.net'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT
scope = LDAP::LDAP_SCOPE_SUBTREE
dnAD = 'CN=Administrateur, CN=Users, DC=dometud, DC=iut-rodez, DC=local'    # dn de l'Administrateur du domaine AD
passwdAD = 'SuperSecret'                                                    # mot de passe de l'Administrateur
ou_etudiants = 'ou=ETUTEST,dc=dometud,dc=iut-rodez,dc=local'                # OU générale vont être créés les comptes dans des sous-OU
prefixe_groupe = 'EtudiantsTest '                                           # préfixe qu'on placera dans les noms des groupes windows d'étudiants
prefixe_partage = "\\\\drac\\"                                              # préfixe d'un lien dfs qu'on ajoutera dans l'attribut homeDirectory de chaque étudiant
domain_AD = "@dometud.iut-rodez.local"                                                                            # domaine Active Directory    
home_drive = "Z:"                                                                                                                      # lettre du disque à monter pour le homeDirectory

# paramètres de connexion à la base
hostname = 'mabase.iut.rdz'                                                   # nom du serveur MySQL
username = 'mysql_reader'                                                     # compte en lecture sur la base
password = 'mysql_paswd'                                                      # mot de passe du compte
databasename = 'iut'                                                          # nom de la base

# connexion à la base, préparation et éxecution de la requête de sélection
puts "Connexion à la base de données ..."
my = Mysql.new(hostname, username, password, databasename)

# Tableau de stockage des membres de chaque groupe : etudiants_du_groupe[dn_grp] = [dn_etu1,dn_etu2,...]
# récupération des groupes existants de la table 'etapes'
# initialisation de chaque etudiants_du_groupe[dn_grp] sous forme d'un Array
etudiants_du_groupe = Hash.new
rgp = my.prepare('select groupe, diplome_iut from etapes')
rgp.execute
rgp.each {|gp,ou|
  dn_grp = "cn="+prefixe_groupe+"#{gp},ou=#{ou},"+ou_etudiants
  etudiants_du_groupe[dn_grp] = []
  }

# récupération des étudiants dans 'res'
rs = "select login, display_name, nom, prenom, datenaissance, diplome_iut, groupe, etu_id, partage from etudiants"
rq = my.prepare(rs)
puts "Execution de la requête SQL ..."
res = rq.execute

# compteurs
cpt_lus = 0
cpt_mod = 0
cpt_add = 0


# connexion à l'AD en ldaps, sinon le password n'est pas modifiable
conn = LDAP::SSLConn.new($HOST, $SSLPORT)
conn.bind(dnAD, passwdAD)
conn.perror("Connexion à Active Directory ... ")

# lecture de chaque ligne du résultat de la requête
puts "Tentative de modification et de création d'une entrée dans AD par ligne de résultat de la requête SQL ..."
res.each {|etu_AD|
  # récupération des valeurs des champs de la base pour AD
  login = etu_AD[0]
  displayName = etu_AD[1]
  nom = etu_AD[2]
  prenom = etu_AD[3]
  date_naiss = etu_AD[4]
  ou_AD = etu_AD[5]
  groupe_AD = etu_AD[6]
  etu_id = etu_AD[7]
  partage = etu_AD[8]
  mot_de_passe = '"'+date_naiss.to_s[8,2]+date_naiss.to_s[5,2]+etu_id.to_s[4,4]+'"'
  dn = "cn=#{login},ou=#{ou_AD},"+ou_etudiants
  passwd = Iconv.iconv('UTF-16LE','UTF-8',mot_de_passe).first
    # le password est sous la forme "JJMMXXXX" : JJMM, jours et mois de naiss
    # XXXX 4 derniers chiffres du num étudiant
    # les doubles-quotes sont requis par AD autour du mot de passe
    # conversion en unicode pour windows AD

  home_dir = prefixe_partage+"#{partage}\\#{login}"
  cpt_lus += 1

  # Pour chaque ligne récupérée, on essaie d'abord de mettre à jour l'étudiant correspondant dans l'AD
  # ensuite de le créer (qu'ils existent ou pas dans les deux cas : on échappe l'erreur LDAP).
  # Dans tous les cas, on l'ajoute comme membre à son groupe d'appartenance.
 
  # modification d'un étudiant dans l'AD
  # note : les attributs cn et name ne sont pas modifiables dans l'AD
  etudiant = [
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'displayName',[displayName]),
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'sn',[nom]),
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'givenName',[prenom]),
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'sAMAccountName',[login]),
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'userPrincipalName',[login+domain_AD]),
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'homeDrive',[home_drive]),
    LDAP.mod(LDAP::LDAP_MOD_REPLACE,'homeDirectory',[home_dir])
    # on n'écrase pas la valeur du password qui est peut-être déjà choisie par l'étudiant
    ]
  begin
    conn.modify(dn,etudiant)
    cpt_mod += 1
  rescue LDAP::ResultError
    # décommenter les deux lignes suivantes pour afficher les entrées non modifiées
    #puts "#{displayName}    OU : #{ou_AD}"
    #conn.perror("modify")
  end

  # ajout d'un étudiant dans l'AD
  etudiant = [
    LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['top','person','organizationalPerson','user']),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'cn',[login]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'displayName',[displayName]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'sn',[nom]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'givenName',[prenom]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'name',[login]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'sAMAccountName',[login]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'userAccountControl',["544"]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'userPrincipalName',[login+domain_AD]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'homeDrive',[home_drive]),
    LDAP.mod(LDAP::LDAP_MOD_ADD,'homeDirectory',[home_dir])
    ]
  begin
    conn.add(dn,etudiant)
    cpt_add += 1
    # AD n'autorise le du passwd qu'en mode modification, pas en création
    # LDAP_MOD_BVALUES est requis car la valeur de l'attribut est binaire
    # On crée le password par défaut JJMMXXX sur les nouvelles entrées
    etudiant_passwd = [LDAP.mod(LDAP::LDAP_MOD_BVALUES,'unicodePwd',[passwd])]
    conn.modify(dn,etudiant_passwd)
  rescue LDAP::ResultError
    # décommenter les deux lignes suivantes pour afficher les entrées non ajoutées
    #puts "#{displayName}    OU : #{ou_AD}"
    #conn.perror("add")
  end

  # stockage de l'étudiant comme membre de son groupe dans le hash etudiants_du_groupe[dn_grp]
  dn_grp = "cn="+prefixe_groupe+"#{groupe_AD},ou=#{ou_AD},"+ou_etudiants
  etudiants_du_groupe [dn_grp].push dn
  }

# mise à jour des membres des groupes etudiants
puts "Mise à jour des groupes"
etudiants_du_groupe.each {|dn_gp, liste_etudiants|
  attr_member = [LDAP.mod(LDAP::LDAP_MOD_REPLACE,'member',liste_etudiants)]
  conn.modify(dn_gp,attr_member)
  }
conn.unbind
my.close
puts "nombre d'étudiants lus dans la base : #{cpt_lus}"
puts "nombre d'entrées modifiées dans AD  : #{cpt_mod}"
puts "nombre d'entrées ajoutées  dans AD  : #{cpt_add}"


Comments