📖 Grundlagen: Wie verbindet man SQL Server mit Active Directory?
ÜBERBLICK
SQL Server kann über den OLE DB-Anbieter ADSDSOObject (auch als "ADSI" bekannt) auf Active Directory zugreifen. Dazu wird ein Linked Server eingerichtet, der die LDAP-Abfragen über OPENQUERY oder direkt mit SELECT ... FROM LinkedServer... ausführt. So lassen sich Benutzer, Gruppen, Kontakte und Organisationsstrukturen direkt in T‑SQL abfragen – ideal für Berechtigungssynchronisation, Monitoring oder Berichte.
-- Voraussetzung: Der SQL Server-Dienst muss über ausreichende Rechte im AD verfügen.-- Typische LDAP-Basis: LDAP://DC=contoso,DC=com-- Filter (z.B. (objectClass=user)) holt alle Benutzerobjekte.
💡 Hinweis: Der Zugriff erfolgt nur lesend. Für Schreiboperationen (z. B. Benutzer anlegen) sind andere Techniken (PowerShell, .NET) besser geeignet. Die Abfragen nutzen die ADSI-Provider-Syntax.
🔗 1. Linked Server für ADSI erstellen
KONFIGURATION
Bevor Sie Active Directory abfragen können, muss ein Linked Server vom Typ "OLE DB Provider for Microsoft Directory Services" eingerichtet werden. Dies geschieht einmalig durch einen Systemadministrator.
-- Linked Server "ADSI" erstellen (Provider: ADSDSOObject)EXEC sp_addlinkedserver
@server = 'ADSI',
@srvproduct = 'Active Directory Services',
@provider = 'ADSDSOObject',
@datasrc = 'LDAP://DC=contoso,DC=com';
-- Optional: Mapping für die aktuelle Anmeldung (für integrierte Sicherheit)EXEC sp_addlinkedsrvlogin @rmtsrvname = 'ADSI', @useself = 'true';
-- Abfrage der Domänenbenutzer über den Linked ServerSELECT *
FROM OPENQUERY(ADSI,
'SELECT Name, sAMAccountName, mail, distinguishedName, title, department
FROM ''LDAP://DC=contoso,DC=com''
WHERE objectClass = ''user'' AND objectCategory = ''person''');
⚙️ Erklärung: Mit sp_addlinkedserver wird ein virtueller Server "ADSI" erstellt. Die LDAP-Pfadangabe muss an Ihre Domäne angepasst werden. Nach erfolgreicher Konfiguration können Sie über OPENQUERY LDAP-Suchanfragen absetzen.
⚡ 2. Active Directory mit OPENQUERY abfragen
STANDARD
Die sicherste und flexibelste Methode ist OPENQUERY. Die LDAP-Anweisung wird als String übergeben – Achten Sie auf einfache Anführungszeichen (doppelte Escaping).
-- Basisabfrage: Alle Benutzer mit Vor- und Nachname, E-Mail, LoginSELECT
sAMAccountName AS Loginname,
Name AS Vollname,
mail AS Email,
telephoneNumber AS Telefon,
department AS Abteilung
FROM OPENQUERY(ADSI,
'SELECT sAMAccountName, Name, mail, telephoneNumber, department
FROM ''LDAP://DC=contoso,DC=com''
WHERE objectClass = ''user'' AND objectCategory = ''person''
AND sAMAccountName IS NOT NULL')
ORDER BY Name;
-- Ausgabe als Tabelle – kann direkt mit lokalen Tabellen verknüpft werden
📌 Erklärung: Innerhalb der OPENQUERY-Klammer steht eine LDAP-SELECT-Anweisung. Der FROM-Teil enthält den (doppelt gequoteten) Distinguished Name des Root-Suchpfads. Die Attributnamen entsprechen den LDAP-Attributen (z. B. sAMAccountName, mail). Sie können die Abfrage wie ein normales SQL-Select behandeln.
👥 3. Details zu Benutzern – Filtern und erweiterte Attribute
PRAXIS
Mit spezifischen LDAP-Filtern lassen sich nur aktive Benutzer, bestimmte Abteilungen oder Mitarbeiter mit Telefonnummer extrahieren.
-- Nur Benutzer, die nicht deaktiviert sind (userAccountControl: 512 = aktiv)SELECT sAMAccountName, Name, mail, userAccountControl
FROM OPENQUERY(ADSI,
'SELECT sAMAccountName, Name, mail, userAccountControl
FROM ''LDAP://DC=contoso,DC=com''
WHERE objectClass = ''user''
AND (userAccountControl = 512 OR userAccountControl = 514)
AND sAMAccountName = ''mustermann''');
-- Alle Benutzer einer bestimmten Organisationseinheit (OU) – Path direkt angebenSELECT Name, sAMAccountName, mail
FROM OPENQUERY(ADSI,
'SELECT Name, sAMAccountName, mail
FROM ''LDAP://OU=IT,DC=contoso,DC=com''
WHERE objectClass = ''user''');
🔍 Wichtige Attribute:userAccountControl gibt den Kontostatus an (2 = deaktiviert, 512 = aktiv, 514 = deaktiviert + Passwort nie abläuft). distinguishedName liefert den kompletten AD-Pfad, manager gibt den Vorgesetzten (DN) zurück.
📁 4. Gruppen & Mitglieder
ERWEITERT
Mit ADSI können Sie auch Gruppenmitgliedschaften ermitteln. Dazu fragt man die Gruppe ab und nutzt das Attribut member (Array von Distinguished Names). Eine vollständige rekursive Auflösung ist auf T‑SQL‑Ebene jedoch schwierig; oft holt man zuerst die DN aller Mitglieder und führt dann eine zweite Abfrage durch.
-- Alle direkten Mitglieder einer Gruppe (z.B. "Domänen-Admins")SELECT member AS MemberDN
FROM OPENQUERY(ADSI,
'SELECT member
FROM ''LDAP://CN=Domain Admins,CN=Users,DC=contoso,DC=com''
WHERE objectClass = ''group''');
-- Mitglieder auflösen: Mit einer zweiten OPENQUERY die Benutzerdaten laden-- (Dazu werden die ausgegebenen DNs in eine IN-Klausel überführt – praktisch über eine Tabelle oder dynamisches SQL)-- Beispiel für einen festen Benutzer: Mitgliedschaft prüfenSELECT *
FROM OPENQUERY(ADSI,
'SELECT sAMAccountName, memberOf
FROM ''LDAP://CN=maxmustermann,CN=Users,DC=contoso,DC=com''
WHERE objectClass = ''user''');
🌟 Tipp: Die Spalte memberOf eines Benutzerobjekts liefert alle Gruppen (als DN-Liste). Sie kann direkt ausgelesen werden. Mit CTEs und String-Funktionen können Sie die DNs parsen, aber das ist aufwendig. Für tiefgehende Auswertungen empfehlen sich PowerShell (Get-ADGroupMember) oder spezialisierte Tools.
⚙️ 5. Performance, Limits & bewährte Methoden
BEST PRACTICES
ADSI-Abfragen über SQL Server sind nicht für sehr große Domänen (10.000+ Objekte) optimiert. Ein ungefiltertes SELECT * kann Timeouts oder langsame Ausführungen verursachen. Daher immer WHERE-Filter verwenden und nur benötigte Attribute abfragen.
-- Paging und Filter verbessern Performance-- Tokenbasierte Suche (z.B. sAMAccountName wie 'adm%')SELECT sAMAccountName, Name
FROM OPENQUERY(ADSI,
'SELECT sAMAccountName, Name
FROM ''LDAP://DC=contoso,DC=com''
WHERE objectClass = ''user''
AND sAMAccountName = ''admin*''');
-- Query-Timeout für Linked Server anpassen (falls nötig)EXEC sp_serveroption 'ADSI', 'query timeout', 300;
-- Sicherheit: Vermeiden Sie xp_cmdshell mit Directory Services – nutzen Sie den Linked Server.-- Benötigte Berechtigung: Das SQL Server-Servicekonto muss Leseberechtigung in der Domäne haben.
⚡ Fazit für die Praxis: Nutzen Sie den Linked Server sparsam. Legen Sie lokale Zwischentabellen an, wenn Sie die AD-Daten wiederholt benötigen (z. B. täglicher Import). Verwenden Sie OPENQUERY mit festen Filtern und vermeiden Sie verteilte Transaktionen (SET XACT_ABORT ON;). Prüfen Sie die Ausführung mit SET FMTONLY OFF, um tatsächliche Metadaten zu erhalten.
🧪 Alternative: Active Directory mit PowerShell (xp_cmdshell)
FÜR FORTGESCHRITTENE
Falls der ADSDSOObject-Provider nicht funktioniert oder Sie komplexe AD-Abfragen (Rekursion, erweiterte Attribute) brauchen, können Sie ein PowerShell-Skript über xp_cmdshell aufrufen und die Ergebnisse in eine temporäre Tabelle schreiben – allerdings ist dies mit zusätzlichen Sicherheitsrisiken verbunden.
-- Aktivieren von xp_cmdshell nur temporär (erfordert Sysadmin-Rechte)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
-- PowerShell-Befehl, der AD-User als CSV exportiert und in Tabelle lädtCREATE TABLE #ADUsers (SamName VARCHAR(100), FullName NVARCHAR(200), Email VARCHAR(200));
INSERT INTO #ADUsers
EXEC xp_cmdshell 'powershell -Command "Get-ADUser -Filter * -Properties Name,SamAccountName,mail | Select SamAccountName,Name,mail | ConvertTo-Csv -NoTypeInformation"';
-- Nach Gebrauch xp_cmdshell wieder deaktivieren
EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;
-- Vorsicht: Diese Methode ist weniger performant und stellt ein Sicherheitsrisiko dar.
🔐 Sicherheitshinweis:xp_cmdshell sollte nur in kontrollierten Umgebungen und vorübergehend aktiviert werden. Die PowerShell-Variante bietet zwar mehr Flexibilität, ist aber meist langsamer als ein gut konfigurierter ADSI Linked Server.
📌 Wichtig: Die genauen Attributnamen können von Ihrer AD-Struktur abweichen. Testen Sie die Abfragen zuerst mit einem Tool wie Softerra LDAP Browser oder dem ADSI-Editor. Die LDAP-Pfade müssen immer die vollständige Domäne enthalten (z. B. LDAP://DC=meinefirma,DC=local).
Die hier bereitgestellten Skripte und Codebeispiele wurden mit größtmöglicher Sorgfalt geprüft und getestet. Dennoch übernehmen wir keinerlei Gewähr für die Richtigkeit, Vollständigkeit oder Fehlerfreiheit der Inhalte. Es kann nicht ausgeschlossen werden, dass die Skripte unbeabsichtigte Fehler enthalten oder unter bestimmten Umgebungsbedingungen nicht wie erwartet funktionieren.
Der Einsatz der Skripte erfolgt ausschließlich auf eigene Gefahr. Vor der Ausführung in einer Produktivumgebung wird dringend empfohlen, die Skripte in einer geeigneten Testumgebung gründlich zu validieren und an die individuellen Gegebenheiten anzupassen. Der Autor haftet nicht für direkte oder indirekte Schäden, die durch die Verwendung oder Nichtverwendbarkeit der bereitgestellten Informationen entstehen.