En algún punto trabajando con Exchange nos vamos a encontrar con que no queda alternativa que recurrir a la línea de comando, específicamente al Shell de Exchange.
El shell de Exchange funciona sobre Windows Powershell por lo que antes de entrar en este tema es recomendable estar familiarizado con los principales conceptos o al menos los que a mi entender pueden resultar de mayor utilidad.
Powershell es una interfaz de línea de comando integrada en el sistema operativo desde Windows 7 o en el caso de servidores Windows Server 2008 R2.
Este es un entorno basado en objetos, es decir que no trabajamos con texto sino que lo hacemos con objetos del .Net Framework.
En todos los casos la sintaxis utiliza un verbo o acción por un lado seguido por un guion y luego un sustantivo o sujeto. Cada cmdlet (comando en Powershell) realiza una tarea administrativa y es posible combinar múltiples comandos para llevar a cabo tareas complejas.
Por ejemplo para ver la lista de servicios de Windows podemos usar el cmdlet Get-Service.
Para definir el comportamiento de un cmdlet utilizamos parámetros, cada parámetro comienza con un guion y en caso de utilizar múltiples parámetros es necesario dejar un espacio entre medio. Para declarar variables usamos el signo de pesos ($) cosa que si armamos un script vamos a usar en múltiples ocasiones. Adicionalmente tenemos a disposición características programáticas y la posibilidad de utilizar loops y estructuras como foreach, If y where entre otras.
A lo largo de este artículo vamos a ver algunas de las interrogantes más comunes al trabajar con Powershell y cómo poner en práctica los conceptos. Para hacer el tema interesante te recomiendo ir abriendo Windows Powershell (powershell.exe) y probar los ejemplos planteados.
Para qué usar el PIPE (|) en Powershell?
Este es uno de los conceptos más importantes.
Al igual que en muchos otros tipos de shell, el pipe nos permite enviar la salida de un comando a otro. En el caso particular de Powershell en lugar de enviar la salida en texto plano se trabaja con objetos.
Por qué nos podría interesar que trabaje con objetos en lugar de texto plano?
Porque cuando pasamos un objeto lo estamos haciendo junto a todas sus propiedades en lugar de tener que trabajar con una salida de texto en “crudo”.
Dependiendo del idioma del teclado cómo realizar el pipe, lo más sencillo es recordar como hacerlo en ASCII: ALT + 124.
Dentro de una misma línea podemos utilizar varias veces el pipe y si bien lo podemos usar para modificar múltiples objetos en una sola línea, también lo podríamos utilizar para dar formato o filtrar la salida.
Formato de la salida en Powershell
Al ejecutar un cmdlet nos devuelve la salida en un formato predeterminado, esta salida la podemos adaptar a nuestros requerimientos.
Por ejemplo, para obtener un listado de los servicios:
Get-Service
Este cmdlet de forma predeterminada nos devuelve 3 columnas:
- Status
- Name
- DisplayName
Si solo quisiéramos obtener el estado y el nombre a mostrar podríamos hacer lo siguiente:
Get-Service | Format-Table DisplayName, Status
De este modo solo traería 2 columnas. Esto sería en formato tabla (Format-Table o FT).
De forma predeterminada el formato tabla nos devuelve pocas propiedades, claro que podríamos especificar que propiedades queremos, pero cómo saber que propiedades tiene un objeto?
Siempre podemos recurrir a technet o al sistema de ayuda entre otras opciones, pero lo más rápido en general es utilizar el formato en lista, por ejemplo:
Get-Service | Format-List
Esto devolvería la salida en formato lista (alias: FL) mostrando más propiedades que en el caso de formato tabla, entonces podríamos ver el nombre de cada propiedad y posteriormente especificarla a continuación del tipo de formato, por ejemplo para ver el nombre a mostrar, estado y servicios de los que depende podríamos hacer:
Get-Service | Format-Table DisplayName, Status, DependentServices
En este caso podríamos utilizar Format-Table o Format-List dependiendo de la salida deseada.
Si bien esto nos permite dar formato cuando trabajamos de forma interactiva, en algún caso podríamos querer exportar la salida a un CSV o HTML por ejemplo, en estos casos no podemos utilizar ni Format-Table ni Format-List, acá sería necesario utilizar Select-Object (alias: Select):
Get-Service | Select DisplayName, Status
Esto devolvería una salida similar a la de formato tabla, la diferencia es que nos habilitaría a complementar con un cmdlet de exportación, por ejemplo:
Get-Service | Select DisplayName, Status | Export-CSV -Path c:\temp\servicios.csv
En este caso el CSV generado incluiría un encabezado seguramente no deseado (#TYPE …):
Para que este encabezado no aparezca podemos usar Export-CSV incluyendo el parámetro NoTypeInformation:
Get-Service | Select DisplayName, Status | Export-CSV -Path c:\temp\servicios.csv -NoTypeInformation
En este caso quedaría del siguiente modo:
Filtrado de salida
El cmdlet Where-Object nos sirve para filtrar los datos devueltos por otros cmdlets. Lo podemos encontrar tanto como Where-Object o incluso como “where” o “?”.
El código incluido dentro del “where” se ubica entre paréntesis de tipo llave: “{}” y este código es evaluado para cada objeto que pasa a través del pipeline. El resultado de la expresión puede ser verdadero (true) o falso (false). Si se evalúa “true” el objeto es devuelto, en caso contrario es ignorado.
Por ejemplo, si ejecutamos en Powershell “Get-Service”, este devuelve todos los servicios del equipo. Si solo nos interesan los servicios que tengan la palabra “time” en el nombre podemos filtrar del siguiente modo:
Get-Service | Where {$_.displayname -like “*time*”}
Si traducimos al español la sentencia diríamos:
“Traer servicios donde el nombre a mostrar tenga la palabra time en cualquier parte”.
En este caso también estamos utilizando el operador “like”.
El operador “like” nos sirve para utilizar comodines como el “*”. Si por ejemplo quisiera todo lo que empiece por time utilizaría “time*”, si quisiera todo lo que finalice con time “*time” y en el caso del ejemplo anterior “*time*”.
Del mismo modo podríamos utilizar a la inversa con “notlike”, en este caso por ejemplo la línea anterior:
Get-Service | Where {$_.displayname -notlike “*time*”}
Se traduciría en:
“Traer servicios donde el nombre a mostrar no tenga la palabra time en ninguna parte”.
Qué es “$_” ?
En el caso del ejemplo anterior el “$_” representa el servicio que en ese momento está pasando por el pipe.
Si queremos acceder a alguna propiedad del objeto que está pasando por el pipe podemos utilizar “.”, entonces si un objeto que pasa por el pipe tiene una propiedad “name” podríamos utilizar “$_.name”.
En un escenario simple podríamos hacer lo siguiente:
“1”,”2″,”3″ | foreach {$_}
Esto nos devuelve en una línea el “1” en otra el “2” y en otra el “3”.
En definitiva “$_” lo vamos a utilizar para acceder al objeto que está siendo pasado por el pipe.
Estructura Foreach
El foreach nos permite procesar objetos dentro de una colección, por ejemplo:
foreach ($usuario in $usuarios)
En este caso en particular la colección estaría almacenada en $usuarios. Esta colección de usuarios puede ser generada de varias formas por ejemplo importando un listado en CSV.
La variable $usuario en este caso hace referencia a cada línea de un CSV y se pudo haber llamado de otro modo, por ejemplo:
foreach ($linea in $usuarios)
La idea es que para cada línea dentro de la colección de objetos almacenada en $usuarios se ejecute el bloque incluido entre llaves “{}”. En la siguiente demo se ve cómo usar el foreach dentro de un script de alta masiva de objetos en Exchange.
Uso de variables en Powershell
Las variables en Powershell comienzan por “$”. Por ejemplo: $variable
Volviendo al ejemplo de servicios, podríamos almacenar en $servicios todos los servicios y en otra línea filtrar la salida de $servicios utilizando Where:
$servicios = Get-Service
$servicios | Where {$_.displayname -like “*time*”}
O directamente podríamos almacenar en $servicios la salida ya filtrada:
$servicios = Get-Service | Where {$_.displayname -like “*time*”}
El nombre de la variable es opcional mientras no coincida con algo reservado para el sistema.
Operadores comunes en Powershell
En Powershell tenemos operadores aritméticos, lógicos, de comparación y asignación.
Veamos algunos de los que vamos a utilizar con más frecuencia:
“=” Este sería un operador de asignación y lo que estamos diciendo es que le asigne un determinado valor a una variable, en este caso almacenamos el nombre de un servidor en $servidor y posteriormente ejecutamos la variable para ver qué devuelve:
$servidor = “servidor1”
“eq” (equal) Esto se leería “igual” pero no es de asignación sino que de comparación, por ejemplo:
“prueba” -eq “prueba”
Esto devuelve “true” porque la palabra “prueba” es igual a “prueba”.
“prueba -eq “prueba1”
Esto devuelve “false” porque “prueba” no es igual a “prueba1”.
“ne” (not equal) Por el contrario podríamos utilizar “no igual”:
“prueba” -ne “prueba”
Esto devuelve “false” porque “prueba” es igual a “prueba”, en cambio:
“prueba” -ne “prueba1”
Devuelve “true” porque “prueba” no es igual a “prueba1”.
Otro operador de comparación muy utilizado es “like” y este nos sirve para utilizar comodines en busca de una coincidencia:
“prueba” -like “*ueba”
Esto devuelve “true”, por el contrario con “notlike”:
“prueba” -notlike “*ueba”
Devuelve “false”.
En adición tenemos operadores lógicos como “And” y “Not”, por ejemplo para traer todos los servicios que se encuentren iniciados y en el nombre para mostrar incluyan la palabra “time” podríamos ejecutar:
Get-Service | Where {$_.displayname –like “*time*” –and $_.status –eq “Running”}
Sobre las comillas en Powershell
El tema de las comillas en muchos casos se puede prestar a la confusión. En este sentido podemos utilizar comillas dobles (“ ”) o simples (‘ ‘).
Cuando utilizamos valores con espacio, por ejemplo Nombre Apellido, estos deben encontrarse entre comillas en todos los casos, estas pueden ser dobles o simples.
Veamos algunos ejemplos con y sin comillas:
$nombre = “Juan”
$apellido = “Perez”
$nombrecompleto = $nombre $apellido
En este caso $nombrecompleto da error, probemos nuevamente pero utilizando comillas simples:
$nombrecompleto = ‘$nombre $apellido’
En este caso si ejecutamos $nombrecompleto vemos que devuelve literalmente: $nombre $apellido
El objetivo sería que se muestre el valor de la variable y no la variable de forma literal, por lo que en lugar de comillas simples tendríamos que usar dobles:
$nombrecompleto = “$nombre $apellido”
Ahora al ejecutar $nombrecompleto se devuelve correctamente: Juan Perez
Ayuda en Powershell
Por último, saber manejarse con el sistema de ayuda de Powershell es muy importante, en lugar de ir a un buscador a ver cómo usar un determinado comando podemos acceder a esta misma información sin salir del shell.
Para esto usamos el cmdlet Get-Help o su alias “Help” seguido por el cmdlet en cuestión.
Tenemos varios parámetros para especificar el nivel de ayuda que deseamos, uno que utilizo con frecuencia es el de “Examples”, este parámetro en lugar de devolver toda la ayuda del cmdlet devuelve ejemplos de casos de uso, muchas veces con esto es suficiente.
Get-Help Get-Service -Examples
En adición podemos obtener ayuda detallada o completa utilizando “Detailed” o “Full” y en caso de que no exista localmente la información, con el parámetro “Online” nos lleva directamente a la página oficial con el detalle del comando.
Con esto llegamos al final del artículo, por más información teórica y práctica sobre el shell de Exchange ver el siguiente recurso para miembros VIP del sitio (videos de entrenamiento):
Soledad Molina says
Muchas gracias, utilizo esta herramienta de manera empírica. A partir de ahora ya es otra cosa.
Jorge Valenzuela Correa says
Buen resumen Daniel.
Solo aportar la mejor innovación, desde mi humilde punto de vista, que incluyen las últimas versiones de Powershell, y es la posibilidad de redirigir las salidas de los comandos, a un contenedor gráfico, que facilitará enormemente el analisis de los resultados, y con solo agregarles al final lo siguiente:
| out-gridview
Ejemplo: get-process | out-gridview
Para hacer tracking de los correos es genial.
Daniel Núñez Banega says
Sin dudas muy práctico el out-gridview para trabajar de forma interactiva. Gracias por el aporte Jorge
Iván says
Muy útil!
Buen trabajo y muchas gracias.
enrique says
Muchas gracias. Muy útil.