Anuncios

Bienvenidos sean a este post, hoy veremos una accion que parece simple pero tiene su complejidad y es util para mantener un control de los usuarios de un dominio, comencemos con el siguiente codigo:

listuser.vbs

Const titulo = "Listas de usuario v. 1.0"

Dim oRootLDAP, Dominio
Dim nombre, departamento, pais, descripcion, empresa, texto
Dim objFSOut, objStreamout, fileout

ahora = now
ano = datepart("yyyy",ahora)
mes = datepart("m",ahora)
dia = datepart("d",ahora)
hora = datepart("h",ahora)
minuto = datepart("n",ahora)
segundo = datepart("s",ahora)
ahora = ano & mes & dia & hora & minuto & segundo

fileout = "Listado -" & ahora & ".log"

Set objFSOut = CreateObject("Scripting.FileSystemObject")
Set objStreamout = objFSOut.CreateTextFile(fileout, 1, false)

Set oRootLDAP = GetObject("LDAP://rootDSE")
Dominio = oRootLDAP.get("defaultNamingContext")
Set oContenedor = GetObject("LDAP://" & Dominio)

objStreamout.WriteLine("Inicio: " & date & " " & time)
objStreamout.WriteLine("cuenta;nombre completo;departamento;pais;descripcion;empresa")

listUsers(oContenedor)

objStreamout.write "Final: " & date() & " " & time()
objStreamout.close()
Set objStreamout = nothing
Set objFSOut = nothing

msgbox "Listo el pollo" & vbCrLf & "Pelada la gallina"

sub listUsers(oObjeto)
dim oUser

for each oUser in oObjeto
	select case lcase(oUser.class)
		case "user"
			cuenta=oUser.get("sAMAccountname")
			nombre=ObtenInfo(cuenta,"DisplayName",Dominio)
			departamento=ObtenInfo(cuenta,"department",Dominio)
			pais=ObtenInfo(cuenta,"c",Dominio)
			descripcion=oUser.get("description")
			empresa=ObtenInfo(cuenta,"company",Dominio)
			texto = cuenta & ";" & nombre & ";" & departamento & ";" & _
				pais & ";" & descripcion & ";" & empresa
			objStreamout.writeline texto
		case "organizationalunit", "container"
			listUSers(GetObject("LDAP://" &oUser.get("distinguishedName")))
	end select
next
end sub

function ObtenInfo(oUser,oCampo, cDominio)
	set cn = createobject("ADODB.Connection")
	set cmd = createobject("ADODB.Command")
	set rs = createobject("ADODB.Recordset")
	cn.open "Provider=ADsDSOObject;"

	cmd.activeconnection=cn
	cmd.commandtext="SELECT " & oCampo & " FROM 'LDAP://" & cDominio & _
	   "' WHERE sAMAccountName = '" & oUser & "'"
	
	set rs = cmd.execute
	if err<>0 then
		 FindUser="Error conectandose a la base del AD:" & err.description
	else
		if (not rs.BOF and not rs.EOF) AND (rs(0)<>"") then
			ObtenInfo=rs(0)
		else
			ObtenInfo="N/A"
		end if
	end if
	cn.close
end function
Anuncios

En este caso tenemos un script donde primero declararemos nuestras variables globales, las primeras dos para el dominio (oRootLDAP y Dominio), las otras seran para almacenar la informacion que obtenemos del AD (nombre, departamento, pais, descripcion, empresa, texto) y las ultimas tres son para manipular el archivo que recibira la informacion, nuestro siguiente paso al igual que las altas masivas generaremos una variable llamada ahora que tendra todos los datos de la hora y fecha que ejecutamos el script para generar el nombre del archivo que almacenaremos en la variable fileout, nuestro siguiente bloque sera este:

Set objFSOut = CreateObject("Scripting.FileSystemObject")
Set objStreamout = objFSOut.CreateTextFile(fileout, 1, false)
Anuncios

El cual se encargara de crear el objeto para que podamos crear un archivo de texto en el disco, con esto no solamente ya esta creado sino que esta apto para recibir informacion, nuestro siguiente bloque sera este:

Set oRootLDAP = GetObject("LDAP://rootDSE")
Dominio = oRootLDAP.get("defaultNamingContext")
Set oContenedor = GetObject("LDAP://" & Dominio)
Anuncios

Este se encargara de conectar al LDAP del Active Directory, la primer linea obtendra la raiz del mismo, nuestro siguiente linea almacenara el nombre de contexto predeterminado (equivale a DC=laboratorio, DC=local) y el ultimo sera el encargado de asignarse la coleccion, es decir el elemento que posee todos los objetos del AD, con esto ya tenemos nuestra conexion establecida y obtuvimos todos los objetos principales, nuestro siguiente bloque:

objStreamout.WriteLine("Inicio: " & date & " " & time)
objStreamout.WriteLine("cuenta;nombre completo;departamento;pais;descripcion;empresa")
Anuncios

Solo se encargara de escribir las dos primeras lineas del archivo de texto, la primera sera la hora y fecha de inicio y la segunda sera la descripcion de los campos que obtendremos, veamos el siguiente bloque:

listUsers(oContenedor)

objStreamout.write "Final: " & date() & " " & time()
objStreamout.close()
Set objStreamout = nothing
Set objFSOut = nothing

msgbox "Listo el pollo" & vbCrLf & "Pelada la gallina"
Anuncios

Esta se encargara de llamar a un sub llamado listUsers y le pasaremos la coleccion que creamos anteriormente (oContenedor) una vez que finalice de ejecutarse continuara escribiendo en el archivo a que hora y fecha finalizo, despues cerraremos el objeto encargado de manejar al archivo y por ultimo setearemos a “nada” los objetos del archivo, por ultimo mostraremos un mensaje de aviso que termino, veamos la sub listUsers:

sub listUsers(oObjeto)
dim oUser

for each oUser in oObjeto
	select case lcase(oUser.class)
		case "user"
			cuenta=oUser.get("sAMAccountname")
			nombre=ObtenInfo(cuenta,"DisplayName",Dominio)
			departamento=ObtenInfo(cuenta,"department",Dominio)
			pais=ObtenInfo(cuenta,"c",Dominio)
			descripcion=oUser.get("description")
			empresa=ObtenInfo(cuenta,"company",Dominio)
			texto = cuenta & ";" & nombre & ";" & departamento & ";" & _
				pais & ";" & descripcion & ";" & empresa
			objStreamout.writeline texto
		case "organizationalunit", "container"
			listUSers(GetObject("LDAP://" &oUser.get("distinguishedName")))
	end select
next
end sub
Anuncios

En este caso pasara por cada uno de los objetos que contiene la coleccion pasada como argumento, y dentro de este bucle usaremos un select case que se encargara de verificar que valor tiene oUser.class, para evitar inconvenientes usamos un lcase para reducir a minusculas el valor, primero analizaremos el segundo case proque en caso de ser organizationalunit o container se volvera a llamar pero pasara como coleccion por medio de GetObject y el valor obtenido de distinguishedName, esta propiedad corresponde a la ubicacion completa del objeto en si, y verificara si posee otros contenedores u objeto de tipo user, este case es el que nos permitira verificar de manera recurrente dentro de cada uno de los contenedores u OU que generamos, pasemos al primer case, en este caso sera para el valor user, y sera el encargado de obtener la informacion por medio de la funcion ObtenInfo, la almacena en una variable llamada de acuerdo al campo que averiguamos y luego por ultimo la almacenamos en una variable llamada texto y las concatenaremos todas con el ampersand (&) y el punto y coma (;) para finalmente escribir en el archivo los datos obtenidos, hablemos sobre la funcion ObtenInfo:

function ObtenInfo(oUser, oCampo, cDominio)
	set cn = createobject("ADODB.Connection")
	set cmd = createobject("ADODB.Command")
	set rs = createobject("ADODB.Recordset")
	cn.open "Provider=ADsDSOObject;"

	cmd.activeconnection=cn
	cmd.commandtext="SELECT " & oCampo & " FROM 'LDAP://" & cDominio & _
	   "' WHERE sAMAccountName = '" & oUser & "'"
	
	set rs = cmd.execute
	if err<>0 then
		 FindUser="Error conectandose a la base del AD:" & err.description
	else
		if (not rs.BOF and not rs.EOF) AND (rs(0)<>"") then
			ObtenInfo=rs(0)
		else
			ObtenInfo="N/A"
		end if
	end if
	cn.close
end function
Anuncios

En este caso trabaja de la misma forma que FindUser pero recibira tres datos, el primero sera la cuenta del usuario, luego el campo que queremos obtener y por ultimo el dominio, luego la mayor diferencia sera a la hora de hacer el query donde en lugar de usar un campo sera usado el que informemos (oCampo) y luego en lugar de buscar en GC lo hara en el LDAP, y el condicional WHERE sera regido por la cuenta, despues verifica si se conecto correctamente en caso contrario nos devuelve un error pero si lo hizo procedera a verificar que el resultado no esta ni al principio ni al final y tampoco es distinto de nada (es decir que no volvio en blanco), si esto se cumple lo devuelve como resultado de la funcion de lo contrario nos devuelve un “N/A” indicando que no tiene dicha informacion, si bien esto podriamos haberlo trabajado directamente con un get y el objeto oUser, este nos puede devolver un error cuando el resultado sea en blanco o null, entonces para evitarlo vi mas correcto usar una funcion pero que a su vez no tenga los campos fijos sino que se adapte a nuestras necesidades porque si bien a mi con esta informacion me alcanza ustedes pueden necesitar mas datos y por ende agregarlos como vimos hasta ahora, pasemos a verlo en accion mediante el siguiente video

Como pueden ver la ejecucion fue rapidisima pero esta velocidad puede variar dependiendo de la cantidad de usuarios en un dominio, recuerden que la informacion obtenida tambien puede ser variada dando una gran flexibilidad a nuestro query, lo que habria dado por este script hace unos años atras…

Anuncios
Nota: Unas posible falla que podemos obtener con este script es a la hora de recuperar informacion de tipo Array, por ejemplo memberOf, porque puede devolvernos un error de Tipo no coinciden pero para el resto funciona perfectamente, en un futuro no muy lejano subire alguna modificacion para este error.
Anuncios

En resumen, hoy hemos visto un script que se encarga de buscar todos los usuarios de nuestro active directory, obtiene la informacion que necesitemos y la descarga a un archivo de texto que esta listo para ser enviado al solicitante, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes comprar mi libro sobre VBscript en Amazon

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00