El Blog de Gualtrysoft

Windows 2000/2003/2008, Active Directory, VBScript, Hyper-V, PowerShell y todo aquello interesante a la hora de usar, configurar y administrar Windows Server. También tenemos longanizas…

Usuarios de Active Directory con Formato “Apellidos, Nombre”

Posted by urpiano en Lunes 30 \30\UTC marzo \30\UTC 2009

Cuando damos de alta un usuario con Usuarios y equipos de Active Directory el campo Nombre para mostrar (displayName) se rellena de forma automática siguiendo el patrón Nombre Inicial Apellidos. La inicial es típica de los anglosajones, que siempre tienen un segundo nombre propio del cuál sólo escriben la inicial; en España este campo de inicial no lo rellenamos, con lo que el patrón que realmente observamos que es utilizado para rellenar Nombre para mostrar es en realidad Nombre Apellidos. Sin embargo, en muchas organizaciones, se quiere que el patrón a seguir sea Apellidos, Nombre lo cuál hace que en muchas de estas organizaciones sea necesario construir "a mano" este Nombre para mostrar, lo que lleva a errores y olvidos de hacerlo. ¿Hay alguna forma de evitar este problema y automatizar el formato de Nombre para mostrar a Apellidos, Nombre en la creación de los usuarios? Veremos como sí es posible automatizarlo y también veremos un script que nos permita convertir los ya existentes de un plumazo.

Es perfectamente posible conseguir que el Nombre para mostrar de un usuario que estamos creando, se monte de forma automática con el formato Apellidos, Nombre. El asunto consiste en retocar un atributo de configuración de Active Directory, tal y como se explica en How to change display names of Active Directory users. Como se ve en el artículo, se utiliza ADSI Edit, por lo que es necesario que estén instaladas las Support Tools que encontramos en el CD de instalación de Windows. Una vez instaladas, abrimos una consola de administración (MMC). Para ello ejecutamos Inicio\Ejecutar, escibimos mmc y pulsamos Aceptar:

Abrir MMC

Abrir MMC

Una vez abierta la consola, debemos agregar el complemento ADSI Edit. Para ello ejecutamos el menú Archivo\Agregar o quitar complemento…:


Agregar complemento

Agregar complemento

Esto abre el cuádro de diálogo de agregar o quitar complementos, en el que pulsamos el botón Agregar…:

Agregar complemento

Agregar complemento

Esto abre el cuadro de diálogo de selección de complementos, en el que seleccionaremos ADSI Edit, pulsaremos Agregar y a continuación Cerrar:

Agregar ADSI Edit

Agregar ADSI Edit

De vuelta en el cuadro de diálogo de agregar o quitar complementos, veremos como ADSI Edit aparece en la lista. Una vez está agregado, pulsamos Aceptar:

Complemento agregado

Complemento agregado

Una vez cargado el complemento ADSI Edit debemos conectarlo al espacio de nombres de configuracion. Para ello, en el panel de árbol, hacemos clic derecho sobre el nodo ADSI Edit y clic sobre Connect to…:

Conectar a

Conectar a

Esto nos sitúa en el cuadro de diálogo de conexión de ADSI Edit. En él deberemos seleccionar el espacio de nombres de configuración. Para ello, en el marco Connection Point seleccionamos la opción Select a Well Known Naming Context, con el desplegable seleccionamos Configuration y pulsamos Ok:

Conectar a configuración

Conectar a configuración

Una vez hecho esto, en el panel del árbol, expandimos el nodo Raíz de consola\ADSI Edit\Configuration [nombredominio.loquesea]\CN=Configuration,DC=nombredominio,DC=loquesea\CN=DisplaySpecifiers. Veremos que contiene una serie de nodos cuyos CNs son números en hexadecimal. Estos números corresponden a los códigos de idiomas, y deberemos modificar en todos aquellos idiomas que sea necesario según nuestra organización. Por ejemplo, en este caso está seleccionado el Inglés (409):

user-Display English

user-Display English

Y en este otro el Español (C0A):

Spanish

Spanish

Si, por ejemplo, en nuestro dominio tenemos controladores de dominio en español y en Inglés, o sencillamente administramos desde equipos que están en cualquiera de los dos idiomas y por tanto su Usuarios y equipos de Active Directory puede estar en cualquiera de los dos idiomas cuando estamos dando de alta a un usuario, es necesario que la modificación que vamos a explicar sea realizada en ambos lenguajes; es decir en los nodos 409 (Ingés) y C0A (Español). Si nos fijamos en las dos imágenes anteriores, en el panel de detalles está seleccionado el mismo atributo, CN=user-Display, que es el atributo que debemos modificar. Hacemos doble clic sobre él, lo que abre el cuadro de diálogo de sus propiedades. En él, buscaremos la propiedad createDialog y pulsaremos el botón Edit:

createDialog

createDialog

En el cuadro de diálogo que se abre pondremos la plantilla de formato que deseamos. Esto se realiza utilizando las siguientes variables:

  • sn: Apellidos
  • givenName: Nombre
  • initials: Inicial

Las variables deben estar encerradas entre signos de mayor y menor y precedidas por tanto por ciento; es decir %<variable>. Lo demás que se escriba seran literales, por ello, para obtener Apellidos, Nombre, como deseamos, el formato que debemos poner es %<sn>, %<givenName>, y a continuación pulsaremos Ok:

createDialog modificación

createDialog modificación

Esto nos devuelve al cuadro de diálogo con las propiedades de user-Display, en el que pulsaremos Aceptar. Estos cambios, como dijimos antes, debemos realizarlos en tantos idiomas como necesitemos, según los idiomas de las herramientas administrativas que usemos en nuestro dominio. Una vez realizados estos cambios, ya podremos ver los resultados creando un usuario desde Usuarios y equipos de Active Directory. En la imagen vemos cómo después de haber escrito el nombre y el apellido, el nombre para mostrar se ha montado como deseabamos:

Creación de usuario

Creación de usuario

No solo se ha montado así el Nombre para mostrar, si no que también el propio nombre de usuario, es decir su Relative Distinguished Name (RDN), que es lo que nos aparece como nombre de usuario en Usuarios y equipos de Active Directory y es la primera parte de su nombre distinguido (es decir, el RDN de CN=Pi, Filemón,OU=Agentes,DC=tia,DC=org es CN=Pi, Filemón.

¿Todo terminado? No, por desgracia no está todo terminado, pues en la gran mayoría de los casos, este tipo de configuración del nombre para mostrar lo establecemos en dominios en los cuales ya existen usuarios y no tienen este formato ni de Nombre para mostrar ni de RDN. Si se quiere que todos los usuarios del dominio tengan este formato, es necesario cambiar los ya existentes, pues el cambio que realizamos en la configuración con ADSI Edit sólo es operativo a la hora de crear nuevos usuarios, no cambia los ya existentes. Para ello, he desarrollado el script que muestro a continuación, que cambia tanto el Nombre para mostrar como el RDN.

Este script permite cambiar el displayName de todos los usuarios de un dominio, una OU, o un grupo de seguridad del dominio, estableciendolo como Apellidos, Nombre. Se puede, así mismo, cambiar el nombre del usuario (su nombre RDN, o sea, la parte propia del objeto en el nombre distinguido), de forma que “CN=Filemón Pi,OU=Agentes,DC=tia,DC=org” pasa a ser “CN=Pi, Filemón,OU=Agentes,DC=tia,DC=org”.

Sintaxis

cscript [//nologo] cambiar-displayname.vbs [/Dominio:nombre_dominio] [/OU:nombre_OU] [/Recursivo] [/Grupo:nombre_grupo] [/Log:fichero] [/RDN] [/?]

Siendo

Etiqueta Dato ¿Requerido? Descripción
Dominio nombre_dominio No
Nombre DNS del dominio en el realizarán los cambio. Si se omite y también se omiten /OU y /Grupo, se realizarán los cambios en el dominio al que pertenece el equipo desde el que se lanza el script
OU nombre_OU No
Nombre canónico de la OU en la que están las cuentas de usuario en las que se realizarán los cambios. Si se omite y también se omiten /Dominio y /Grupo, se realizarán los cambios en el dominio al que pertenece el equipo desde el que se lanza el script. El nombre canócico es de la forma /, por ejemplo tia.org/Agentes/Patosos; en este ejemplo el nombre es de una OU de nombre Patosos contenida en la OU Agentes y que pertenecen al dominio tia.org. Hay que tener en cuenta de que el separador de objetos en este tipo de nombre es la barra de división; en el caso de que alguno de los objetos contenga una barra en su nombre, es necesario quitar el significado anteponiendo una barra de división entera (slash inverso) a la barra de división, por ejemplo tia.org/Oficinas\/Superintendencia
Recursivo subárbol No
Si se ha pasado el parámetro /OU, en caso de recibirse este modificador se cambiarán a todos los usuarios que estén dentro del subárbol cuya raíz es la OU; en caso de no recibirse, sólo se cambiarán los usuarios contenidos en la propia OU
Grupo nombre_grupo No
Nombre WinNT del grupo de seguridad al que pertenecen las cuentas de usuario a las que se realizarán los cambios. Si se omite y también se omiten /Dominio y /OU, se realizarán los cambios en el dominio al que pertenece el equipo desde el que se lanza el script
Log fichero No
Ruta y nombre de un fichero de texto en el que se volcarán los resultados de la ejecución del script
RDN nombre_relativo No
Si se pasa este parámetro, no sólo se cambiará el displayName, si no que también se cambiará el nombre del objeto de usuario, es decir, su distinguishedRelativeName, o sea, la parte del nombre distinguido correspondiente al propio objeto, no al contenedor en el que está
?   No
Muestra la ayuda en línea.

Ejemplos:

– Se cambiará el nombre para mostrar a los usuarios del dominio desde el que se lanza el script. Los resultados de la ejecución se mostrarán por pantalla:

cscript //nologo cambiar-displayname.vbs

– Se cambiarán el nombre para mostrar y el nombre de objeto a los usuarios del dominio desde el que se lanza el script. Los resultados de la ejecución se mostrarán por pantalla:

cscript //nologo cambiar-displayname.vbs /RDN

– Se cambiarán el nombre para mostrar y el nombre de objeto a los usuarios del dominio tia.org. Los resultados de la ejecución se mostrarán por pantalla. Los resultados de la ejecución se mostrarán por pantalla:

cscript //nologo cambiar-displayname.vbs /Dominio:tia.org /RDN

– Se cambiarán el nombre para mostrar y el nombre de objeto a los usuarios contenidos en la unidad organizativa tia.org/Agentes. Los resultados de la ejecución se mostrarán por pantalla:

cscript //nologo cambiar-displayname.vbs /OU:tia.org/Agentes /RDN

– Se cambiarán el nombre para mostrar y el nombre de objeto a los usuarios contenidos en la unidad organizativa tia.org/Agentes y los de todas las OU contenidas en ella. Los resultados de la ejecución se mostrarán por pantalla:

cscript //nologo cambiar-displayname.vbs /OU:tia.org/Agentes /Recursivo /RDN

– Se cambiarán el nombre para mostrar y el nombre de objeto a los usuarios del grupo TIA\Agentes. Los resultados de la ejecución se mostrarán por pantalla:

cscript //nologo cambiar-displayname.vbs /Grupo:TIA\Agentes /RDN

– Se cambiarán el nombre para mostrar y el nombre de objeto a los usuarios del dominio tia.org. Los resultados de la ejecución se guardarán en el fichero \\bacteriosrv\logs\cdn.txt:

cscript //nologo cambiar-displayname.vbs /Dominio:tia.org /Log:\\bacteriosrv\logs\cdn.txt /RDN

Este es el código del script

'*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
'*°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°*
'* cambiar-displayname.vbs                                             *
'*                                                                     *
'* Este script permite cambiar el displayName de todos los usuarios de *
'* un dominio, una OU, o un grupo de seguridad del dominio,            *
'* estableciendolo como Apellidos, Nombre. Se puede, así mismo,        *
'* cambiar el nombre del usuario (su nombre RDN, o sea, la parte       *
'* propia del objeto en el nombre distinguido).                        *
'*                                                                     *
'* Sintaxis                                                            *
'*                                                                     *
'* cscript [//nologo] cambiar-displayname.vbs                          *
'* [/Dominio:nombre_dominio] [/OU:nombre_OU [/Recursivo]]              *
'* [/Grupo:nombre_grupo] [/Log:fichero] [/RDN] [/?]                    *
'*                                                                     *
'* Siendo                                                              *
'*                                                                     *
'* - /Dominio: nombre_dominio (Opcional):                              *
'*         Nombre DNS del dominio en el realizarán los cambio. Si se   *
'*         omite y también se omiten /OU y /Grupo, se realizarán los   *
'*         cambios en el dominio al que pertenece el equipo desde el   *
'*         que se lanza el script                                      *
'*                                                                     *
'* - /OU: nombre_OU (Opcional):                                        *
'*         Nombre canónico de la OU en la que están las cuentas de     *
'*         usuario en las que se realizarán los cambios. Si se omite y *
'*         también se omiten /Dominio y /Grupo, se realizarán los      *
'*         cambios en el dominio al que pertenece el equipo desde el   *
'*         que se lanza el script. El nombre canócico es de la forma   *
'*         <FQDN del dominio>/<Nombre OU>, por ejemplo                 *
'*         tia.org/Agentes/Patosos; en este ejemplo el nombre es de    *
'*         una OU de nombre Patosos contenida en la OU Agentes y que   *
'*         pertenecen al dominio tia.org. Hay que tener en cuenta de   *
'*         que el separador de objetos en este tipo de nombre es la    *
'*         barra de división; en el caso de que alguno de los objetos  *
'*         contenga una barra en su nombre, es necesario quitar el     *
'*         significado anteponiendo una barra de división entera       *
'*         (slash inverso) a la barra de división, por ejemplo         *
'*         tia.org/Oficinas\/Superintendencia                          *
'*                                                                     *
'* - /Recursivo: subárbol (Opcional):                                  *
'*         Si se ha pasado el parámetro /OU, en caso de recibirse este *
'*         modificador se cambiarán a todos los usuarios que estén     *
'*         dentro del subárbol cuya raíz es la OU; en caso de no       *
'*         recibirse, sólo se cambiarán los usuarios contenidos en la  *
'*         propia OU                                                   *
'*                                                                     *
'* - /Grupo: nombre_grupo (Opcional):                                  *
'*         Nombre WinNT del grupo de seguridad al que pertenecen las   *
'*         cuentas de usuario a las que se realizarán los cambios. Si  *
'*         se omite y también se omiten /Dominio y /OU, se realizarán  *
'*         los cambios en el dominio al que pertenece el equipo desde  *
'*         el que se lanza el script                                   *
'*                                                                     *
'* - /Log: fichero (Opcional):                                         *
'*         Ruta y nombre de un fichero de texto en el que se volcarán  *
'*         los resultados de la ejecución del script                   *
'*                                                                     *
'* - /RDN: nombre_relativo (Opcional):                                 *
'*         Si se pasa este parámetro, no sólo se cambiará el           *
'*         displayName, si no que también se cambiará el nombre del    *
'*         objeto de usuario, es decir, su distinguishedRelativeName,  *
'*         o sea, la parte del nombre distinguido correspondiente al   *
'*         propio objeto, no al contenedor en el que está              *
'*                                                                     *
'* - /?: ayuda (Opcional):                                             *
'*         Muestra la ayuda en línea                                   *
'*                                                                     *
'*                                                                     *
'* Ejemplos:                                                           *
'*                                                                     *
'* - Se cambiará el nombre para mostrar a los usuarios del dominio     *
'* desde el que se lanza el script. Los resultados de la ejecución se  *
'* mostrarán por pantalla:                                             *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs                            *
'*                                                                     *
'* - Se cambiarán el nombre para mostrar y el nombre de objeto a los   *
'* usuarios del dominio desde el que se lanza el script. Los           *
'* resultados de la ejecución se mostrarán por pantalla:               *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs /RDN                       *
'*                                                                     *
'* - Se cambiarán el nombre para mostrar y el nombre de objeto a los   *
'* usuarios del dominio tia.org. Los resultados de la ejecución se     *
'* mostrarán por pantalla. Los resultados de la ejecución se mostrarán *
'* por pantalla:                                                       *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs /Dominio:tia.org /RDN      *
'*                                                                     *
'* - Se cambiarán el nombre para mostrar y el nombre de objeto a los   *
'* usuarios contenidos en la unidad organizativa tia.org/Agentes. Los  *
'* resultados de la ejecución se mostrarán por pantalla:               *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs /OU:tia.org/Agentes /RDN   *
'*                                                                     *
'* - Se cambiarán el nombre para mostrar y el nombre de objeto a los   *
'* usuarios contenidos en la unidad organizativa tia.org/Agentes y los *
'* de todas las OU contenidas en ella. Los resultados de la ejecución  *
'* se mostrarán por pantalla:                                          *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs /OU:tia.org/Agentes        *
'* /Recursivo /RDN                                                     *
'*                                                                     *
'* - Se cambiarán el nombre para mostrar y el nombre de objeto a los   *
'* usuarios del grupo TIA\Agentes. Los resultados de la ejecución se   *
'* mostrarán por pantalla:                                             *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs /Grupo:TIA\Agentes /RDN    *
'*                                                                     *
'* - Se cambiarán el nombre para mostrar y el nombre de objeto a los   *
'* usuarios del dominio tia.org. Los resultados de la ejecución se     *
'* guardarán en el fichero \\bacteriosrv\logs\cdn.txt:                 *
'*                                                                     *
'* cscript //nologo cambiar-displayname.vbs /Dominio:tia.org           *
'* /Log:\\bacteriosrv\logs\cdn.txt /RDN                                *
'*                                                                     *
'*                                                                     *
'*                                                                     *
'*                                                                     *
'* © Fernando Reyes                                                    *
'* Marzo De 2009                                                       *
'*°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°*
'*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*

'Exigimos la declaración de variables
Option Explicit

'Constantes para el tipo de objeto de AD
Const clas_DOMINIO = 1
Const clas_CONTENEDOR = 2
Const clas_OU = 3
Const clas_GRUPO = 4
Const clas_EQUIPO = 5
Const clas_USUARIO = 6
Const clas_CONTACTO = 7

'Declaración de variables
Dim str_Error     'As String
Dim int_Error     'As String
Dim str_Dominio   'As String
Dim str_OU        'As String
Dim str_Grupo     'As String
Dim str_Log       'As String
Dim str_Salida    'As String
Dim bol_RDN       'As Boolean
Dim obj_FS        'As Scripting.FileSystemObject
Dim obj_TS        'As Scripting.TextStream
Dim arr_Usuarios  'As String Array
Dim bol_Recursivo 'As Boolean
Dim str_Usuario   'As String

bol_RDN = False


'Validando los argumentos y almacenando
'sus valores
If f_RevisarArgumentos( _
                       str_Error, _
                       int_Error) Then

    Call s_Ayuda(str_Error)
    WScript.Quit int_Error

End If

'Preparamos los encabezados de la devolucion
str_Salida = "DpAntiguo" & vbTab & _
             "DnAntiguo" & vbTab &  _
             "DpNuevo" & vbTab & _
             "DnNuevo"
             
'Mostramos los encabezados por pantalla
WScript.Echo str_Salida

'Los subrayamos
WScript.Echo "=========" & vbTab & _
             "=========" & vbTab & _
             "=======" & vbTab & _
             "======="

'Si hay que volcar la información en un fichero de Log
If Len(str_Log) > 0 Then

    'Creamos un objeto FileSystemObject
    Set obj_FS = CreateObject("Scripting.FileSystemObject")
    
    'Creamos el fichero de Log. Si existe lo reemplazaremos.
    'El fichero estará creado en Unicode
    Set obj_TS = obj_FS.CreateTextFile(str_Log, True, True )
    
    'Ponemos los encabezados en el fichero
    obj_TS.WriteLine str_Salida
    
End If

For Each str_Usuario In arr_Usuarios

    If Not Len(Trim(str_Usuario)) = 0 Then _    
            Call s_RDN(str_Usuario, bol_RDN)

Next 'str_Usuario

'Si hay que volcar información en el fichero
'de salida de ruta y nombre str_Log
If Len(str_Log) > 0 Then

    'Cerramos el fichero de salida
    obj_TS.Close

    'Limpieza de popa :-)
    Set obj_TS = Nothing
    Set obj_FS = Nothing

End If

Function f_RevisarArgumentos( _
                             str_Error, _
                             int_Error _
                             ) 'As Boolean
'***********************************************************************
'* Procedimiento: f_RevisarArgumentos                                  *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 25/03/2009 10:25:52                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función revisa los argumentos recibidos,        *
'*                recogiendo los posibles fallos por falta de          *
'*                argumentos requeridos y almacenando en las           *
'*                variables correspondientes los argumentos            *
'*                recibidos. recibe dos parámetros cuyo fin es ser de  *
'*                salida: una cadena que almacenará los errores        *
'*                detectados y un entero que almacenará el código de   *
'*                los errores detectados. Hay tres tipos de error;     *
'*                error 1 para los argumentos sin nombre requeridos y  *
'*                no encontrados, error 2 para los argumentos con      *
'*                nombre requeridos y no encontrados, por último,      *
'*                error 4 para los combos de argumentos opcionales     *
'*                (un combo de argumentos opcionales es aquel          *
'*                conjunto de argumentos opcionales que es requerido   *
'*                que se pase al menos uno de ellos y que si se pasa   *
'*                más de uno se ignorarán aquellos que estén detrás    *
'*                en la prioridad entre ellos; una característica      *
'*                clara de lo que es un combo de argumentos es cuando  *
'*                dos omás argumentos almacenan su valor en la misma   *
'*                variable). En el caso de producirse más de un tipo   *
'*                de error, el número de error será la suma de ambos   *
'*                de los errores recibidos, es decir 3, 5 o 6          *
'***********************************************************************

    Dim bol_Devolucion 'As Boolean
    Dim bol_Error1 'As Boolean
    Dim bol_Error2 'As Boolean
    Dim bol_Error4 'As Boolean

    'Iniciamos los indicadores
    bol_Devolucion = False
    bol_Error1 = False
    bol_Error2 = False
    bol_Error4 = False


    'Si hay que mostrar la ayuda, se muestra y
    'termina el script
    If WScript.Arguments.Named.Exists("?") Then

        Call s_Ayuda("******************" & vbCrLf & _
                     "*     AYUDA      *" & vbCrLf & _
                     "******************")

        WScript.Quit 0

    End If

    'Revisamos que esté el argumento
    '/Log (fichero)
    If WScript.Arguments.Named.Exists("Log") Then

        str_Log =  _
               WScript.Arguments.Named("Log")

    End If

    'Revisamos que esté el argumento
    '/RDN (nombre_relativo)
    If WScript.Arguments.Named.Exists("RDN") Then

        bol_Rdn = True

    End If

    'Revisamos si ha sido pasado el argumento
    '/Dominio (nombre_dominio)
    If WScript.Arguments.Named.Exists("Dominio") Then

        'Obtenemos el nombre distinguido del dominio
        str_Dominio = f_DomDNSaLDAP( _
               WScript.Arguments.Named("Dominio"))
               
        bol_Recursivo = True
        
        'Obtenemos el array de usuarios
        arr_Usuarios = f_UsuariosOU(str_Dominio,True)
                
    'Revisamos si ha sido pasado el argumento
    '/OU (nombre_OU)
    Elseif WScript.Arguments.Named.Exists("OU") Then

        'Almacenamos el nombre distinguido de la OU
        str_OU = _
               f_OUCanonico2DN(WScript.Arguments.Named("OU"))
WScript.Echo str_ou
        'Revisamos que esté el argumento
        '/Recursivo (subárbol)
        bol_Recursivo = _
                WScript.Arguments.Named.Exists("Recursivo")
                
        'Obtenemos el array de usuarios
        arr_Usuarios = f_UsuariosOU(str_OU, bol_Recursivo)
                
    'Revisamos si ha sido pasado el argumento
    '/Grupo (nombre_grupo)
    Elseif WScript.Arguments.Named.Exists("Grupo") Then

        'Almacenamos el nombre distinguido del grupo
        str_Grupo = _
               f_NTaDN(WScript.Arguments.Named("Grupo"),"")
               
        'Obtenemos el array con los usuarios miembros del
        'grupo
        arr_Usuarios = f_UsuariosGrupo(str_Grupo)

    'Como no se ha pasado ni nombre de dominio, ni de OU,
    'ni de grupo, obtenemos el nombre distinguido del dominio
    'al que pertenece el equipo desde el que se lanza el
    'script
    Else

        'Obtenemos el nombre del dominio al que pertenece el
        'equipo desde el que se lanza el script
        str_Dominio = f_DNDominio
                
        bol_Recursivo = True
        
        'Obtenemos el array de usuarios
        arr_Usuarios = f_UsuariosOU(str_Dominio, bol_Recursivo)
        
    End If

    'Preparamos las variables de devolucion:
    'el entero como suma de los posibles errores 1, 2 y 4
    int_Error = Abs(bol_Error1) + _
                (2 * Abs(bol_Error2)) + _
                (4 * Abs(bol_Error4))
    'La devolucion de la función será True en caso de
    'haber alguno de los errores
    bol_Devolucion = (bol_Error1 Or bol_Error2 Or bol_Error4)

    'Hacemos la devolución de la función
    f_RevisarArgumentos = bol_Devolucion

End Function 'f_RevisarArgumentos

Sub s_Ayuda(str_Error)
'***********************************************************************
'* Procedimiento: s_Ayuda                                              *
'* Tipo         : Sub                                                  *
'* Devolución   :                                                      *
'* Fecha y Hora : 25/03/2009 10:25:52                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Este procedimiento muestra la ayuda en línea.        *
'*                Recibe un parámetro de tipo cadena que si viene      *
'*                será mostrado antes de la línea; pensado para que    *
'*                se muestre un error que se haya detectado.           *
'***********************************************************************

    'Si hay que mostrar algún texto previo a la ayuda, lo hacemos
    If Len(str_Error) > 0 Then

        WScript.Echo str_Error & vbCrLf & vbCrLf

    End If

    'A continuación, mostramos la ayuda por pantalla
    WScript.Echo "Este script permite cambiar el displayName de tod" & _
                 "os los usuarios de un dominio,"
    WScript.Echo "una OU, o un grupo de seguridad del dominio, esta" & _
                 "bleciendolo como Apellidos,"
    WScript.Echo "Nombre. Se puede, así mismo, cambiar el nombre de" & _
                 "l usuario (su nombre RDN, o"
    WScript.Echo "sea, la parte propia del objeto en el nombre dist" & _
                 "inguido)."
    WScript.Echo ""
    WScript.Echo "Sintaxis"
    WScript.Echo ""
    WScript.Echo "cscript [//nologo] cambiar-displayname.vbs [/Domi" & _
                 "nio:nombre_dominio]"
    WScript.Echo "[/OU:nombre_OU [/Recursivo]] [/Grupo:nombre_grupo" & _
                 "] [/Log:fichero] [/RDN] [/?]"
    WScript.Echo ""
    WScript.Echo "Siendo"
    WScript.Echo ""
    WScript.Echo "- /Dominio: nombre_dominio (Opcional):"
    WScript.Echo "Nombre DNS del dominio en el realizarán los cambi" & _
                 "o. Si se omite"
    WScript.Echo "y también se omiten /OU y /Grupo, se realizarán l" & _
                 "os cambios en"
    WScript.Echo "el dominio al que pertenece el equipo desde el qu" & _
                 "e se lanza el"
    WScript.Echo "script"
    WScript.Echo ""
    WScript.Echo "- /OU: nombre_OU (Opcional):"
    WScript.Echo "Nombre canónico de la OU en la que están las cuen" & _
                 "tas de usuario"
    WScript.Echo "en las que se realizarán los cambios. Si se omite" & _
                 " y también se"
    WScript.Echo "omiten /Dominio y /Grupo, se realizarán los cambi" & _
                 "os en el"
    WScript.Echo "dominio al que pertenece el equipo desde el que s" & _
                 "e lanza el"
    WScript.Echo "script. El nombre canócico es de la forma <FQDN del"
    WScript.Echo "dominio>/<Nombre OU>, por ejemplo tia.org/Agentes" & _
                 "/Patosos; en"
    WScript.Echo "este ejemplo el nombre es de una OU de nombre Pat" & _
                 "osos contenida"
    WScript.Echo "en la OU Agentes y que pertenecen al dominio tia." & _
                 "org. Hay que"
    WScript.Echo "tener en cuenta de que el separador de objetos en" & _
                 " este tipo de"
    WScript.Echo "nombre es la barra de división; en el caso de que" & _
                 " alguno de los"
    WScript.Echo "objetos contenga una barra en su nombre, es neces" & _
                 "ario quitar el"
    WScript.Echo "significado anteponiendo una barra de división en" & _
                 "tera (slash"
    WScript.Echo "inverso) a la barra de división, por ejemplo"
    WScript.Echo "tia.org/Oficinas\/Superintendencia"
    WScript.Echo ""
    WScript.Echo "- /Recursivo: subárbol (Opcional):"
    WScript.Echo "Si se ha pasado el parámetro /OU, en caso de reci" & _
                 "birse este"
    WScript.Echo "modificador se cambiarán a todos los usuarios que" & _
                 " estén dentro"
    WScript.Echo "del subárbol cuya raíz es la OU; en caso de no re" & _
                 "cibirse, sólo"
    WScript.Echo "se cambiarán los usuarios contenidos en la propia OU"
    WScript.Echo ""
    WScript.Echo "- /Grupo: nombre_grupo (Opcional):"
    WScript.Echo "Nombre WinNT del grupo de seguridad al que perten" & _
                 "ecen las"
    WScript.Echo "cuentas de usuario a las que se realizarán los ca" & _
                 "mbios. Si se"
    WScript.Echo "omite y también se omiten /Dominio y /OU, se real" & _
                 "izarán los"
    WScript.Echo "cambios en el dominio al que pertenece el equipo " & _
                 "desde el que"
    WScript.Echo "se lanza el script"
    WScript.Echo ""
    WScript.Echo "- /Log: fichero (Opcional):"
    WScript.Echo "Ruta y nombre de un fichero de texto en el que se" & _
                 " volcarán los"
    WScript.Echo "resultados de la ejecución del script"
    WScript.Echo ""
    WScript.Echo "- /RDN: nombre_relativo (Opcional):"
    WScript.Echo "Si se pasa este parámetro, no sólo se cambiará el" & _
                 " displayName,"
    WScript.Echo "si no que también se cambiará el nombre del objet" & _
                 "o de usuario,"
    WScript.Echo "es decir, su distinguishedRelativeName, o sea, la" & _
                 " parte del"
    WScript.Echo "nombre distinguido correspondiente al propio obje" & _
                 "to, no al"
    WScript.Echo "contenedor en el que está"
    WScript.Echo ""
    WScript.Echo "- /?: ayuda (Opcional):"
    WScript.Echo "Muestra la ayuda en línea"
    WScript.Echo ""
    WScript.Echo ""
    WScript.Echo "Ejemplos:"
    WScript.Echo ""
    WScript.Echo "- Se cambiará el nombre para mostrar a los usuari" & _
                 "os del dominio desde el que se"
    WScript.Echo "lanza el script. Los resultados de la ejecución s" & _
                 "e mostrarán por pantalla:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs"
    WScript.Echo ""
    WScript.Echo "- Se cambiarán el nombre para mostrar y el nombre" & _
                 " de objeto a los usuarios del"
    WScript.Echo "dominio desde el que se lanza el script. Los resu" & _
                 "ltados de la ejecución se"
    WScript.Echo "mostrarán por pantalla:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs /RDN"
    WScript.Echo ""
    WScript.Echo "- Se cambiarán el nombre para mostrar y el nombre" & _
                 " de objeto a los usuarios del"
    WScript.Echo "dominio tia.org. Los resultados de la ejecución s" & _
                 "e mostrarán por pantalla. Los"
    WScript.Echo "resultados de la ejecución se mostrarán por pantalla:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs /Dominio" & _
                 ":tia.org /RDN"
    WScript.Echo ""
    WScript.Echo "- Se cambiarán el nombre para mostrar y el nombre" & _
                 " de objeto a los usuarios"
    WScript.Echo "contenidos en la unidad organizativa tia.org/Agen" & _
                 "tes. Los resultados de la"
    WScript.Echo "ejecución se mostrarán por pantalla:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs /OU:tia." & _
                 "org/Agentes /RDN"
    WScript.Echo ""
    WScript.Echo "- Se cambiarán el nombre para mostrar y el nombre" & _
                 " de objeto a los usuarios"
    WScript.Echo "contenidos en la unidad organizativa tia.org/Agen" & _
                 "tes y los de todas las OU"
    WScript.Echo "contenidas en ella. Los resultados de la ejecució" & _
                 "n se mostrarán por pantalla:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs /OU:tia." & _
                 "org/Agentes /Recursivo /RDN"
    WScript.Echo ""
    WScript.Echo "- Se cambiarán el nombre para mostrar y el nombre" & _
                 " de objeto a los usuarios del"
    WScript.Echo "grupo TIA\Agentes. Los resultados de la ejecución" & _
                 " se mostrarán por pantalla:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs /Grupo:T" & _
                 "IA\Agentes /RDN"
    WScript.Echo ""
    WScript.Echo "- Se cambiarán el nombre para mostrar y el nombre" & _
                 " de objeto a los usuarios del"
    WScript.Echo "dominio tia.org. Los resultados de la ejecución s" & _
                 "e guardarán en el fichero"
    WScript.Echo "\\bacteriosrv\logs\cdn.txt:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo cambiar-displayname.vbs /Dominio" & _
                 ":tia.org"
    WScript.Echo "/Log:\\bacteriosrv\logs\cdn.txt /RDN"
    WScript.Echo ""
    WScript.Echo ""
    WScript.Echo ""

End Sub 's_Ayuda

Function f_NTaDN(str_RutaNT, str_DN)
'***********************************************************************
'* Procedimiento: f_NTaDN                                              *
'* Tipo         : Función                                              *
'* Devolución   : Cadena                                               *
'* Fecha y Hora : May 2007                                             *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe o bien el nombre NT              *
'*                ("dominio\nombre"), como primer parámetro, de un     *
'*                objeto de dominio y devuelve el nombre distinguido   *
'*                de ese objeto, o bien el nombre distinguido de un    *
'*                objeto, como segundo parámetro, y devuelve el        *
'*                nombre NT.                                           *
'*                Basada en el código de Richard Mueller, MVP de       *
'*                Scripting y ADSI (http://www.rlmueller.net)          *
'***********************************************************************

    'Constantes para el objeto NameTranslate
    Const ADS_NAME_INITTYPE_GC = 3
    Const ADS_NAME_TYPE_NT4 = 3
    Const ADS_NAME_TYPE_1779 = 1

    Dim obj_TraductorDeNombres
    Dim int_De, int_A,str_Nombre

    If  Len(str_RutaNT) > 0 Then

        int_De = ADS_NAME_TYPE_NT4
        int_A = ADS_NAME_TYPE_1779
        str_Nombre = str_RutaNT

    ElseIf Len(str_DN) > 0 Then

        int_De = ADS_NAME_TYPE_1779
        int_A = ADS_NAME_TYPE_NT4
        str_Nombre = str_DN

    Else

        WScript.Echo "Error 1 en f_NTaDN: No se ha pasado " & _
                     "ningún nombre para traducir."
        Exit Function
    End If
    'Creamos el objeto NameTranslate.
    Set obj_TraductorDeNombres = CreateObject("NameTranslate")

    On Error Resume Next

    'Lo iniciamos localizando el catálogo global
    obj_TraductorDeNombres.Init ADS_NAME_INITTYPE_GC, ""

    'Establecemos el parámetro de nombre en el traductor de nombres
    obj_TraductorDeNombres.Set int_De, str_Nombre

    'Usamos el método Get del traductor de nombres para obtener el
    'nombre traducido
    f_NTaDN = obj_TraductorDeNombres.Get(int_A)

    'Limpieza de kks :-)
    Set obj_TraductorDeNombres = Nothing

    On Error GoTo 0

End Function 'f_NTaDN

Function f_DomDNSaLDAP(str_Dominio)
'***********************************************************************
'* Procedimiento: f_DomDNSaLDAP                                        *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 01/07/2008 15:51:56                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe un nombre de dominio FQDN        *
'*                (dominio.local) y devuelve su nombre distinguido     *
'*                (DC=dominio,DC=local)                                *
'***********************************************************************

    f_DomDNSaLDAP = "DC=" & _
                    Replace(str_Dominio, ".",",DC=", 1, -1, 1)    

End Function 'f_DomDNSaLDAP

Function f_OUCanonico2DN(str_Nombre)
'***********************************************************************
'* Procedimiento: f_OUCanonico2DN                                      *
'* Tipo         : Función                                              *
'* Devolución   : Cadena                                               *
'* Fecha y Hora : 25/03/2009 10:46:01                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe un nombre canónico de unidad     *
'*                organizativa (dominio.local/OU) y devuelve su        *
'*                nombre distinguido (OU=OU,DC=dominio,DC=local)       *
'***********************************************************************

    Dim arr_Nombres     'As String Array
    Dim str_Temp        'As String
    Dim str_Devolucion  'As String
    Dim int_Nombre      'As Integer
    
    'Reemplazamos las apariciones de barras de división con el
    'significado quitado por un caracter no imprimible, para así no
    'confundirlas con las barras de división separadoras de nombres
    str_Temp = Replace(str_Nombre, "\/", Chr(1))
    
    'Obtenemos el array de nombres usando la barra de división como
    'separador
    arr_Nombres = Split(str_Temp, "/")
    
    'Iniciamos la devolución con el nombre distinguido del dominio
    'que es el primer elemento del array
    str_Devolucion = "," & f_DomDNSaLDAP(arr_Nombres(0))
    
    'Recorremos el array, comenzando por el segundo elemento, pues el
    'primero (nombre del dominio) ya ha sido procesado
    For int_Nombre = LBound(arr_Nombres) + 1 To UBound(arr_Nombres)
    
        'Agregamos el nombre actual como OU, anteponiendolo a la
        'devolución montada hasta ahora
        str_Devolucion = ",OU=" & arr_Nombres(int_Nombre) & _
                         str_Devolucion
    
    Next 'int_Nombre
    
    'A la devolución montada le sobra la primera coma; la quitamos
    str_Devolucion = Right(str_Devolucion, Len(str_Devolucion) - 1)
    
    'Devolvemos el nombre distinguido obtenido
    f_OUCanonico2DN = replace(str_Devolucion, Chr(1), "\/")

End Function 'f_OUCanonico2DN

Function f_DNDominio()
'***********************************************************************
'* Procedimiento: f_DNDominio                                          *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 25/03/2009 12:06:40                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función devuelve el nombres distinguido del     *
'*                dominio al que pertenece el equipo desde el que se   *
'*                lanza el script                                      *
'***********************************************************************

    Dim obj_RootDSE 'As rootDSE

    'Conectamos con rootDSE
    Set obj_RootDSE = GetObject("LDAP://rootDSE")

    'Devolvemos el nombre NT del dominio
    f_DNDominio = obj_RootDSE.Get("defaultNamingContext")
    
    'Limpieza de culete :-))
    Set obj_RootDSE = Nothing

End Function 'f_DNDominio

Function f_UsuariosOU(str_DNOU, bol_Recursivo) 'As String Array
'***********************************************************************
'* Procedimiento: f_UsuariosOU                                         *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 25/03/2009 12:24:02                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe el nombre distinguido de una OU  *
'*                y devuelve un array con los nombres distinguidos de  *
'*                los usuarios que están dentro de esa OU. La función  *
'*                recibe también una booleana que, en el caso de que   *
'*                esté a True, provoca que se listen los usuarios de   *
'*                todo el subarbol que empieza en la OU; si la         *
'*                booleana está a False, sólo se listan los usuarios   *
'*                pretenecientes a la propia OU. Dada la operativa de  *
'*                esta función, se puede pasar el nombre de un dominio *
'*                y la booleana de recursividad a True para obtener un *
'*                array con los nombres distinguidos de todos los      *
'*                usuarios del dominio                                 *
'***********************************************************************

        'Declaraciones necesarias para obtener los
        'usuarios de la OU
        Dim obj_Conexion  'As ADODB.Connection
        Dim obj_Comando   'As ADODB.Command
        Dim rs_Usuarios   'As ADODB.Recordset
        Dim str_Filtro    'As String
        Dim str_Consulta  'As String
        Dim str_Usuarios  'As String
        Dim str_Atributos 'As String

        'Creamos un objeto de conexión a ADO
        Set obj_Conexion = CreateObject("ADODB.Connection")
        
        'Creamos un objeto de comando de ADO
        Set obj_Comando = CreateObject("ADODB.Command")

        'Establecemos el proveedor de ADSI para el objeto
        'conexión y abrimos la conexión
        obj_Conexion.Provider = "ADsDSOOBject"
        obj_Conexion.Open "Active Directory Provider"

        'Establecemos la conexión como conexión activa
        'del objeto comando
        Set obj_Comando.ActiveConnection = obj_Conexion

        'Montamos el filtro por el cual sólo obtendremos los
        'objetos de tipo cuenta de usuario
        str_Filtro = "(&(objectCategory=person)(objectClass=user))"

        'Como atributo devuelto por la consulta establecemos
        'el nombre distinguido
        str_Atributos = "distinguishedName"

        'Montamos la cadena de consulta ADSI
        str_Consulta = "<LDAP://" & str_DNOU & ">;" & _
                        str_Filtro & ";" & _
                        str_Atributos

        'En la cadena de consulta nos falta especificar el ámbito
        'de búsqueda
        If bol_Recursivo Then 

            'La búsqueda será en todo el subárbol de raíz la OU
            'recibida como parámetro
            str_Consulta = str_Consulta & ";subtree"

        Else

            'La consulta sólo devolverá los usuarios que están en
            'la propia OU recibida como parámetro
            str_Consulta = str_Consulta & ";onelevel"    

        End If

        'Establecemos la cadena de consulta en el objeto comando
        obj_Comando.CommandText = str_Consulta
        
        'Con estas tres líneas garnatizamos que se pagine la
        'consulta, con lo que no se limitará a los 1000 primeros
        'objetos
        obj_Comando.Properties("Page Size") = 1000
        obj_Comando.Properties("Timeout") = 300
        obj_Comando.Properties("Cache Results") = False

        'Ejecutamos la consulta
        Set rs_Usuarios = obj_Comando.Execute

        'Recorremos los usuarios devueltos
        Do Until rs_Usuarios.EOF

            'Nos aseguramos de sólo incluir objetos de usuario
            If f_TipoObjeto( _
                   rs_Usuarios.Fields("distinguishedName")) = _
                                               clas_USUARIO Then
            
                'Agregamos el usuario actual, añadiendo un salto
                'de línea como separador
                str_Usuarios = str_Usuarios & _
                               rs_Usuarios.Fields( _
                                       "distinguishedName") & _
                               VbCrLf
    
                'Vamos al próximo registro
                rs_Usuarios.MoveNext
                
            End If

        Loop

        'Cerramos la conexión
        obj_Conexion.Close
        
        'Limpieza de KKs
        Set obj_Conexion = Nothing
        Set obj_Comando = Nothing
        Set rs_Usuarios = Nothing

        'Devolvemos el array
        f_UsuariosOU = Split(str_Usuarios, VbCrLf)

End Function 'f_UsuariosOU

Function f_TipoObjeto(str_DN)
'***********************************************************************
'* Procedimiento: f_TipoObjeto                                         *
'* Tipo         : Función                                              *
'* Devolución   : Entero                                               *
'* Fecha y Hora : 25/03/2009 13:06:54                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe un nombre distinguido de objeto  *
'*                y mira si se trata de un dominio, contenedor,        *
'*                unidad organizativa, grupo, equipo usuario o         *
'*                contacto. La devolución correspondiente será una de  *
'*                estas constantes:                                    *
'*                                                                     *
'*                clas_DOMINIO = 1                                     *
'*                clas_CONTENEDOR = 2                                  *
'*                clas_OU = 3                                          *
'*                clas_GRUPO = 4                                       *
'*                clas_EQUIPO = 5                                      *
'*                clas_USUARIO = 6                                     *
'*                clas_CONTACTO = 7                                    *
'*                                                                     *
'***********************************************************************

    Const vbTextCompare = 1

    Dim str_Clase      'As String
    Dim bol_Usuario    'As Boolean
    Dim int_Devolucion 'As Integer
    Dim str_Tipos      'As String
    Dim obj_Objeto     'As iADs?
    
    'Obtenemos el objeto de nombre distinguido el recibido como
    'parámetro
    Set obj_Objeto = GetObject("LDAP://" & str_DN)
    
    'Recorremos el array con sus tipos de objeto
    For Each str_Clase In obj_Objeto.Get("objectClass")
    
        'Miramos si el tipo es uno de estos
        Select Case LCase(str_Clase)
        
            'De tipo dominio
            Case "domain"
            
                int_Devolucion = clas_DOMINIO
                Exit for
            
            'De tipo contenedor
            Case "container"
            
                int_Devolucion = clas_CONTENEDOR
                Exit For
                
            'De tipo unidad organizativa
            Case "organizationalunit"
            
                int_Devolucion = clas_OU
                Exit For
                
            'De tipo grupo
            Case "group"
            
                int_Devolucion = clas_GRUPO
                Exit For
                
            'De tipo usuario. Dado que tanto los usuarios, como
            'los contactos, como los equipo, pertenecen a esta
            'clase, es necesario que verifiquemos cuál de estos
            'tres tipos es. Para ello pasamos el atributo
            'objectClass a una cadena y buscamos si aparece
            '"Contacto" o "Computer" en ella, con lo que sabremos
            'si es o lo uno o lo otro; en el caso de que no aparezcan
            'ninguno de los dos, se trata de un usuario
            Case "user"
            
                'Pasamos el array de clases a una cadena
                str_Tipos = Join(obj_Objeto.Get("objectClass"),";")
            
                'Si se cumple esta condición, se trata de un contacto
                If InStr(1, _
                         str_Tipos, _
                         "contact", _
                         vbTextCompare) > 0 Then
                
                    int_Devolucion = clas_CONTACTO
                
                'Si se cumple esta condición, se trata de un equipo    
                ElseIf InStr(1, _
                             str_Tipos, _
                             "computer", _
                             vbTextCompare) > 0 Then
                
                    int_Devolucion = clas_EQUIPO
                    
                'Al no cumplirse ninguna de las dos, se trata de
                'un usuario
                Else
                
                    int_Devolucion = clas_USUARIO
                    
                End if
                Exit For
                
            'De tipo contacto
            Case "contact"
            
                int_Devolucion = clas_CONTACTO
                Exit For
                
            'De tipo equipo
            Case "computer"
            
                int_Devolucion = clas_EQUIPO
                Exit for
        
        End select
    
    Next 'str_Clase
    
    'Devolvemos el tipo obtenido
    f_TipoObjeto = int_Devolucion
    
    'Vaciamos el objeto
    Set obj_Objeto = Nothing

End Function 'f_TipoObjeto

Function f_UsuariosGrupo(str_DN)
'***********************************************************************
'* Procedimiento: f_UsuariosGrupo                                      *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 25/03/2009 14:00:07                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe un nombre distinguido de grupo   *
'*                y devuelve un array con los nombres distinguidos de  *
'*                los usuarios que son miembros de él                  *
'*                                                                     *
'***********************************************************************

    Dim obj_Grupo       'As iADsGroup
    Dim arr_Miembros    'As String Array
    Dim str_Miembro     'As String
    Dim str_Devolucion  'As String

'     WScript.Echo str_DN

    'Obtenemos el objeto del grupo
    Set obj_Grupo = GetObject("LDAP://" & str_DN)
    
    'Cargamos su información
    obj_Grupo.GetInfo
     
    'Obtenemos el array con los nombres distinguidos de los miembros
    'del grupo
    arr_Miembros = obj_Grupo.GetEx("member")
     
    'Recorremos los miembros del grupo
    For Each str_Miembro in arr_Miembros
    
        'Verificamos si el objeto se trata de un usuario
        If f_TipoObjeto(str_Miembro) = clas_USUARIO Then
        
            'Agregamos el usuario a la devolución y añadimos
            'un salto de línea como separador
            str_Devolucion = str_Devolucion & _
                             str_Miembro & _
                             VbCrLf

        End If
        
    Next
    
    'Quitamos el salto de línea final
    If Len(str_Devolucion) > 0 Then _
                    str_Devolucion = Left(str_Devolucion, _
                                          Len(str_Devolucion) -2)
                                          
    'Efectuamos la devolución
    f_UsuariosGrupo = Split(str_Devolucion, VbCrLf)
    
    'Limpieza de bullarenga :-)
    Set obj_Grupo = Nothing

End Function 'f_UsuariosGrupo

Sub s_RDN(str_DN, bol_RDN) 'As String
'***********************************************************************
'* Procedimiento: f_RDN                                                *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 15/12/2008 12:55:15                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe un nombre distinguido de         *
'*                usuario y devuelve un nuevo RDN montándolo como      *
'*                "CN=Apellidos, Nombre" siempre que sea posible       *
'***********************************************************************

	Dim str_RDN 'As String
	Dim obj_Usuario 'As iADsUser
	Dim str_SN 'As String
	Dim str_GN 'As String
	Dim obj_OU
	Dim obj_NuevoUsuario
	Dim str_Temp

	'Obtenemos el objeto usuario con el proveedor LDAP
	Set obj_Usuario = GetObject("LDAP://" & str_DN)
	
	'Establecemos control de errores
	On Error Resume Next

    str_Salida = ""
    
	str_Salida = str_Salida & _
	             obj_Usuario.get("displayName")
	str_Salida = str_Salida & vbTab
	str_Salida = str_Salida & _
	             obj_Usuario.Get("distinguishedName") & vbTab
	
	'Obtenemos los apellidos del usuario
	str_SN = "" & obj_Usuario.LastName

	'Obtenemos el nombre propio del usuario
	str_GN = "" & obj_Usuario.FirstName

	'Si tanto el nombre propio como los apellidos tienen contenido
	If Len(Trim(str_SN)) > 0 _
	And Len(Trim(str_GN)) > 0 Then

		'Montamos el nombre relativo como "Apellidos, Nombre"
		str_RDN = str_SN & ", " & str_GN
		
		str_Temp = ""
		
		str_Temp = obj_Usuario.Get("displayName")
		
		If str_RDN = str_Temp Then
		
		    str_Salida = ""
		    Set obj_Usuario = Nothing
		    Exit Sub
		    
		End If

	'En cualquier otro caso no hay que hacer nada
	Else
	
	    str_Salida = ""
        Set obj_Usuario = Nothing
	    Exit Sub

	End If
	
	'Si el nombre relativo obtenido no está vacío
	If Len(Trim(str_RDN)) > 0 Then

		'Ponemos el nuevo displayName
		obj_Usuario.Put "displayName", str_RDN
		
		'Guardamos el cambio
		obj_Usuario.SetInfo
		
		str_Salida = str_Salida & _
		             str_RDN & vbTab
		
		'Si hay que cambiar también el nombre del objeto
        If bol_RDN Then

            'Obtenemos el contenedor donde está
    		Set obj_OU = GetObject(obj_Usuario.Parent)
    		
    		'Movemos el usuario (cambar el nombre de un objeto en AD
    		'no es cambiar su propiedad nombre, si no que es moverlo
    		'Como nuevo nombre relativo ponemos el nombre obtenido y
    		'cambiamos las comas que encontremos en él por barra de
    		'división entera coma(backslash) "\,", para así quitarle
    		'el significado a la coma (de otra forma, se produce un
    		'error
    		Set obj_NuevoUsuario = obj_OU.MoveHere( _
    		                            "LDAP://" & str_DN, _
    		                            "CN=" & Replace(str_RDN, _
    		                                            ",", _
    		                                            "\,"))
    		                                            
    		'Volcamos en la salida el nuevo nombre distinguido
    		str_Salida = str_Salida & _
    		             obj_NuevoUsuario.Get("distinguishedName")
    		             
    		'Vaciamos el objeto del usuario una vez movido
    		Set obj_NuevoUsuario = Nothing
		
		'Si no hay que cambiar el nombre del objeto, se refleja
		'esto en la salida
		Else
		
		    'Volcamos la falta de cambios en la salida
		    str_Salida = str_Salida & "Sin cambios"
		    
        End If
	
    	'Mostramos la información por pantalla
    	WScript.Echo str_Salida
    	
    	'Si hay que guardar la información en el log, lo
    	'hacemos
    	If Len(str_Log) > 0 Then obj_ts.WriteLine str_Salida
    	
    	'Limpiamos l varable de salida
    	str_Salida = ""
        
	End If

	'Devolvemos el control de errores a CSCript
	On Error Goto 0

	'Vaciamos el objeto usuario
	Set obj_Usuario = Nothing

End Sub 's_RDN

3 comentarios to “Usuarios de Active Directory con Formato “Apellidos, Nombre””

  1. fabian said

    Pregunta, como guardo el script? copio el texto y lo guardo como archivo.vbs?? Gracias!!

  2. Emiliano said

    Hola! Tengo varias OU, con usuarios creados… y los mismos se ven en el formato “nombre – apellido”, yo necesito cambiar esa nomeclatura y que sea “apellido – nombre”, para ellos tengo que aplicar el script? es la única solución?
    No entendi complicar el script… me podrias orientar?
    Solo pretendo corregir esos usuarios, no quiero cambiar más nada… te digo esto porque me da miedo a que me modifique algo más en el dominio.

    Muchas gracias!
    Saludos.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: