[Sysadmins MV] Consulta en LDAP AD

bZZ

Buenas tardes queridos sysadmins de MV:

Os planteo la siguiente situación. Imaginemos un AD de una empresa de talleres que está organizado de la siguiente manera:

Madrid
..................Alcobendas
.....................................GR Jefes Alcobendas
....................................GR Curritos Alcobendas
....................................GR Administrativos Alcobendas
..................Alcorcón
.....................................GR Jefes Alcorcón
.....................................GR Curritos Alcorcón
.....................................GR Administrativos Alcorcón

Valencia
..................Alicante
.....................................GR Jefes Alicante
.....................................GR Curritos Alicante
.....................................GR Administrativos Alicante
..................Castellón
.....................................GR Jefes Alicante
.....................................GR Curritos Alicante
.....................................GR Administrativos Alicante

etc.

Ahora pongamos que aparte, tenemos un grupo de distribución llamado GR-Software-Users que le instala al usuario un paquete de software.
En ese grupo vamos añadiendo a los usuarios que piden el software.

Ahora necesitamos una lista de todos los usuarios que tienen instalado el software PERO sólo quiero que me salgan los curritos y los administrativos, por ejemplo.
¿Cómo hago la consulta? La cuestión es que tengo 100 grupos para los curritos (GR Curritos Alcobendas, GR Curritos Madrid, GR Curritos Vigo, etc) y no quiero poner los 100 grupos en la consulta...

Lo máximo que he conseguido es sacar una lista de todos los usuarios que están en GR-Software-Users y cada uno con los grupos a los que pertecene cada usuario... pero cómo saco la lista ya filtrada?

for /F %i in ('dsquery group domainroot -name GR-Software-Users| dsget group -members| dsget user -samid| findstr /C:"."') do @echo %i && echo %i >> c:\consulta.txt && dsquery user domainroot -samid %i | dsget user -memberof >> c:\consulta.txt

Sé que tiene que ser muy fácil, pero no tengo cojones a sacarlo... Me veo picando los usuarios uno por uno... ¿Alguna solución?

Yekale7

Yo no tengo ni idea pero quizás AikonCWD puede que te ayude, si lo lee claro...

1 1 respuesta
AikonCWD

#2 Y como sabes que podría ayudar en consultas LDAP? xD

#1 Dame unos minutos que intentaré replicar tu estructura de AD (por cierto, vaya caos de AD de objetos que tenéis xD) y te hago la consulta.

Una cosa que no me ha quedado del todo clara... entiendo que hay varios usuarios de GR Curritos * que están en el grupo de GR Software, no? Y quieres sacar un excel o listado con todos los usuarios que están en GR Software pero agrupados por su grupo de GR Curritos *, es eso?

2 respuestas
shortyStyle

#1 Te recomiendo usar powershell, con el módulo de AD puedes hacer de todo.

1 respuesta
Yekale7

#3 La verdad que lo supuse totalmente :P

bZZ

#3 No es un AD real xD Necesito saber todos los curritos que tienen instalado el software, es decir, todos los usuarios de GR Curritos* que estén también en GR-Software-Users

Ahora mismo tengo este script:

for /F %i in ('dsquery group domainroot -name GR-Software-Users| dsget group -members| dsget user -samid| findstr /C:"."') do @echo %i && echo %i >> c:\consulta.txt && dsquery user domainroot -samid %i | dsget user -memberof | findstr "Curritos" >> c:\consulta.txt

Así saco una lista de todos los usuarios que están en GR-Software-Users, y los que están en un grupo *Curritos" me sale la línea en el txt.

Es decir, me saca esto:

paquito.garcia
manolito.suarez
juanito.jimenez
"CN=GR Curritos Pontevedra,OU....
rodolfo.valentino
"CN=GR Curritos Pamplona,OU...
manolo.bombo
salvador.salido

Es decir, paquito.garcia, manolito.suarez, manolo.bombo y salvador.salido están en GR Software-Users pero NO están en ningún grupo que se llame * Curritos *

Creo que con pulir el formato de salida ya estaría bien.

1 respuesta
Red_HMR

Como dice #4 usa Powershell, con poco que busques consultas vas a sacarlo fácilmente, hay mucha documentación y más sobre AD.

AikonCWD

#6 Ya casi lo tengo... me falta solo alcarar una duda más. Usas NestedGroups en tu ActiveDirectory? Es decir, imagina que tienes un usuario miembro de 2 grupos diferentes (grupo A y grupo B) y a su vez tienes el grupo B metido dentro de otro grupo (por ejemplo C). Ese tipo de estructuras se llaman NestedGroups. Sería tu caso?

1 respuesta
bZZ

#8 Es el caso :D

1 respuesta
AikonCWD

#9 Vale, prueba el siguiente código:

Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oADO = CreateObject("ADODB.Connection")
Set oCMD = CreateObject("ADODB.Command")

oADO.Provider = "ADsDSOObject"
oADO.Open "Active Directory Provider"
Set oCMD.ActiveConnection = oADO

If oFSO.FileExists("output.log") Then oFSO.DeleteFile "output.log"

strLDAP = "LDAP://" & GetObject("LDAP://RootDSE").Get("defaultNamingContext")
oCMD.CommandText = "SELECT distinguishedName, sAMAccountName FROM '" & strLDAP & "' WHERE objectCategory = 'user'"

oCMD.Properties("Page Size") = 1000
oCMD.Properties("Timeout") = 30
oCMD.Properties("Searchscope") = 2
oCMD.Properties("Cache Results") = False

Set RS = oCMD.Execute

RS.MoveFirst
Do Until RS.EOF
	Set objUser		= GetObject("LDAP://" & RS.Fields("distinguishedName"))
	Set oGroups	= objUser.Groups
	writeLog objUser.sAMAccountName
	For Each Group in oGroups
		writeLog " > " & Group.CN
		GetNested(Group)
	Next
	writeLog ""
	RS.MoveNext
Loop

Function GetNested(oGroup)
	On Error Resume Next
	For Each Nest in oGroup.GetEx("memberOf")
		Set oNest = GetObject("LDAP://" & Nest)
		writeLog " >> " & oNest.CN
		GetNested(oNest)
	Next
End Function

Function writeLog(text)
	Set F = oFSO.OpenTextFile("output.log", 8, True)
		F.WriteLine text
	F.Close
End Function

MsgBox "Proceso finalizado correctamente", vbInformation, ""

Copia el texto y lo guardas como listar_usuarios_grupos_nested.vbs, ejecutalo desde el controlador de dominio o desde cualquier equipo del dominio con una sesión de usuarios con permisos sobre el AD.

Espera a que finalice (verás un msgbox informativo) y abre el fichero output.log, verás todos tus usuarios y abajo los grupos a los que pertenece. Los grupos nested estarán indicados por una doble >>. Si puedes, pasame ese output.log en un pastebin (por MP). A partir de ahí, si la información es correcta solo tendremos que meter un par de condiciones y utilizar la función InStr() para devolver solo los grupos deseados :)

PD: PowerShell es muy potente, pero a mi personalmente no me gusta demasiado, su sintaxis es horrorosa.

1 1 respuesta
bZZ

#10 Gracias, lo pruebo y te digo!

Edit: Parece que funciona :D No te puedo pasar el output por cuestiones de confidencialidad, pero la salida es asi:

nombre.apellido
Grupo A
Grupo AA
Grupo B
Grupo C
Grupo CC

Ahora la cuestión es filtrar los usuarios que estén en los grupos que yo diga.

1 respuesta
mgkiller

Si lo que quieres es sacar todos los usuarios que pertenecen a "GR-Software-Users" y además al grupo que tu pases como parámetro, creo que lo más sencillo con vbs es que consultes los users que pertenecen al grupo "GR-Software-Users".
Luego recorres esa consulta y para cada user con GetEx("memberOf") saques los grupos a los que pertenece, recorres el array de grupos comparando con el que has pasado como parámetro y si lo tiene escribes ese user en el archivo output.
No puedo hacerlo o probarlo desde donde estoy ahora pero seguro que si Aikon lo lee te podrá ayudar =)

AikonCWD

#11 Prueba ahora:

Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oADO = CreateObject("ADODB.Connection")
Set oCMD = CreateObject("ADODB.Command")

oADO.Provider = "ADsDSOObject"
oADO.Open "Active Directory Provider"
Set oCMD.ActiveConnection = oADO

If oFSO.FileExists("output.log") Then oFSO.DeleteFile "output.log"

strLDAP = "LDAP://" & GetObject("LDAP://RootDSE").Get("defaultNamingContext")
oCMD.CommandText = "SELECT distinguishedName, sAMAccountName FROM '" & strLDAP & "' WHERE objectCategory = 'user'"

oCMD.Properties("Page Size") = 1000
oCMD.Properties("Timeout") = 30
oCMD.Properties("Searchscope") = 2
oCMD.Properties("Cache Results") = False

Set RS = oCMD.Execute

RS.MoveFirst
Do Until RS.EOF
	Set objUser	= GetObject("LDAP://" & RS.Fields("distinguishedName"))
	Set oGroups	= objUser.Groups
	skipNested	= False
	For Each Group in oGroups
		
		If (InStr(Group.CN, "GR Curritos") > 0) Or (InStr(Group.CN, "GR Administrativos") > 0) Then
			writeLog objUser.sAMAccountName
			skipNested = True
			Exit For
		End If
		If skipNested = False Then Call GetNested(Group, objUser.sAMAccountName)
	Next
	RS.MoveNext
Loop

Function GetNested(oGroup, oUserName)
	On Error Resume Next
	Err.Number = 0
	For Each Nest in oGroup.GetEx("memberOf")
		If Err.number = 0 Then
			Set oNest = GetObject("LDAP://" & Nest)
			cn = oNest.CN
			If Err.Number = 0 Then
				If (InStr(cn, "GR Curritos") > 0) Or (InStr(cn, "GR Administrativos") > 0) Then
					writeLog oUserName
					Exit For
				End If
				GetNested(oNest)
			End If
		End If
	Next
End Function

Function writeLog(text)
	Set F = oFSO.OpenTextFile("output.log", 8, True)
		F.WriteLine text
	F.Close
End Function

MsgBox "Proceso finalizado correctamente", vbInformation, ""

En las líneas 28 y 46 cambia los 2 strings por los 2 grupos que quieres filtrar. Tal y como lo he dejado es case sensitive, pero con un LCase() se podría solucionar... A partir de aquí ya deberías poder entender el código y modificarlo a tu antojo. La función al utilizar NestedGroups se ha complicado bastante, y es fea de cojones, pero VBS carece de control de errores y excepciones, así que más bonito no lo puedo dejar.

Ya me dices que tal te va!

1 1 respuesta
bZZ

#13 Funciona bien! Pero ayúdame a pulirlo :D

 If (InStr(Group.CN, "GR Curritos") > 0) Or (InStr(Group.CN, "GR Administrativos") > 0) Then
            writeLog objUser.sAMAccountName
            skipNested = True

Si quiese sólo los curritos de Zaragoza, es decir, sólo los miembros del grupo GR Curritos Zaragoza ¿cómo lo pongo? Porque así me muestra todos los que pertenecen a GR Curritos*

Luego en el output me gustaría que saliese el nombre del usuario, supongo que dejándolo así:

 If (InStr(Group.CN, "GR Curritos") > 0) Or (InStr(Group.CN, "GR Administrativos") > 0) Then
            writeLog objUser.sAMAccountName
            writeLog objUser.displayName

¿Y para que me salga el grupo también? ¿Y para concatenar el usuario, su nombre completo y su grupo con ; y luego exportalo a CSV? XD

Probablemente estoy pidiendo demasiado xD

1 respuesta
bZZ

Doble post because shit happens.

AikonCWD

#14 La condición para buscar el grupo está repetida en 2 líneas diferentes (because nestedgroups), mira en la líneas 28 y 46. Tal y como te he puesto el ejemplo, el script dumpea los usuarios que esten en uno de esos 2 grupos, si quieres solo los de zaragoza puedes poner:

If (InStr(Group.CN, "GR Curritos Zaragoza") > 0) Or (InStr(Group.CN, "GR Administrativos Zaragoza") > 0) Then
...
...
If (InStr(cn, "GR Curritos Zaragoza") > 0) Or (InStr(cn, "GR Administrativos Zaragoza") > 0) Then

La función writeLog objUser.sAMAccountName ya imprime el username. Si necesitas más datos del usuario tendrás que mirar la documentación de ese objeto:

Puedes probar con writeLog objUser.CN o writeLog objUser.displayName. Lo del grupo tiene un poco más de guasa... ya que cada usuario podría estar en 1, 2, 3, 4, ... N grupos, no? Habría que crear una función que recorra el array de los grupos y los dumpee en la misma línea del TXT/CSV. Si quieres mañana puedo intentar hacertelo. De momento prueba:

writeLog objUser.sAMAccountName & ";" & objUser.CN & ";" & objUser.displayName

Usa la función buscar/reemplazar para cambiar output.log por output.csv

PD: Al final te voy a pasar una factura por mi tiempo xD


EDIT: Tienes que ampliar la consulta LDAP, añadiendo los nuevos campos:

"SELECT distinguishedName, sAMAccountName, CN, displayName FROM '"
1 respuesta
bZZ

#16 No me he explicado bien:

If (InStr(Group.CN, "GR Curritos Zaragoza")

me devuelve los users que están en Gr Curritos Zaragoza y (ejemplo tonto) GR Curritos Zaragoza-pruebas cuando sólo me interesan Gr Curritos Zaragoza ¿Cómo lo podemos solucionar?

PD: Te has ofrecido voluntario! Factura no pero tienes todas mis gratificaciones xDDD Además asi aprendemos los 2 xDDD

1 respuesta
AikonCWD

#17 A vale, que tienes grupos con nombres más largos xD. Quita la función InStr()

If (Group.CN = "GR Curritos Zaragoza")