Active Directory et sa programmation !

Note : 8,00/10

12345678910
Auteur : TDEMAN Article lu 8 040 fois

Description : Ce document présente les éléments essentiels pour modifier le contenu de la base Active Directory. Exporter les informations dans un format personnalisé, modifier un attribut particulier (téléphone,...).
2 liens vers des exemples sont fournis.




Active Directory et sa programmation (2 exemples).

Etant donné la richesse des informations contenues dans l'annuaire Active Directory, les raisons de vouloir accéder ou modifier le contenu sont nombreuses. La création de rapports personnalisés, l'extraction de listes, la modification de champs précis tels que le nom complet pour les normaliser, les téléphones afin de les adapter à une TOIP sont des exemples pratiques. Le but de cet article est de montrer que « c'est facile, et çà peut rapporter gros ». Non, cela fait surtout gagner beaucoup de temps !

Différents outils sont à connaître avant de partir dans l'inconnu notamment l'outil ADSIEDIT.MSC présent dans les « supports tools ». C'est l'équivalent de « regedit » pour les registres, c'est-à-dire qu'il permet de visualiser les données « brutes » de l'annuaire et du schéma.

Avant de modifier un champ précis, il est important de vérifier la correspondance entre l'affichage dans l'outil « Active Directory Utilisateurs et Ordinateurs ». Modifier le champ sur l'objet souhaité dans l'interface graphique, puis vérifier dans ADSIEDIT l'objet correspondant.

Les administrateurs de systèmes étant rarement des développeurs, c'est l'outil Vbscript que nous utiliserons pour cette présentation. La version « texte » cscript.exe sera utilisée et forcée par la commande « cscript //h :cscript ».

Si le besoin est uniquement de type rapports ou listes sans modifications, on choisira un accès de type base de données utilisant une connexion, un fournisseur de données (« Provider ») et des jeux d'enregistrements « recordsets ». Pour pouvoir modifier , un accès direct à l'objet sera nécessaire. Celui-ci permettra un accès complet en lecture et écrire sur l'objet. Souvent, on retrouvera les 2 types d'accès dans les scripts avancés.

Lors de la réalisation d'un script, l'étendue souhaitée est à définir. Par défaut, on pense souvent au domaine courant. Mais, il est possible de réaliser la modification sur la totalité de la forêt !!! C'est à la fois très pratique et très dangereux en cas de modification.

  • La modification sur un domaine se fera en utilisant le protocole LDAP (389)
  • La modification sur la forêt se fera en utilisant le protocole GC (3268)

Attention, le catalogue global ne contient qu'une petite partie des données. ADSIEDIT permet de vérifier dans le schéma que l'élément souhaité fait bien partie du catalogue global. Si nécessaire, un élément peut être ajouté au catalogue global.

Set oRecordset = oConnection.Execute("<LDAP://"+StrDomainNC+">; (objectCategory=user); name,displayname,mail,telephonenumber; subtree")

Cette instruction permettra de récupérer le nom, le nom complet, l'adresse principale de messagerie et le numéro de téléphone dans le domaine. Il suffira de remplacer LDAP par GC et de s'assurer que StrDomainNC contient la racine de la forêt pour obtenir une liste globale de tous les utilisateurs.

Voici un exemple de boucle sur les enregistrements :

If Not oRecordset.EOF Then

    While Not oRecordset.EOF


' Attention les champs vides (Null) ne peuvent pas être concaténés en chaine de caractères

' Il faut donc les vérifier

Name=VerifyNull(oRecordset.Fields("Name"))

      DisplayName=VerifyNull(oRecordset.Fields("DisplayName"))

      Mail=VerifyNull(oRecordset.Fields("Mail"))

      Telephonenumber=VerifyNull(oRecordset.Fields("telephonenumber"))


      Wscript.echo Name+","+Displayname+","+mail+","+TelephoneNumber


      oRecordset.movenext

Wend

End If


Attention, si vous souhaitez tester la valeur de certains champs, ceux-ci doivent avoir été inclus dans l'instruction « Execute ».


Un exemple complet peut être trouvé à l'adresse suivante :

http://base.faqexchange.info/Documents%20partages/Scripting/VbScript/listUser.vbs.txt

Set oRecordset = oConnection.Execute("<GC://"+StrDomainNC+">;(&(objectCategory=user)(samaccountname="+samAccountName+"));distinguishedName;subtree")

Cette instruction permettra de rechercher le ?DistinguishedName? d'un utilisateur dans toute la forêt. Le DistinguishedName est le nom unique d'un objet qui permettra d'atteindre et de modifier la totalité de l'objet.

Le détail de l'instruction de connexion est très important ! Il s'agit d'une seule chaine de caractères composées de 4 parties séparées par des « ; ».

"<GC://"+StrDomainNC+"> : Cette partie indique le plus souvent le domaine sous la forme ?dc=domaine,dc=Extension?. Mais, on peut très bien ne souhaiter qu'une OU particulière que l'on indiquera sous la forme « Ou=MesUsers,Dc=Domaine,Dc=Extension ».

(&(objectCategory=user)(samaccountname="+samAccountName+")) : Cette partie permet de sélectionner et/ou rechercher des éléments précis dans la base Active Directory avec des ET (&) et des ou (|).

distinguishedName : il s'agit de la liste des champs souhaités séparés par des virgules.

Subtree : Ce dernier élément permet de définir la « portée de la requête.3 niveaux sont possible.

  1. « Subtree » permettra de parcourir tous les sous-niveaux.
  2. « Base » ne regardera que le niveau indiqué.
  3. « One » ne descendra que d'un seul niveau.



Si l'on veut introduire des modifications dans la base Active Directory, on ajoutera une fonction à l'intérieur de la boucle. Les raisons sont nombreuses : normaliser les noms complets (nom/prénoms), les numéros de téléphones lors d'un passage à une TOIP, intégrer des données depuis une base Intranet (ou un autre annuaire).

Nous allons prendre l'exemple de la normalisation du nom complet sous la forme « NOM Prénom ».

Tout d'abord, on sélectionnera les utilisateurs et les contacts dans la requête:

(&(objectCategory=user)(objectCategory=contact))

Si l'on veut tester et exclure une OU précise, on pourra tester les champs dans la boucle :

     if not(isnull(oRecordset.Fields("distinguishedName"))) Then

           DnName=oRecordset.Fields("distinguishedName")

           If Instr(ucase(Dnname),"OU=PASTOUCHER")=0 Then

             u=u+1

             UpdateAccount(DnName)

           End if

         end if

Le champ « distinguishedName » sera transmis en paramètre à la fonction UpdateAccount dont nous allons commenté les éléments principaux :

Function UpdateAccount(DnName)

Dim oUser

wscript.echo dnname

set oUser=GetObject("LDAP://"+DnName)

La function ?GetObject? permettra d'obtenir un pointeur sur l'objet (ici User) sur lequel toutes les fonctions (methodes) seront applicables et tous les champs seront accessibles.

samaccountname=ouser.samaccountname

sn=oUser.sn

givenname=oUser.givenname

DisplayName=Trim(oUser.DisplayName)

Récupération du login, nom, prénom et NomComplet !!!

Si les conditions sont remplies, on pourra définir la nouvelle valeur 

NewDisplayName=Ucase(sn)+" "+givenname

Puis réécrire l'information modifiée dans Active Directory :

oUser.put "DisplayName", NewDisplayName

oUser.setinfo


L'exemple complet se trouve ici : http://base.faqexchange.info/Documents%20partages/Scripting/VbScript/majdisplay.vbs.txt.

Attention, afin d'éviter des problèmes de performances sur les contrôleurs de domaine, les opérations sur Active Directory ont été limitées à 1000 enregistrements. Normalement, il faut adapter son développement afin de traiter les opérations par « page » de 1000 enregistrements, ce qui est assez contraignant. En fait, le plus simple est de modifier ce paramètre sur l'un des contrôleurs de domaine à partir duquel seront lancés tous les scripts. Ce paramètre se change avec l'outil NTDSUTILS dans « ldap policies » après s'être connecté sur le contrôleur de domaine souhaité.


Bien entendu, le but n'est pas de réécrire un script depuis zéro. Mais grâce aux indications et aux exemples fournis ici, vous trouverez probablement de nombreuses applications pratiques. De nombreux forums pourront vous aider à rechercher les informations manquantes.




A Propos de l'auteur

Thierry DEMAN - Architecte Systèmes chez "LE PERMIS INFORMATIQUE"

MS MVP Exchange, MCSE+MSI, MCDBA, MCITP SQL-DEV,SQL-ADM,EXCH,W2K8-ADM (51 MCPs)

http://www.faqexchange.info http://isafirewalls.org

 



[ Voir la fiche de TDEMAN ] - [ Voir tous les articles de TDEMAN ] - [ Contacter TDEMAN ] - [ Visiter le site de TDEMAN ]



Mots définissants ce tutorial

Mot(s) associé(s) :

active directory programmation vbscript




Commentaire(s)

Commentaire de : dfs le 15/03/2007 10:03:27Envoyer un message à dfs
une bonne idee pour les maj
Commentaire de : dbocart le 16/08/2007 15:01:17Envoyer un message à dbocart
bonjour,

Votre script fonctionne très bien sur un AD mais ma société posséde un AD et un annuaire LDAP séparé qui ne contienne pas les meme information,l'annuaire 'LDAP' étant bcp plus complet c'est celui qui m'interresse évidement, j'arrive d'ailleur a y acceder en php et en vba sous excel mais impossible sous vbs pourriez vous me donner un p'tit coup de pouce ???

Ci dessous les scripts en php et vba

nb : TOTO = nom de ma sociéte, je ne prefere pas la mentionner)

Init en VBA sous excel


'Initialisation de la connection
Set conn = New ADODB.Connection
conn.Provider = "ADsDSOObject"
conn.Open "ADs Provider"
mystr = "cn,mail,telephonenumber,ou,l,sitecode"
nbItems = UBound(Split(mystr, ","))

For Each uid In liste
    Set RS = conn.Execute("<LDAP://annuaire.com:389/o=TOTO/ou=users>;" & _
            "(&(objectClass=TOTODominoUser)(uid=" & uid & "));" & mystr)
    Sheets("feuil1").Activate
    For x = 0 To nbItems
        Cells(li, 2 + x).Value = "'" & RS.fields(x).Value
        If RS.fields(x) = Null Then
            Cells(li, 2 + x).Value = "'" & RS.fields(x).Value(0)
            If Len(RS.fields(x).Value(1)) > Len(RS.fields(x).Value(0)) Then
                Cells(li, 2 + x).Value = "'" & RS.fields(x).Value(1)
            End If
        End If
    Next
    li = li + 1
Next

'Entête de colonne
For x = 0 To nbItems
    Cells(1, 2 + x).Value = "'" & RS.fields(x).Name
Next
[A:ZZ].EntireColumn.AutoFit





extrait de l'init en PHP :

$Ldap_Host = 'annuaire.com'; // Adresse du Serveur LDAP TOTO
$Ldap_Port = 389; // Numéro du Port (Valeur Standard)
$Ldap_Basedn = 'ou=users,o=TOTO'; // Définition du Chemin d'un Utilisateur et Organisation pour la Recherche :

global $Ldap_Info;

$Ldap_Cnx = ldap_connect($Ldap_Host,$Ldap_Port); // Tentative de Connexion au Serveur

if ($Ldap_Cnx) { // Connexion Réussie
// $Ldap_Result = ldap_search ($Ldap_Cnx, $Ldap_Basedn, 'cn=*'.$Users.'*');
$Ldap_Result = ldap_search ($Ldap_Cnx, $Ldap_Basedn, $Users);
$Ldap_Info = ldap_get_entries ($Ldap_Cnx, $Ldap_Result);

ldap_close ($Ldap_Cnx); // Fermeture de la Connexion.

} else {
echo 'Impossible de se Connecter au Serveur LDAP -> '.$Ldap_Host;
}
}

en vbs :

j'arrive à afficher les groupes utilisateurs de la facon suivante :
Dim ADSIPath, objOU, objOUMember

ADSIPath = "LDAP://annuaire.com/o=TOTO/ou=groups"
wscript.echo ADSIPath

Set objOU = GetObject(ADSIPath)

For Each objOUMember in objOU
wscript.echo objOUMember.cn
'wscript.sleep(100)
Next

wscript.echo "Export terminé !"



mais pour la meme requete sur les utilisateurs ou=user

j'ai une erreur 'Dépassement de la limite de taille pour cette requête'

la base étant importante (+200000 entrées) c'est peut etre normal,

je ne cherche pas à lister toutes les infos de l'annuaire mais juste les informations d'un utilisateur

Merci d'avance !

Cordialement
BOCART David


Commentaire de : TDEMAN le 16/08/2007 15:38:13Envoyer un message à TDEMAN
Bonjour,

Si la sélection dans AD (ou LDAP)dépasse les 1000 enregistrements, il est nécessaire d'utiliser un mode "paginé" pour la requète.

Voici un exemple d'utilisation d'une commande (paginée par 100) :



' Use ADO to search Active Directory for ObjectClass nTDSDSA.
' This will identify all Domain Controllers.
Set objCommand = CreateObject("ADODB.Command")
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
objCommand.ActiveConnection = objConnection


strBase = "<LDAP://" & strConfig & ">"
strFilter = "(ObjectClass=nTDSDSA)"
strAttributes = "AdsPath"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"


objCommand.CommandText = strQuery
objCommand.Properties("Page Size") = 100
objCommand.Properties("Timeout") = 60
objCommand.Properties("Cache Results") = False

Set objRecordSet = objCommand.Execute
Commentaire de : dbocart le 16/08/2007 17:42:31Envoyer un message à dbocart
Génial, merci beaucoup de votre réactivité et en plus ca marche !

il faut par contre modifier la variable strFilter par ce que l'on recherche

strFilter = "(sn=nom)" par ex

Encore mille merci !

Cordialement


Commentaire de : dodii le 25/12/2007 21:33:33Envoyer un message à dodii
Bonsoir tout le monde
Là je galère avec ce sujet !
J'arrive pas à faire fonctionnner aucun script (vbs|php) pour afficher (et puis ajouter et supprimer) des utilisateur ou des groupes de mon domaine !
commencant par le debut s'il vous plait :
->Set objOU = GetObject("LDAP://annuaire.com/o=TOTO/ou=groups")
1-pour remplacer annuaire.com qu'est ce que je dois mettre l'addresse du serveur , le nom du serveur ou le nom du domaine ?
2-TOTO c'est quoi ? est ce que je dois la remplacrer par un truc ou la laisse.
3-ou=groups c'est quoi ? est ce que je dois la remplacrer par un truc ou la laisse.
4- est ce que je dois executer ce script directement sur le serveur ?!

Aidez moi s'il vous plait.
Merci et joyeux NOEL :)
Commentaire de : TDEMAN le 06/01/2008 12:53:09Envoyer un message à TDEMAN
Hello, Meilleurs voeux à tous pour cette nouvelle année 2008!

Les scrips LDAP/VBS peuvent s'exécuter aussi bien sur les serveurs que sur les clients à condition de posséder les droits nécessaires.

L'adresse du serveur n'est pas nécessaire dans le monde Windows. Annuaire.com doit correspondre au nom du domaine/AD que l'on veut modifier.

/o=TOTO est probablement une erreur de recopie, il doit manquer un "U"!( Il ne s'agit pas de mon script initial, mais d'une partie de script inséré dans un commentaire...)

/OU=Groups correspond à une OU qui s'appelerait "GROUPS" et qui se trouverait à la racine du domaine/AD.

/OU=TOTO correspond à un Sous-OU qui se trouverait juste en dessous.

A bientôt

Ajouter un commentaire :

Pour ajouter un commentaire, vous devez vous identifier :
Si vous n'avez pas encore de compte sur un des sites TechnoS-SourceS / CodeS-SourceS cliquez ici pour créer votre compte.

Login et mot de passe que vous avez sur
CodeS-SourceS/TechnoS-SourceS




Mot de passe oublié ? / Activation de compte
Créer un compte