Auslesen von Sub-CNs in Active Directory

  • VB.NET
  • .NET (FX) 3.0–3.5

Es gibt 51 Antworten in diesem Thema. Der letzte Beitrag () ist von jnL.

    Auslesen von Sub-CNs in Active Directory

    Hallo Community,

    ich habe das Problem, dass ich Sub-CNs in einem Active Directory nicht auslesen kann - ich komme einfach nicht an die richtige Lösung dran, wie ich das umsetzen muss.

    Mehr Informationen:
    Es gibt eine Form, auf der Textfelder vorhanden sind (siehe Anhang)
    Nach Eingabe der Anmeldeinformationen, des Hostnamens (hier bspw. ABCD1234) und Klick auf Suchen, baut das Tool erfolgreich die Verbindung zum AD unter der angegebenen Domäne mit gegebenem Benutzernamen und Passwort auf.
    Dies geschieht erst einmal mit:

    VB.NET-Quellcode

    1. Dim strDomain = "LDAP://" textbox_domain.Text & "/dc=" & Replace(textbox_domain.Text, ".", ",dc=")
    2. Dim entry As New DirectoryEntry(strDomain, textbox_user.Text, textbox_pass.Text)


    Anschließend deklariere ich einen Sucher und suche nach dem angegebenen Hostnamen:

    VB.NET-Quellcode

    1. Dim search As New DirectoryServices.DirectorySearcher(entry)
    2. Dim result As DirectoryServices.SearchResult
    3. Dim entries As New ArrayList
    4. search.Filter = ("cn=" & textbox_hostname.Text)
    5. For Each result In search.FindAll()
    6. entries.Add(result.GetDirectoryEntry.Path)
    7. Next


    Gehen wir nun davon aus, dass nur ein Eintrag gefunden wurde, liegt uns nun z.B. folgender Pfad vor:

    VB.NET-Quellcode

    1. entries.Item(0) = "CN=ABCD1234,OU=Notebooks,OU=TAComputers,DC=example,DC=com"


    So weit so gut, hier kann ich sogar Werte auslesen ( entry.InvokeGet("Wertname") ).
    Unter dem obigen Pfad zu CN=ABCD1234 gibt es jedoch nochmals CNs. Variierend von Hostname zu Hostname sind das zwischen 0 und bis zu 20 Unter-CNs, die dort zu finden sind.

    Und genau hier komme ich nicht wirklich weiter... Wie komme ich an die variabel genannten Sub-CN (Sub-CN z.B. "CN=2014-06-16T13:03:04\+01:00" oder auch "CN=2014-09-06-05T14:39:08\+01:00") und kann daraus erneut Werte auslesen? ?(


    Wenn jemand einen Tipp hat oder mich in die richtige Richtung stupsen könnte, wäre ich sehr dankbar!
    Bilder
    • bitlocker.jpg

      19,8 kB, 210×362, 204 mal angesehen
    ich weiß nicht was ein CN ist, aber wenn man von einem CN weitere (Sub-)CNs abrufen kann, dann bildet das einen Datenbaum.
    Und mit einer rekursiven Methode kann man dann auch hinkriegen, alle Sub-CNs eines CNs in einer Liste zu sammeln.
    Wie gesagt: Ich bräuchte nur die Methode, mit der man aus einem CN die Sub-CNs ausliest.
    ErfinderDesRades, wo reden wir aneinander vorbei? Vielleicht habe ich mich nicht super verständlich ausgedrückt...
    Was ich mit

    VB.NET-Quellcode

    1. entries.Item(0) = "CN=ABCD1234,OU=Notebooks,OU=TAComputers,DC=example,DC=com"
    erhalte ist der komplette Pfadname. Darin enthalten ist dann der Objektname (CN=ABCD1234).
    Zu/in diesem Objekt (sagen wir hier: Ordner) gibt es Werte, die ich auslesen kann.
    In dem "ABCD1234-Ordner" gibt es "Unterordner" (Sub-CNs), die einen variablen Namen besitzen. Sprich: es gibt einen weiteren "CN=..."-Vorsatz an den obigen Pfadnamen (z. B. "CN=2014-09-06-05T14:39:08\+01:00,CN=ABCD1234,OU=...")
    Und zu diesem "Ordner" würde ich gerne vollautomatisiert wechseln und einen bestimmten Wert, der in jedem "Unterordner" vorhanden ist, auslesen.
    ah - ich komme glaub langsam drauf.
    Der fehlende Baustein ist der Datentyp von was in dein Snippet aus post#1 als entry bezeichnet ist - wenn du mir dessen Datentyp verrätst, kann ich glaub eine Methode angeben, die aus einem entry viele entries generiert.
    Ich brauche den Datentyp, denn die Methode wird letztlich diese Signatur haben:

    VB.NET-Quellcode

    1. private function(parents As IEnumerable(of EntryDatentype))As IEnumerable(of EntryDatentype)
    und für EntryDatentype muss ich was sinnvolles einsetzen können.
    Danke!
    Also mehr als folgendes wird mir bei Visual Studio Professional 2012 nicht dazu angezeigt:
    entry: Dim entry As System.DirectoryServices.DirectoryEntry
    DirectoryEntry: Die System.DirectoryServices.DirectoryEntry-Klasse kapselt einen Knoten oder ein Objekt in der Active Directory-Domänendienste-Hierarchie.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „jnL“ ()

    jo, ist genau wassich brauche. Ich werkel nu mal - dauert bischen .
    Noch paar wichtige Tipps :
    Ohne Kenntnis der Datentypen kann man garnet proggen. Aber Strict Off verbirgt die Datentypen gewissermassen - daher:
    Option Strict On!

    Und das ist auch jedem nützlich, ders noch nicht kennt:
    VisualStudio richtig nutzen (Google ist nicht deine Mami)


    edit:
    vlt sowas:

    VB.NET-Quellcode

    1. Private Function AllEntries(ByVal root As DirectoryEntry) As List(Of DirectoryEntry)
    2. Dim lst = New List(Of DirectoryEntry)
    3. Dim recurse As Action(Of DirectoryEntry) = _
    4. Sub(parent)
    5. lst.Add(parent)
    6. For Each de As DirectoryEntry In parent.Children
    7. recurse(de)
    8. Next
    9. End Sub
    10. recurse(root)
    11. Return lst
    12. End Function

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Nach erfolgreichem Login, fügt dein Code den Parent-Pfad an "lst" an, nur bei der ForEach-Schleife ist Schluss. Er kommt dort hin und beendet sie direkt (ohne Fehlermeldung).
    Ich vermute, dass er dort abbricht, weil er meint, er finde keine Children. Doch da kann ich mit z. B. dem Windows-Tool ldp.exe das gesuchte Objekt aufrufen und sehe dort die Children...
    Ich habe gerade noch folgende Möglichkeit ausprobiert:

    Nach dem Deklarieren von entry mit Login und dem Sucher kommt Folgendes:

    VB.NET-Quellcode

    1. Dim oResults As SearchResultCollection
    2. Dim oResult As SearchResult
    3. Dim subCNs As New List(Of String)
    4. search.PropertiesToLoad.Add("name")
    5. oResults = search.FindAll
    6. For Each oResult In oResults
    7. subCNs.Add(oResult.Path.ToString)
    8. Next


    Heraus bekomme ich nun wirklich alle Children (kompletten Pfad), die das Objekt hat. Der erste Schritt zum Ziel...

    Da ich jedoch einen bestimmten Wert von jedem Children auslesen möchte, probiere ich es mit einer direkten Verbindung zum Children-Pfad.
    Hier kommt jedoch die Meldung: "Das angegebene Verzeichnisdienstattribut bzw. der angegebene Verzeichnisdienstwert ist nicht vorhanden."
    wie ich sehe, proggst du immer noch strict off.
    so kannst du keinen Vorschlag machen, wie man von einem DirectoryEntry auf viele weitere schließen kann.
    (ich bin da ziemlich stur, dass man meine Fragen beantwortet, weil du kannst mir sonstwas erzählen, was du probiert hast - wenn es kein Vorschlag ist, wie man von einem DirectoryEntry auf viele weitere schließen kann, führt es nur von Höcksken auf Stöcksken.)

    aber vlt. bringt dein Code uns ja weiter, nur mach bitte eine richtige Methode davon, der argumente übergeben werden - und man dann sieht, was damit gemacht wird.
    So weiß ich nicht, was search für einen Datentyp hat, und ich sehe auch nirgends ein DirectoryEntry, und wir wollten doch mit einem DirectoryEntry was machen.

    also bei mir gehts auch so durchn Compiler:

    VB.NET-Quellcode

    1. Private Function AllEntries(ByVal root As DirectoryEntry) As List(Of DirectoryEntry)
    2. Dim lst = New List(Of DirectoryEntry)
    3. Dim recurse As Action(Of DirectoryEntry) = _
    4. Sub(parent)
    5. lst.Add(parent)
    6. Dim search As New DirectoryServices.DirectorySearcher(parent)
    7. search.PropertiesToLoad.Add("name")
    8. For Each oResult As SearchResult In search.FindAll
    9. lst.Add(oResult.GetDirectoryEntry)
    10. Next
    11. ''vorherige Version
    12. 'For Each de As DirectoryEntry In parent.Children
    13. ' recurse(de)
    14. 'Next
    15. End Sub
    16. recurse(root)
    17. Return lst
    18. End Function
    Aber da ich nur Einzelplatzsystem hab kannich nix wirklich testen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    immer noch strict off

    Ich habe Option Strict gerade on gestellt und bin dabei, den Code zu überarbeiten.

    Könntest du mir eventuell sagen, wie ich den Sucher deklarieren muss, damit mir Option Strict on keinen Strich durch die Rechnung macht?
    Bisher sieht es so aus:

    VB.NET-Quellcode

    1. Dim entry As New DirectoryEntry(strPath, tb_user.Text, tb_pass.Text)
    2. Dim search As DirectorySearcher(entry)

    Hier gibt es bei "(entry)" den Fehler "Array bounds cannot appear in type specifiers."

    Und auch weiterhin kann ich z. B. "search.PropertiesToLoad.Add("name")" nicht ausführen, da "search" nicht deklariert wurde.

    Sobald ich das umgeschrieben habe (Option Strict on), poste ich mal den ganzen Code.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „jnL“ ()

    guck dir die Klasse DirectorySearcher im ObjectBrowser an.
    Sieh nach, welcher Datentyp da erwartet wird, in Sub New. - (es gibt mehrere Sub New).

    Vergleiche das mit dem Datentyp von login - den weiß ich ja nicht.

    bist du ühaupt sicher, dass du login dort übergeben willst? Weil entry sieht mir dort viel naheliegender aus.
    Hallo jnL,

    so wie ich das sehe fehlt dir eigentlich nur noch ein entscheidener Schritt für das Ergebnis...

    Du hast ja die CNs (Objekte) schon ausfindig gemacht... Im Grunde kannst oder besser gesagt, solltest
    du nun das Objekt direkt ansprechen. Ich mache dafür immer gerne ne Funktion, wenn ich ein bestimmtes
    Attribut haben möchte.

    Wichtig ist, dass die Variabel "uname" der komplette Pfad des Objektes ist - > bsp.
    "CN=Test,OU=Testordner,OU=Users,DC=example,DC=com"

    Ich sammel meist alles in eine Liste oder ein Array und übergebe es dann der gewünschten Attributsfunktion...
    Auch solltest du darauf achten, dass du das Objekt auf Nativ setzt... Der Benutzer (mit Berechtigungen - sollte eigentlich auch mit einem normalen Account gehen, da keine Schreibrechte erforderlich sind...) und das Passwort kannst du auch direkt angeben (statt GlobalVars.UN, GlobalVars.PW)...

    VB.NET-Quellcode

    1. Shared Function fname_f(ByRef uname As String) As String
    2. Dim userN As DirectoryEntry = New DirectoryEntry(uname, CStr(GlobalVars.UN), CStr(GlobalVars.PW), AuthenticationTypes.Secure)
    3. Dim ADSuser As ActiveDs.IADsUser
    4. ADSuser = CType(userN.NativeObject(), ActiveDs.IADsUser)
    5. Try
    6. Dim Fname As String = ADSuser.FirstName.ToString()
    7. Return Fname
    8. Catch ex As Exception
    9. Dim Fname = ""
    10. Return Fname
    11. End Try
    12. End Function


    Bevor jemand meckert - die Catch Anweisung wird gewollt mit einer leeren Variabel belegt, da es unter anderem
    User ohne Vornamen gibt (Service-Accounts, technische Mailboxen etc....)

    Liebe Grüße,
    DomDom
    Ich hatte im letzten Beitrag einen kleinen Fehler. Login sollte entry lauten. Entschuldige...
    Danke erst mal für deine Hilfe, Zeit und Bemühungen.

    Da durch Option Strict on viele Fehlermeldungen aufgetaucht sind, werde ich eine Weile brauchen, bis ich die überarbeitet habe. Mir ist das alles mit Strict on auch neu :whistling:
    Morgen werde ich dann hoffentlich den überarbeiteten Code online stellen ;)

    /edit:
    @DomDom
    Vielen Dank für deinen Lösungsvorschlag, ich werde ihn gleich testen und berichten!
    btw: Wofür steht "ActiveDs.IADsUser" in "Dim ADSuser As ActiveDs.IADsUser"?
    Du kannst auch über die Dateien, die du dir später vorknöpfen willst, einfach erstmal "Option Strict Off" drüber schreiben.
    evtl. mit ToDo - Kommentar:

    VB.NET-Quellcode

    1. Option Strict Off
    2. 'Todo: umstellen
    Der Todo-Kommentar wird dann im VS in der Aufgabenliste geführt - da kannste diese dirty-Stellen dann gezielt von aufsuchen

    Auch zum Konvertieren paar Beispiele abfragen.
    Weil Intellisense schlägt da immer einen Unfug vor, den man in 95% der Fälle besser machen kann.