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…

PowerShell: Cmdlet Para Importar Contactos de un Exchange de Producción a Otro Exchange de Laboratorio Sin Relación de Confianza Entre Ambos

Posted by urpiano en Viernes 30 \30\UTC noviembre \30\UTC 2012

Este Cmdlet lo he desarrollado como paso previo a un script que he tenido que crear de sincronización de contactos que venían de otras organizaciones de correo. Como quería que en el laboratorio estuviesen los mismos contactos que en producción y además con las mismas propiedades fundamentales (nombres, direcciones de correo y atributos personalizados), desarrollé este Cmdlet para poder, de un plumazo, traer de nuevo los contactos tantas veces fuera necesario, eliminando los que ya hubiera creado con anterioridad y que, por haber realizado cambios en ellos al ejecutar otros scripts, no estuvieran ya igual que en producción.

El Cmdlet requiere que sea lanzado desde un equipo perteneciente al dominio de destino, el del laboratorio, que el usuario que lo lance tenga permisos de administrador en el dominio del laboratorio, que estén instaladas las herramientas de administración de Exchange en el equipo desde el que se lanza el script y que tenga PowerShell v2.0. Además de todo esto, es necesario que la OU de destino sea un reflejo de la de origen, es decir, que tenga las mismas OUs dentro de ella.

Junto con este Cmdlet (Import-Contacts) hay dos Cmdlets más: Get-PrimarySmtpAddress (que devuelve la dirección de correo SMTP primaria del array de direcciones proxy recibido) y Add-PsSnapinControled (que se encarga de cargar el PsSnapin de Exchange si no está ya cargado). Es necesaria la presencia de estos otros dos Cmdlets, pues son invocados desde Import-Contacts. Lo mejor es guardar en un fichero ps1 los tres Cmdlets, invocar este ps1 desde la consola de PowerShell y ya se puede hacer una llamada a Import-Contacs.

El Cmdlet incluye ayuda, pego a continuación la ayuda con modificador -Detailed (esta ayuda se basa en la prestación de ayuda basada en comentarios de PowerShell 2.0 ¿Qué todavía tienes la versión 1.0 y no la 2.0? ¿A qué esperas para instalarla si es mucho mejor?):

NOMBRE
    Import-Contacts

SINOPSIS
    Importa contactos de Exchange desde una organización a otra.

SINTAXIS
    Import-Contacts [-ProductionDC]  [-LaboratoryDC]  [-ProductionOU]  [-Labo
    ratoryOU]  [[-User] ] [[-Password] ] [-Anonymous]  [-Detailed] []

DESCRIPCIÓN
    Este Cmdlet permite importar los contactos de una organización de
    Exchange en otra, cuando no existe relación de confianza entre ambos dominios,
    lo que impide usar Cmdlets de Exchange para acceder al Exchange de origen,
    siendo necesario hacerlo vía LDAP. La importación la hace creando en el destino
    los contactos con los mismos atributos esenciales: nombres, direcciones y
    atributos personalizados.

    Es ideal para poder importar a un entorno de laboratorio/preprodución los
    contactos que tenemos en producción, para así poder, por ejemplo, desarrollar un
    script de sincronización trabajando con casos lo más cercanos a la realidad de
    nuestro entorno de producción.

    El Cmdlet permite importar todos los contactos del dominio (poniendo como valor
    del parámetro ProductionOU el nombre distinguido del dominio de origen) o sólo
    los contactos contenidos en el subárbol que tiene como raíz determinada OU
    (poniendo como valor del parámetro ProductionOU el nombre distinguido de dicha
    OU de origen).

    Los contactos son creados en la OU del dominio de destino especificado por el
    parámetro LaboratoryOU, previo borrado de los que ya estuvieran ahí contenidos
    (recordemos que el Cmdlet tiene como propósito el importar a laboratorio lo que
    tengamos en producción. y por tanto es necesario borrar lo que haya en la OU de
    destino).

    El script requiere tener ya creado el subárbol de unidades organizativas en el
    destino que sea homólogo al de origen, pues en la creación de del contacto se
    usa el nombre distinguido del contacto de origen sustituyéndole la parte que es
    igual al parámetro ProductionOU por la del parámetro LaboratoryOU. Veamos un
    ejemplo:

    ProductionOU : OU=Contactos,DC=tia,DC=org
    LaboratoryOU : OU=Contactos,DC=tia,DC=lab
    ContactoDN   : CN=Anselmo Nigote,OU=SOBRINA,OU=Contactos,DC=tia,DC=org

    En este caso, el contacto nuevo se intentará crear en la OU
    OU=SOBRINA,OU=Contactos,DC=tia,DC=lab y por tanto deberá existir previamente.

PARÁMETROS
    -ProductionDC 
        Nombre del controlador de dominio de producción al que se consultará.
        Este parámetro admite el alias "DcP".

    -LaboratoryDC 
        Nombre del controlador de dominio de laboratorio sobre el que se operará
        en el entorno de laboratorio. Este parámetro admite el alias "DcL".

    -ProductionOU 
        Nombre distinguido de la raíz del árbol, dentro del dominio de
        producción, en la que se consultará para obtener los contactos. Este parámetro
        admite el alias "OuP".

    -LaboratoryOU 
        Nombre distinguido de la raíz del árbol, dentro del dominio de
        laboratorio, en la que se crearán los contactos. Este parámetro admite el alias
        "OuL".

    -User 
        Nombre del usuario con el que se conectará al dominio de producción. Es
        requerido si no se pasa el modificador -Anonymous; si el modificador -Anonymous
        se pasa y también se pasa este parámetro, el nombre de usuario será ignorado.
        Este parámetro admite el alias "U".

    -Password 
        Contraseña del usuario con el que se conectará al dominio de producción.
        Es requerido si no se pasa el modificador -Anonymous; si el modificador
        -Anonymous se pasa y también se pasa este parámetro, la contraseña será
        ignorada. Este parámetro admite el alias "C".

    -Anonymous []
        Si se pasa este modificador, la conexión al dominio de producción se
        realizará de forma anónima, ignorándose los parámetros User y PAssword si fuesen
        pasados también. Este modificador admite el alias "A".

    -Detailed []
        Si se pasa este modificador, se mostrará información sobre los contactos
        según se van creando. Este modificador admite el alias "D".

        Este cmdlet admite los parámetros comunes Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer y OutVariable. Para obtener más información, escriba:
        "get-help about_commonparameters".

    -------------------------- EJEMPLO 1 --------------------------

    PS C:\>Import-Contacts -ProductionDC dc1.tia.org `

    >> -LaboratoryDC dc1.tia.lab `
    >> -ProductionOU "OU=Contactos,DC=tia,DC=org" `
    >> -LaboratoryOU "OU=Contactos,DC=tia,DC=lab" `
    >> -User bacterio `
    >> -Password OfeliaFoca
    >>

    En este ejemplo Se importan los contactos del origen, que están en el subárbol
    de producción cuyo nombre distinguido es OU=Contactos,DC=tia,DC=org, al
    destino, ubicándolos en el subárbol del laboratorio cuyo nombre distinguido es
    OU=Contactos,DC=tia,DC=lab. Se conectará al origen con el usuario "bacterio" y
    la contraseña "OfeliaFoca".

    -------------------------- EJEMPLO 2 --------------------------

    PS C:\>Import-Contacts -ProductionDC dc1.tia.org `

    >> -LaboratoryDC dc1.tia.lab `
    >> -ProductionOU "OU=Contactos,DC=tia,DC=org" `
    >> -LaboratoryOU "OU=Contactos,DC=tia,DC=lab" `
    >> -Anonymous
    >>

    En este ejemplo Se importan los contactos del origen, que están en el subárbol
    de producción cuyo nombre distinguido es OU=Contactos,DC=tia,DC=org, al
    destino, ubicándolos en el subárbol del laboratorio cuyo nombre distinguido es
    OU=Contactos,DC=tia,DC=lab. Se conectará al origen de forma anónima.

NOTAS
    Para ver los ejemplos, escriba: "get-help Import-Contacts -examples".
    Para obtener más información, escriba: "get-help Import-Contacts -detailed".
    Para obtener información técnica, escriba: "get-help Import-Contacts -full".

Este es el código de los tres Cmdlets, los dos de apoyo (Get-PrimarySmtpAddress y Add-PsSnapinControled) y el de importación de contactos en sí (Import-Contacts):

Function Get-PrimarySmtpAddress(
    [parameter(Mandatory=$true,
               HelpMessage="Entre el array de direcciones proxy a analizar")]
    [String[]]$proxyAddresses)
{
    ForEach($AddressIn$proxyAddresses)
    {
        If($Address.SubString(0,4) -ceq"SMTP")
        {
            Return$Address.SubString(5,$Address.Length -5)
        }
    }
<#
    .SYNOPSIS
        Devuelve la dirección SMTP primaria de el array de direcciones proxy
recibido como parámetro.

    .DESCRIPTION
        Esta función devuelve la dirección SMTP primaria de el array de
direcciones proxy recibido como parámetro. Para hacer esto, la función busca el
elemento que empieza con el identificador de protocolo SMTP en mayúsculas, que
es la forma que tiene Exchange de identificar la dirección SMTP primaria. La
devolución sólo contendrá la dirección, sin el protocolo.

    .PARAMETER proxyAddresses
        Este es el array de direcciones de correo que se corresponde con el
atributo proxyAddresses de un objeto DirectoryEntry.

    .EXAMPLE
        PS C:\> $Direcciones = "smtp:morti@tia.org","SMTP:mortadelo@tia.org"
        PS C:\> Get-PrimarySmtpAddress -proxyAddresses $Direcciones

En este ejemplo hemos puesto un array de cadenas con dos direcciones SMTP para
que se vea más claro el contenido de lo que evalúa la función. La devolución que 
se obtiene es "mortadelo@tia.org".

    .INPUTS
        System.String, System.Byte, System.Int32, System.Object

    .OUTPUTS
        System.String, System.Byte, System.Int32, System.Object

    .NOTES
        Fernando Reyes López © 11/2012

    .LINK
        http://freyes.svetlian.com

    .LINK
        https://urpiano.wordpress.com
#>
}
FunctionAdd-PsSnapinControled([string]$PsSnapin)
{
    # Lo primero es almacenar la acción configurada para los errores que no# interrumpen (de forma predeterminada es "Continue") y vaciar la variable# de errores$Accion=$ErrorActionPreference$Error.Clear()

    # Cambiamos la acción a Stop, para que sí interrumpan y puedan ser# capturados por Try/Catch$ErrorActionPreference="Stop"# Miramos si está cargada la snap-in de SCVMM. Si no lo está dará error,# de ahí el que lo capturemos con Try/CatchTry
    {
        # Enviamos al limbo la devolución de Get-PsSnapin para evitar# que sea parte de la devolución del Cmdlet        [Void](Get-PSSnapin$PsSnapin)
    }
    Catch
    {
        # Si estamos aquí, es que Get-PsSnapin produjo un error y por tanto# el snap-in de SCVMM no está cargado, por lo que debemos cargarlo.# Para cargar el snap-in de SCVMM usamos una estructura Try/Catch,# por si la consola de SCVMM no estuviera instalada en el equipo y# por tanto no existiera este snap-in.Try
        {
            Add-PSSnapin-name$PsSnapin
        }
        Catch
        {
            # Como se ha producido error al intentar cargar el snap-in de# SCVMM, se lo advertimos al usuario, restituimos la acción ante# errores que no interrumpen y terminamos el CmdletWrite-Host"El Snapin $PsSnapin no está presente en este equipo."

        }
    }
    # Restituimos la acción original ante errores que no interrumpen$ErrorActionPreference=$Accion<#

    .SYNOPSIS
    Carga un PsSnapin controlando errores o si está ya cargado.
    .DESCRIPTION
    Esta función permite cargar un PsSnapin de forma controlada, pues evita el
    error que se produciría en el caso de que el PsSnapin ya estuviera cargado.
    Además de esto, incluye control de errores para el casode intentar cargar un
    PsSnapion cuyos ensamblados no estuvieran presentes en el equipo.
    .PARAMETER PsSnapin
    Se trata de una cadena de texto con el nombre del PsSnapin que se debe
    cargar. El nombre del PsSnapin debe incluir el espacio de nombres que lo
    alberga (por ejemplo Microsoft.Exchange.Management.PowerShell.Admin es el
    PsSnapin que carga los Cmdlets de Exchange).
    .EXAMPLE
        Add-PsSnapinControled Microsoft.Exchange.Management.PowerShell.Admin
    Carga el PsSnapin que permite utilizar los Cmdlets de Exchange. Si ya está
    cargado, evita cargarlo de nuevo; si falla la carga es porque las
    herramientas administrativas de Exchange no están instaladas en el equipo.
    .AUTHOR
    Fernando Reyes López © 2011
#>
}
FunctionImport-Contacts
{
    [CmdletBinding()]
    [OutputType([System.String[]])]
    param(
    [parameter(Mandatory=$true, 
               HelpMessage="Entre el nombre o IP del DC de origen")]
    [Alias("DcP")][String] $ProductionDC, 
    [parameter(Mandatory=$true, 
               HelpMessage="Entre el nombre o IP del DC de destino")]
    [Alias("DcL")][String] $LaboratoryDC,
    [parameter(Mandatory=$true, 
               HelpMessage="Entre el nombre distinguido de la OU de origen")]
    [Alias("OuP")][String] $ProductionOU,
    [parameter(Mandatory=$true, 
               HelpMessage="Entre el nombre distinguido de la OU de destino")]
    [Alias("OuL")][String] $LaboratoryOU,
    [Alias("U")]  [String] $User,
    [Alias("C")]  [String] $Password,
    [Alias("A")]  [Switch] $Anonymous,
    [Alias("D")]  [Switch] $Detailed)

    # Cuando no se ha recibido el nombre del DC de producciónIf([String]::IsNullOrEmpty($ProductionDC))
    {
        # sólo aparecerá el protocolo LDAP en la ruta LDAP$Protocolo="LDAP://"
    }
    # cuando sí se ha recibido el DC de producciónElse
    {
        # Aparecerá el protocolo y el DC en la ruta LDAP$Protocolo="LDAP://$ProductionDC/"
    }
    # LA ruta LDAP será la ruta que hemos obtenido hasta ahora más el nombre# distinguido del contenedor de producción en el que se obtendrán los# destinatarios$RutaProduccion="$Protocolo$ProductionOU"# Si se debe conectar a producción de forma anónimaIf($Anonymous)
    {
        # Creamos un objeto de tipo de autenticación anónimo para el acceso a un# servicio de directorio$TipoAutenticacion=`
                       [System.DirectoryServices.AuthenticationTypes]::Anonymous# Creamos un objeto DirectoryEntry que se carga con una conexión al# controlador de dominio de producción, pasando el objeto de tipo de# autenticación (anónima) que creamos antes$DeDominioRemoto=New-ObjectSystem.DirectoryServices.DirectoryEntry`$RutaProduccion,`$null,`$null,`$TipoAutenticacion
    }
    # si la conexión no es anónima ni se ha recibido usuario y contraseña no se# podrá conectar al dominio de producción.ElseIf([String]::IsNullOrEmpty($User))
    {
        # advertimos al usuario y terminamos la función devolviendo NullWrite-Host"No se ha especificado que la conexión al dominio remoto sea
                    anónima ni se ha pasado usuario y contraseña para conectar.
                    La conexión debe realizarse de forma anonima o con
                    credenciales del dominio remoto"Return
    }
    # La conexión de realizará con usuario y contraseñaElse
    {
        $DeDominioRemoto=New-ObjectSystem.DirectoryServices.DirectoryEntry`$RutaProduccion,`$User,`$Password
    }
    # Obtenemos la hora de inicio del proceso$InicioProceso=Get-Date# Cargamos el PsSnapin de Exchange si es necesarioAdd-PsSnapinControled-PsSnapin`"Microsoft.Exchange.Management.PowerShell.Admin"# Iniciamos el progreso que se muestra en pantalla con una línea en blancoWrite-Host""# Iniciamos la indicación del contacto que se está borrando en este momento,# no produciendo un salto de línea después de escribir, lo que permitirá que# se ponga el número en su momento.Write-Host"Borrando el contacto Nº  "-NoNewline# Iniciamos el recuento de contactos borrados$Borrados=0# Borramos los contactos existentes en el laboratorioGet-MailContact-OrganizationalUnit"$LaboratoryOU"`-ResultSizeUnlimited-DomainController$LaboratoryDC`
        | ForEach{ `# Obtenemos los retrocesos que hay que hacer, en función de los dígitos# que tiene el número de contactos borrados hasta ahora en formato# #.##0$Retroceso="`b"* ("{0:N0}"-f$Borrados).Length
        # incrementamos el contador de contactos borrados$Borrados++# Mostramos número de contacto que se va a borrar, previo retroceso para# así borrar el anterior; por supuesto, se escribirá sin hacer salto de# línea al finalWrite-Host"$Retroceso$("{0:N0}" -f $Borrados)" -NoNewline
        # Eliminamos el contactoRemove-MailContact$_-Confirm:$false-DomainController$LaboratoryDC
    }
    Write-Host""# Obtenemos la hora de fin de borrado$FinBorrado=Get-Date# Obtenemos el tiempo que ha sido empleado en borrar los contactos$TiempoBorrado=New-TimeSpan-Start$InicioProceso-End$FinBorrado#Creamos un objeto DirectorySearcher$Buscador= [System.DirectoryServices.DirectorySearcher]$DeDominioRemoto# Montamos el filtro LDAP. El filtro hará que sólo se traigan los objetos de# tipo contacto, que tengan dirección de correo y que sean contactos de# Exchange$Filtro="(&(objectClass=Contact)(msExchRecipientDisplayType=*))"# Establecemos el filtro en el objeto DirectorySearcher$Buscador.Filter =$Filtro# Establecemos que la búsqueda será recursiva$Buscador.SearchScope ="Subtree"# Establecemos paginación para que así no se limiten los resultados# obtenidos$Buscador.PageSize =1000# Iniciamos el contador de contactos$Creados=0# Si la salida no debe ser detalladaIf($Detailed-eq$false)
    {
        # Iniciamos el progreso que se muestra en pantalla con una línea en# blancoWrite-Host""# Iniciamos la indicación del contacto que se está creando en este# momento, no produciendo un salto de línea después de escribir, lo que# permitirá que se ponga el número en su momento.Write-Host"Procesando el Contacto Nº  "-NoNewline
    }
    # Iniciamos la variable temporal que almacenará el valor de los atributos# personalizados como nula$Valor=$null# Iniciamos el contador de errores$Errores=0# Recorremos los objetos encontrados con la consultaForEach($objContactoIn$Buscador.FindAll())
    {
        # Incrementamos el contador de contactos$Creados++# Obtenemos el objeto DirectoryEntry del contacto$Contacto=`New-ObjectSystem.DirectoryServices.DirectoryEntry($objContacto.Path)

        # Obtenemos el alias como la parte de usuario de la dirección de correo$Alias= ([String]$Contacto.mail).Split("@")[0]

        # Si la salida debe ser detalladaIf($Detailed) 
        {    
            # Mostramos el número de contacto, su dirección de correo y su alias"$Creados - $($Contacto.Mail)`: $Alias"
        }
        # Si la salida no debe ser detalladaElse
        {
            # Obtenemos los retrocesos que hay que hacer, en función de los# dígitos que tiene el número de contactos creados hasta ahora en# formato #.##0$Retroceso="`b"* ("{0:N0}"-f ($Creados-1)).Length
            # Mostramos número de contacto que se va a borrar, previo retroceso# para así borrar el anterior; por supuesto, se escribirá sin hacer# salto de línea al finalWrite-Host"$Retroceso$("{0:N0}" -f $Creados)" -NoNewline
        }
        # Vaciamos el contenido de la variable temporal con la dirección SMTP# primaria$PrimarySmtpAddress=""# Obtenemos la dirección SMTP primaria$PrimarySmtpAddress=`Get-PrimarySmtpAddress-ProxyAddresses$Contacto.proxyAddresses
        # Creamos el contacto en el dominio de laboratorio. Para especificar la# OU en la que se crea, reemplazamos el nombre distinguido de la OU de# producción por el de la del laboratorio en la ruta del contenedor del# contacto en el dominio origen quitando ademas el protocolo y el# controlador de dominio ("LDAP://DC"); de esa manera conseguimos que el# contacto sea creado en la Sub-OU que corresponda en el laboratorio.# Además de ésto, pasamos el parámetro PrimarySmtpAddress para evitar# que se aplique la directiva de direcciones.$AccionError=$ErrorActionPreference$ErrorActionPreference="Stop"$Parametros= @{}
        $Parametros.Add("WindowsEmailAddress", "$($Contacto.Mail)")
        $Parametros.Add("EmailAddresses", $Contacto.proxyAddresses)
        $Parametros.Add("DomainController", $LaboratoryDC)
        # Agregamos los parámetros correspondientes a los atributos# personalizados que tengan valor. Para ello realizamos un bucle de 1 a# 15, pues esos son los atributos personalizados que existen1..15|Foreach{
            # Eliminamos la variable con el valor de CustomatributeN de una# ejecución anteriorRemove-Variable-NameValor-Force# Obtenemos el valor del atributo personalizado actual$Valor=Invoke-Expression"`$Contacto.extensionAttribute$_"# Si no es nulo ni cadena vacía agregamos el parámetro# correspondienteIf([String]::IsNullOrEmpty($Valor) -eq$false)
            {
                $Parametros.Add("CustomAttribute$_", $Valor)
            }
        }
        Try
        {
            # Creamos el contacto$NuevoContacto=New-MailContact-Alias$Alias`-OrganizationalUnit`
                            ( `
                                ( $Contacto.Parent -replace$ProductionOU,$LaboratoryOU) `-replace"LDAP://$ProductionDC/",""`
                            ) `
                            -DisplayName "$($Contacto.displayName)"`
                            -FirstName "$($Contacto.givenName)"`
                            -LastName "$($Contacto.sn)"`
                            -Name "$($Contacto.cn)"`
                            -PrimarySmtpAddress "$PrimarySmtpAddress"`
                            -ExternalEmailAddress "SMTP`:$PrimarySmtpAddress"`
                            -DomainController $LaboratoryDC# Agregamos el contacto como parámetro Identity a los parámetros que# pasaremos a Set-MailContact$Parametros.Add("Identity",$NuevoContacto)

            Try
            {
                # Ha habido éxito creando el contacto, ahora intentaremos# modificarle para establecer sus propiedades personalizadas, la# dirección de correo de Windows y las proxyAddressesSet-MailContact @Parametros
            }
            Catch
            {
                # Ha habido error modificando el contacto. Lo intentaremos de# nuevo, pero esta vez omitiremos agregar dirección de correo de# Windows por si fuese nula, así que usaremos Set-MailContact# para establecer las otras propiedades$Parametros.Remove("WindowsEmailAddress")
                Set-MailContact @Parametros
                # Obtenemos el objeto DirectoryEntry correspondiente al contacto$ContactoAD= [ADSI] `"LDAP://$LaboratoryDC/$($NuevoContacto.DistinguishedName)"Try
                {
                    # Intentamos establecer la dirección de correo de Windows$ContactoAD.Mail ="$($Contacto.Mail)"$ContactoAD.SetInfo()
                }
                # Si se produce error ignoramos la dirección de correo de# WindowsCatch
                {
                    $Errores++Continue
                }
            }
        }
        # Se ha producido error al intentar crear el contacto. Esto es debido# a que ya existe algún/os contacto/os que tiene la dirección de correo# del contacto nuevo como una de sus direcciones proxy. Aunque esto no# se debería producir, nos interesa que el laboratorio sea una copia del# entorno de producción, y por tanto queremos traernos estos errores# también. Lo que haremos será localizar los contacto que tengan la# dirección de correo entre sus direccines proxy, guardarnos en una# variable las direcciones proxy que tengan, vaciarles este atributo en# su objeto DirectoryEntry, dar de alta el nuevo contacto y restaurar# las direcciones proxyCatch
        {
            # Establecemos el valor que buscaremos en el filtro de# Get-MailContact. Este valor el el de la dirección de correo del# contacto que queremos crear$Filtro="SMTP:$PrimarySmtpAddress"# Obtenemos los contactos que contengan la dirección de correo entre# sus direcciones proxy$MailContactOld=Get-MailContact`-Filter {EmailAddresses -like$Filtro} `
                                        -ResultSize Unlimited `
                                        -DomainController $LaboratoryDC# Creamos dos diccionarios vacíos, uno para direcciones proxy y el# otro para target addresses$proxyAddressesBak= @{}
            $targetAddressBak= @{}
            # Recorremos loc contactos obtenidosForEach($MCIn$MailContactOld)
            {
                # Obtenemos el objeto DirectoryEntry del contacto actual$MailContactDE= [ADSI] `"LDAP://$LaboratoryDC/$($MC.DistinguishedName)"# Agregamos al diccionario de direcciones proxy el nombre# distinguido del contacto como clave y sus direcciones proxy# (convertido a array de cadenas) como valor. Para preservar los# espacios que pudiera haber contenidos en los elementos del# array, los sustituimos por un caracter no imprimible (ASCI 1);# al resultado lo convertimos en cadena: PowerShell cuando# convierte a cadena un array separa los elementos por un# espacio; como queremos obtener un array de cadenas, en las que# puede haber espacios dentro de ellas, es necesario cambiar los# espacios por otro caracter, pues los elementos los tendremos# que separar con Split, al que se pasa de separador el espacio,# lo que haría que se creasen más elementos de la cuenta si# hubiera un/os espacio/s en algún/os elemento/s. Por tanto lo# que hacemos es sustituir los espacios, convertir a cadena y# reconvertir a array usando el espacio como separador.                [Void]$ProxyAddressesBak.Add( `$MC.DistinguishedName, `
                            ([String]($MailContactDE.proxyAddresses -replace`"",[char]1)).Split(""))
                # Lo mismo que hicimos con las direcciones proxy lo hacemos con# targetAddress, sólo que esta vez el elemento loagregamos al# diccionario de targetaddress                [Void]$targetAddressBak.Add( `$MC.DistinguishedName, `
                            ([String]($MailContactDE.targetAddress -replace`"",[char]1)).Split(""))
                # Vaciamos los atributos de las direcciones proxy y# targetAddress$MailContactDE.proxyAddresses.Clear()
                $MailContactDE.targetAddress.Clear()
                # Validamos los cambios$MailContactDE.SetInfo()
            }
            # Creamos el nuevo contacto$NuevoContacto=New-MailContact`-Alias$Alias`-OrganizationalUnit`
                (`
                    ( $Contacto.Parent -replace$ProductionOU,$LaboratoryOU) `-replace"LDAP://$ProductionDC/",""`
                ) `
                -DisplayName "$($Contacto.displayName)"`
                -FirstName "$($Contacto.givenName)"`
                -LastName "$($Contacto.sn)"`
                -Name "$($Contacto.cn)"`
                -PrimarySmtpAddress "$PrimarySmtpAddress"`
                -ExternalEmailAddress "SMTP`:$PrimarySmtpAddress"`
                -DomainController $LaboratoryDC$Parametros.Add("Identity",$NuevoContacto)
            Try
            {
                # Una vez creado intentamos ponerle sus direcciones proxy, los# atributos personalizados y la dirección de correo de WindowsSet-MailContact @Parametros
            }
            Catch
            {
                # Si la dirección de correo de Windows provocó error,# modificamos el contacto sin establecerla$Parametros.Remove("WindowsEmailAddress")
                Set-MailContact @Parametros
                # Obtenemos el objeto DirectoryEntry del contacto$ContactoAD= [ADSI] `"LDAP://$LaboratoryDC/$($NuevoContacto.DistinguishedName)"Try
                {
                    # Intentamos establecer la dirección de correo de Windows$ContactoAD.Mail ="$($Contacto.Mail)"# validamos el cambio$ContactoAD.SetInfo()
                }
                # Si se produce error ignoramos la dirección de correo de# WindowsCatch
                {
                    $Errores++Continue
                }
            }
            # Ahora restauraremos las direcciones proxy y targetAddress de los# contactos a los que se las quitamosForEach($MCIn$MailContactOld)
            {
                # Obtenemos el objeto DirectoryEntry del contacto$MailContactDE= [ADSI] `"LDAP://$LaboratoryDC/$($MC.DistinguishedName)"# Restauramos las direcciones proxy$MailContactDE.proxyAddresses =`$proxyAddressesBak.Get_Item($MC.DistinguishedName)
                # Restauramos targetAddress$MailContactDE.targetAddress =`$targetAddressBak.Get_Item($MC.DistinguishedName)
                # Validamos el cambio$MailContactDE.SetInfo()
            }
        }
        # Restablecemos la acción predeterminada ante errores que no interrumpen$ErrorActionPreference=$AccionError
    }
    # Recogemos la hora de final$FinProceso=Get-Date# Obtenemos el tiempo empleado en crear los contactos$TiempoCreacion=New-TimeSpan-Start$FinBorrado-End$FinProceso# Obtenemos la duración del proceso completo$Duracion=New-TimeSpan-Start$InicioProceso-End$FinProceso# Mostramos el resumen, con la información de ejecución, el total de# contactos y el tiempo empleado por el proceso para su finalización, así # como el empleado borrando y creando los contactos"`r`n""OU de Origen                 `:  $ProductionOU""DC de Origen                 `:  $ProductionDC""OU de Destino                `:  $LaboratoryOU""DC de Destino                `:  $LaboratoryDC""Contactos Borrados           `:  $("{0:N0}" -f $Borrados)""Contactos Procesados         `:  $("{0:N0}" -f $Creados)""Contactos Creados            `:  $("{0:N0}" -f ($Creados - $Errores))""Contactos No Creados (Error) `:  $("{0:N0}" -f $Errores)""Hora de Inicio del Proceso   `:  $($InicioProceso.ToString())""Hora de Final del Proceso    `:  $($FinProceso.ToString())""Duración Borrado             `:  $($TiempoBorrado.ToString())""Duración Altas               `:  $($TiempoCreacion.ToString())""Tiempo de Proceso            `:  $($Duracion.ToString())"<#
    .SYNOPSIS
        Importa contactos de Exchange desde una organización a otra.

    .DESCRIPTION
        Este Cmdlet permite importar los contactos de una organización de
Exchange en otra, cuando no existe relación de confianza entre ambos dominios,
lo que impide usar Cmdlets de Exchange para acceder al Exchange de origen,
siendo necesario hacerlo vía LDAP. La importación la hace creando en el destino
los contactos con los mismos atributos esenciales: nombres, direcciones y
atributos personalizados.

Es ideal para poder importar a un entorno de laboratorio/preprodución los
contactos que tenemos en producción, para así poder, por ejemplo, desarrollar un
script de sincronización trabajando con casos lo más cercanos a la realidad de
nuestro entorno de producción.

El Cmdlet permite importar todos los contactos del dominio (poniendo como valor
del parámetro ProductionOU el nombre distinguido del dominio de origen) o sólo
los contactos contenidos en el subárbol que tiene como raíz determinada OU
(poniendo como valor del parámetro ProductionOU el nombre distinguido de dicha
OU de origen).

Los contactos son creados en la OU del dominio de destino especificado por el
parámetro LaboratoryOU, previo borrado de los que ya estuvieran ahí contenidos
(recordemos que el Cmdlet tiene como propósito el importar a laboratorio lo que
tengamos en producción. y por tanto es necesario borrar lo que haya en la OU de
destino).

El script requiere tener ya creado el subárbol de unidades organizativas en el
destino que sea homólogo al de origen, pues en la creación de del contacto se
usa el nombre distinguido del contacto de origen sustituyéndole la parte que es
igual al parámetro ProductionOU por la del parámetro LaboratoryOU. Veamos un
ejemplo:

ProductionOU : OU=Contactos,DC=tia,DC=org
LaboratoryOU : OU=Contactos,DC=tia,DC=lab
ContactoDN   : CN=Anselmo Nigote,OU=SOBRINA,OU=Contactos,DC=tia,DC=org

En este caso, el contacto nuevo se intentará crear en la OU
OU=SOBRINA,OU=Contactos,DC=tia,DC=lab y por tanto deberá existir previamente.

    .PARAMETER ProductionDC
        Nombre del controlador de dominio de producción al que se consultará.
Este parámetro admite el alias "DcP". 

    .PARAMETER LaboratoryDC
        Nombre del controlador de dominio de laboratorio sobre el que se operará
en el entorno de laboratorio. Este parámetro admite el alias "DcL". 

    .PARAMETER ProductionOU
        Nombre distinguido de la raíz del árbol, dentro del dominio de
producción, en la que se consultará para obtener los contactos. Este parámetro
admite el alias "OuP". 

    .PARAMETER LaboratoryOU
        Nombre distinguido de la raíz del árbol, dentro del dominio de
laboratorio, en la que se crearán los contactos. Este parámetro admite el alias
"OuL". 

    .PARAMETER User
        Nombre del usuario con el que se conectará al dominio de producción. Es
requerido si no se pasa el modificador -Anonymous; si el modificador -Anonymous
se pasa y también se pasa este parámetro, el nombre de usuario será ignorado.
Este parámetro admite el alias "U".

    .PARAMETER Password
        Contraseña del usuario con el que se conectará al dominio de producción.
Es requerido si no se pasa el modificador -Anonymous; si el modificador
-Anonymous se pasa y también se pasa este parámetro, la contraseña será
ignorada. Este parámetro admite el alias "C".

    .PARAMETER Anonymous
        Si se pasa este modificador, la conexión al dominio de producción se
realizará de forma anónima, ignorándose los parámetros User y PAssword si fuesen
pasados también. Este modificador admite el alias "A". 

    .PARAMETER Detailed
        Si se pasa este modificador, se mostrará información sobre los contactos
según se van creando. Este modificador admite el alias "D". 

    .EXAMPLE
        PS C:\> Import-Contacts -ProductionDC dc1.tia.org `
>> -LaboratoryDC dc1.tia.lab `
>> -ProductionOU "OU=Contactos,DC=tia,DC=org" `
>> -LaboratoryOU "OU=Contactos,DC=tia,DC=lab" `
>> -User bacterio `
>> -Password OfeliaFoca
>>

En este ejemplo Se importan los contactos del origen, que están en el subárbol
de producción cuyo nombre distinguido es OU=Contactos,DC=tia,DC=org, al
destino, ubicándolos en el subárbol del laboratorio cuyo nombre distinguido es
OU=Contactos,DC=tia,DC=lab. Se conectará al origen con el usuario "bacterio" y
la contraseña "OfeliaFoca".

    .EXAMPLE
        PS C:\> Import-Contacts -ProductionDC dc1.tia.org `
>> -LaboratoryDC dc1.tia.lab `
>> -ProductionOU "OU=Contactos,DC=tia,DC=org" `
>> -LaboratoryOU "OU=Contactos,DC=tia,DC=lab" `
>> -Anonymous
>>

En este ejemplo Se importan los contactos del origen, que están en el subárbol
de producción cuyo nombre distinguido es OU=Contactos,DC=tia,DC=org, al
destino, ubicándolos en el subárbol del laboratorio cuyo nombre distinguido es
OU=Contactos,DC=tia,DC=lab. Se conectará al origen de forma anónima.

    .INPUTS
        System.String

    .OUTPUTS
        System.String[]

    .NOTES
        Fernando Reyes López © 11/2011

    .LINK
        http://freyes.svetlian.com

    .LINK
        https://urpiano.wordpress.com
#>
}

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: