Buscar pertenencia de grupos en un ciertos usuarios

Entre los miles (?) de pedidos que recibo diariamente, me tocó algo que crei interesante compartir.

Me pidieron un reporte que incluyera los grupos a los que pertenecen una lista de usuarios. OBVIAMENTE no lo iba a hacer a mano, asi que…

Powershell!

Los puntos a tener en cuenta son los siguientes:

  1. c:\datos es la ruta de donde, por defecto, se toma el archivo de usuarios y se guarda el reporte.
  2. lista de usuarios.txt debe contener la lista de usuarios.
  3. En caso de que se quieran seleccionar usuarios con algun filtro, se puede usar get-aduser -filter {enabled -eq $true} (Para usuarios habilitados, por ejemplo)
  4. Devuelve un csv con el nombre “Reporte Usuarios”.
## Agregar el modulo de Active Directory.
import-module activedirectory
$Final=@()
## Apunta a la ruta donde esta el archivo
$ruta="c:\datos"
## Apunta al archivo
$archivo="lista de usuarios.txt"
## Obtiene una lista de todos los usuarios que esten habiliados.
$usuariostotal= get-content $ruta\$archivo
foreach ($usuario in $usuariostotal){
## Guarda el nombre del usuario.
$nombre=$usuario.name
## Guarda el username.
$user=$usuario.samaccountname
$Completo=@()
$Grupos="ERROR DESCONOCIDO"
$primero=$true
$agregar=""
## Busca los grupos a los que pertenece el usuario.
$Completo=get-ADPrincipalGroupMembership $user | select-object name
$cantidad=$Completo.count
## Por cada grupo hace una iteracion.
for($a=0;$a -lt ($cantidad);$a++){
if ($primero){
$primero=$false
## Toma el nombre del grupo y lo pasa a una variable.
$agregar=$Completo[$a].name
## Agrega la variable anterior a $grupos para dejarlo todo en una sola variable.
$Grupos="$agregar"
}
else{
$agregar=$Completo[$a].name
$Grupos+=", $agregar"
}
}
$Final+=$usuario | select-object @{Expression={$nombre};Label="Nombre"},@{Expression={$user};Label="Username"}, @{expression={$Grupos};Label="Grupos"},@{expression={$cantidad};Label="Cantidad"}
}
## Exporta el reporte a un CSV
$Final | export-csv $ruta\"ReporteUsuarios.csv"

Esto es todo por hoy!



Como averiguar los administradores locales de los servidores

En este glorioso día (?) me pidieron de superurgencia un reporte que contuviera todos los administradores locales de los servidores que tenemos en uno de nuestros sitios.
Haciendo cuentas en el aire, me di cuenta de que eran mas de 60!

Y ahora… Quien podrá ayudarme? 🙁

OBVIAMENTE POWERSHELL!

Los puntos a tener en cuenta son los siguientes:

  1. $mascara debe ser cambiado a la subred que se desee (por ej, 172.17.*)
  2. Devuelve un csv con el nombre “Reporte admin” y la mascara de subred que se utilice para buscar.
import-module activedirectory
$ruta=get-location
$prueba=@()
$completo=0
## la subred que se quiere buscar
$mascara="172.16.*"
cd "AD:\"
$equipos= Get-ADComputer -Filter {OperatingSystem -like "*server*"} -Properties ipv4address, CanonicalName, operatingsystem | Where-Object {$_.ipv4address -like $mascara}
$total=$equipos.count
foreach ($compu in $equipos){
$nombre=$compu.name
$laip=$compu.ipv4address
$canonico=$compu.canonicalname
$OS=$compu.operatingsystem
$online=$false
$idioma=" "
$admicompleto=@()
$administradores="ERROR DESCONOCIDO"
$sacoadmin=$false
$adminroto=$false
$primero=$true
$agregar=" "
$online = Test-Connection $nombre -Quiet -count 1
if ($online){
## Esta online
$coso=get-wmiobject -ComputerName $nombre win32_operatingsystem
if(!$?){
$idioma="999999"
}else{
$idioma=$coso.oslanguage
}
Switch ($idioma){
"3082" {
#Español Int
$admicompleto= Invoke-Command -ComputerName $nombre -ScriptBlock {net localgroup administradores}
if($?){
$sacoadmin=$true
}else{
$adminroto=$true
}
break
}
"1033" {
#Ingles US
$admicompleto= Invoke-Command -ComputerName $nombre -ScriptBlock {net localgroup administrators}
if($?){
$sacoadmin=$true
}else{
$adminroto=$true
}
break
}
"1046" {
#Portugues bra
$admicompleto= Invoke-Command -ComputerName $nombre -ScriptBlock {net localgroup administradores}
if($?){
$sacoadmin=$true
}else{
$adminroto=$true
}
break
}
"999999" {
# Si dio error en el get idioma
$administradores="ERROR DE WMI"
$sacoadmin=$false
break
}
Default {
# No se encuentra el codigo de idioma
$administradores="NO SE PUDO RESOLVER EL CODIGO $idioma"
$sacoadmin=$false
break
}
}
if($sacoadmin){
## Ejecuto bien el proceso de admin
for($a=6;$a -lt ($admicompleto.count -2);$a++){
if ($primero){
$primero=$false
$agregar=$admicompleto[$a]
$administradores="$agregar"
}
else{
$agregar=$admicompleto[$a]
$administradores+=", $agregar"
}
}
}
if($adminroto){
##no ejecuto el proceso de admin por un error
$administradores="ERROR DE EJECUCION REMOTA"
}
}else{
## No esta online
$idioma="NO DISPONIBLE"
$administradores="NO DISPONIBLE"
}
$PRUEBA+=$compu | select-object @{Expression={$nombre};Label="Nombre"}, @{expression={$online};label="Online"}, @{expression={$laip};label="IP"}, @{expression={$OS};label="OS"}, @{expression={$canonico};label="OU"}, @{Expression={$idioma};Label="Idioma"},@{expression={$sacoadmin};Label="ProcesoAdmins"} ,@{expression={$administradores};Label="Administradores"}
}
cd $ruta
$archivo="./reporte admin "+$mascara.Replace('*','x')+".csv"
$prueba | export-csv $archivo

El resultado no fue 100% exitoso, pero de 60+ servidores, quedaron unos 5-6 en los que tuve que entrar a revisar manualmente. Nada mal, eh?

Prometo mas actualizaciones en las proximas semanas, tengo un par de scripts armados que no tuve tiempo de compartir.

Eso es todo por hoy!



Como migrar un SMTP Relay en Exchange 2013

Como les habré comentado a algunos, estamos en plena migración de sistema de correos (Exchange 2007 a 2013), y como toda migración siempre hay cosas nuevas que probar y aprender 😀

Hoy me toco migrar un Receive Connector que usamos de SMTP Relay (uno en el ambiente viejo) y configurarlo balanceado entre 4 servidores (todos en el ambiente nuevo) Para esto tenía que copiar a mano unos 130 registros. 130 registros. A 4 servidores. NO.

Primero busque una manera rápida de exportar la lista de ips que estaban habilitadas.

SERVIDORBASE es el nombre del server que tiene el Receive Connector del que vamos a copiar la lista de ips.
RC-A-COPIAR es el nombre del Receive Connector del que vamos a copiar esta lista.

$ips=(Get-ReceiveConnector "SERVIDORBASE\RC-A-COPIAR").remoteipranges
view raw Obtener las ips.ps1 hosted with ❤ by GitHub

Perfecto! Con eso tenemos una array de ips (y rangos de ips, si es que usamos) completamente importables. (Cabe aclarar que lo probé tanto desde un Receive Connector armado en 2007 como en uno armado en 2013. No sé si habrá algún cambio de estructura en cuanto a tipo de dato, pero exportándolo así quedan igualitos)

El paso siguiente fue un poquito más largo. Tenía que crear los RC, configurarlos para hacer Relay y agregarles las ips que ya había exportado.

SERVIDORTARGET es el nombre del server en donde vamos a configurar el RC nuevo.

New-ReceiveConnector -Server SERVIDORTARGET -name "Relay SERVIDORTARGET" -Usage custom -AuthMechanism externalauthoritative -PermissionGroups ExchangeServers -Bindings 0.0.0.0:25 -TransportRole FrontEndTransport -RemoteIPRanges $ips
view raw Crear RC nuevos.ps1 hosted with ❤ by GitHub

Pero momento, justo antes de empezar a crear los RC nuevos, se me ocurrió revisar todos los RC que ya existían… Menos mal, porque de 4 servidores, dos ya tenían creados un RC destinado a Relay.
(Asumo que todos son ordenados y que nombran adecuadamente los RC. Si no es así, ordénenlos, no sean giles)

Get-ReceiveConnector | where {$_.name -like "*relay*"}
Oops
Oops

Bien, vamos por partes entonces. Primero usé el código que puse antes (Crear RC nuevos) y armé los dos que no existen. Listo? Bueno, ahora a modificar los que me faltan.
(En mi caso, quería modificar todos los RC cuyo nombre empezara con Relay. Si tuviera que modificar solo un RC lo especificaría en la primer parte del código que pongo más abajo)

Get-ReceiveConnector | where {$_.name -like "relay*"} | Set-ReceiveConnector -RemoteIPRanges $ips

Listo!

Si necesitan verificar que haya quedado bien se pueden ver los datos de cada RC de manera sencilla, como test rápido vamos a revisar que tengamos la misma cantidad de ips habilitadas en todos los RC cuyo nombre comience con Relay.

Get-ReceiveConnector | where {$_.name -like "relay*"} | select name, @{Expression={$_.remoteipranges.count};Label="cantidad"}
view raw Verificar RCs.ps1 hosted with ❤ by GitHub
Todos

Con esto termina el post de hoy. Hasta la próxima!



Managing Office 365 Identities and Requirements Aprobado! :D

Como les comente en el ultimo post, me anote para dar el primer examen de office 365, y lo aprobe!
(En realidad lo rendi hace dos semanas, pero recien el viernes me llego un correo de Microsoft avisando que estaba todo bien, y el fin de semana estuve muuuuy ocupado, jeje)

Proximo paso: 70-410, 70-411 y 70-412!

Hasta la proxima!





Vouchers para examenes de Microsoft

Apurense antes de que se agoten!

Microsoft esta regalando vouchers para 4 examenes (71-532, 70-533, 70-346 y 70-347), y lo unico que tienen que hacer es entrar ACA y llenar sus datos.

Que ganas rindiendo estos examenes?

Con el 71-532 y el 70-533 podes certificarte como Microsoft Certified Specialist en Azure
Con el 70-346 y el 70-347 podes certificarte como Microsoft Certified Solutions Associate en Office 365

Tengan en cuenta que esta oferta es valida hasta que se agoten los vouchers, y que estos vouchers deben ser usados antes del 31 de diciembre del 2014. Para usarlos, tienen que entrar a Prometric y buscar una fecha y lugar donde rendir.
No se cuelguen, yo ya tengo fecha para el 70-346 😀

Si te sirvio o conoces alguien que le pueda servir, pasalo! Nunca esta de mas estudiar un poco y aprender tecnologias nuevas.

Hasta la proxima





Chequear version de windows

Hoy me toco meterle mano al bisabuelo de Powershell: Batch.

Me pidieron que un script viejo no se ejecute en versiones Server de windows, y como no tengo manera de evitar la ejecución, tuve que volver a hacer magia.

Lo que hace este pequeño pedazo de código es chequear la versión de windows, verificar si es una versión “normal” de windows y si es así, vuelve a la etiqueta SIGUE. Simple y sencillo.

:CHEQUEASERVER
REM Se fija que sea windows XP, Vista, 7 u 8.
REM Si no es, no instala
systeminfo | find "Microsoft Windows" > %TEMP%\osname.txt
FOR /F "usebackq delims=: tokens=2" %%i IN (%TEMP%\osname.txt) DO set vers=%%i
echo %vers% | find "Windows 7" > nul
if %ERRORLEVEL% == 0 goto SIGUE
echo %vers% | find "Windows Vista" > nul
if %ERRORLEVEL% == 0 goto SIGUE
echo %vers% | find "Windows XP" > nul
if %ERRORLEVEL% == 0 goto SIGUE
echo %vers% | find "Windows 8" > nul
if %ERRORLEVEL% == 0 goto SIGUE
REM Si llego hasta aca abajo, no es
REM win xp, vista, 7 ni 8. Por descarte
REM es windows server, y no se instala.
:FIN
view raw CHEQUEASERVER.bat hosted with ❤ by GitHub


Limpiapendrives, o como empeze a scriptear.

Hoy comparto mi primer script, de cuando recién arrancaba.
El 11 de julio del 2011, mi hermana vino a casa con un pendrive infectado con un virus que ocultaba todos los archivos y creaba accesos directos que apuntaban a un .vbs que se copiaba a los demás discos.
Básicamente lo que hace es sacarle a todos los archivos del directorio y subcarpetas donde se corre los atributos Hidden, System y Read-Only.

Me había olvidado de este pequeño paso, pero hoy me lleve una sorpresita, ya que me entere que todo el curso de mi hermana (y varias personas mas) lo usan casi a diario!

Pueden bajar el .bat de aca mismo (o para los mas curiosos, acá abajo les va la transcripción)

@echo off
echo Este ejecutable te devuelve todo lo que tenias en el pendrive.
echo Acordate de borrar todos los accesos directos y los .EXE que estan aca.
attrib /d /s -r -h -s *.*
echo.
echo.
echo Listo!
echo.
echo.
echo.
echo Creado por Lucas Camilo
echo.
echo.
pause
exit
view raw LimpiaPendrives.bat hosted with ❤ by GitHub

Gracias Clari por la inspiración para un nuevo post! 🙂



Feliz dia del sysadmin!

Hoy, como todos los ultimos viernes de julio, es el dia del sysadmin.
Vayan a abrazar a su administrador de sistemas amigo, haganle un regalito, aunque sea un mail para saludarlos, un poco de reconocimiento cada tanto no viene mal 🙂

Y para mis compañeros, sumense hoy a la AdminFest, mas abajo les dejo el mapita de como llegar.

 





Ver usuarios bloqueados

Alguna vez les paso que un usuario de Active Directory se les bloqueo varias veces una misma semana? Y un mismo día?
Bueno, a no preocuparse, con este pequeño script vamos a poder ver en donde se produce ese bloqueo o intento fallido de login para poder resolverlo fácilmente.

Antes que nada, necesitamos tener instaladas las herramientas de administración remota (también llamado RSAT).
Podemos bajarlas desde estos links:
Windows 7 x86
Windows 7 x64

Una vez instalados, solo queda abrir una ventana de Powershell y ejecutar el siguiente script.

function show-progressbar([int]$actual,[int]$completo,[string]$status,[string]$Activity)
{
$porcentaje=($actual/$completo)*100
if (!$status){
$status="Buscando datos $actual de $completo"
}
if (!$Activity){
$Activity="Obteniendo Resultados"
}
Write-Progress -Activity $Activity -status $status -percentComplete $porcentaje
}
###############################################################
# Ya explique esta funcion en este post #
# https://powershelleando.com.ar/2014/05/30/show-progressbar/ #
###############################################################
import-module activedirectory
$logcompleto=@()
$logdcs=@()
$cuentadc=1
# crea una carpeta "Eventos". Si ya existe no hace nada.
New-Item -Name eventos -Type directory -Force
# Levanta todos los DCs
$dcs=Get-ADDomainController -Filter *
$dctotal=$dcs.count
foreach ($dc in $dcs){
$nombredc=$dc.name
# Variable de ayuda para filtrar cuando no hay eventos
$sineventos=$false
$cuentaeventos=0
# Variable de ayuda para filtrar eventos de no repeticion
$mostrar=$true
$tipo=" "
# Revisando Domain Controller $nombredc - Chequeando disponibilidad.
$Status="Revisando Domain Controller $nombredc ($cuentadc de $dctotal)- Chequeando disponibilidad."
show-progressbar $cuentadc $dctotal $status
$online = Test-Connection $nombredc -Quiet -count 1
if ($online){
# Revisando Domain Controller $nombredc - Recuperando eventos.
$Status="Revisando Domain Controller $nombredc ($cuentadc de $dctotal) - Recuperando eventos."
show-progressbar $cuentadc $dctotal $status
# Get-WinEvent busca en el log de eventos, usamos -FilterXPath
#para consultar por eventos especificos, en este caso, indicamos
#los dos EventIDs que nos pueden resultar interesantes.
# EventID=4740 es el evento de bloqueo de usuario
# EventID=4625 es el evento de intento fallido de login
$logs=(Invoke-Command -computername $nombredc -ScriptBlock {Get-WinEvent -FilterXml "<QueryList><Query Path='Security'><Select Path='Security'>*[System[(EventID=4740 or EventID=4625)]]</Select></Query></QueryList>" -ErrorAction silentlyContinue} )
if ($logs -eq $null){
# Marca que no hay eventos para acomodar
$sineventos=$true
$eventostotal=0
}
if (!$sineventos){
$eventostotal=$logs.count
# Revisa evento por evento
foreach ($evento in $logs){
$cuentaeventos+=1
# Revisando Domain Controller $nombredc - cantidad de eventos
$Status="Revisando Domain Controller $nombredc ($cuentadc de $dctotal) - Evento numero $cuentaeventos de $eventostotal"
show-progressbar $cuentadc $dctotal $status
# mensaje tiene la variable message del evento
$mensaje=$evento.message
$hora=$evento.TimeCreated.ToShortTimeString()
$fecha=$evento.TimeCreated.ToShortDateString()
$id=$evento.ID
$datos=$mensaje.Split("`n")
Switch ($id)
{
4740{
## CASE 4740 - Bloqueo de un usuario
# datos[10] tiene la linea del usuario
$usuario=(($datos[10].split("`t"))[3])
# datos[13] tiene la linea del equipo que genero el evento
$Maquina=(($datos[13].split("`t"))[2])
$tipo="Bloqueo"
## FIN CASE 4740
}
4625{
## CASE 4625 - Intento de login fallido
## (chequear por si es un error)
if ($evento.Providername -eq "Microsoft-Windows-Security-Auditing"){
# datos[12] tiene la linea del usuario
$usuario=(($datos[12].split("`t"))[3])
# datos[25] tiene la linea del equipo que
#genero el evento
$Maquina=(($datos[25].split("`t"))[2])
$tipo="Intento de Login"
}
else{
$mostrar=$false
}
## FIN CASE 4625
}
default{
## Hubo un error.
$usuario="ERROR"
$maquina="ERROR"
$tipo="ERROR"
}
}
if ($mostrar){
$logcompleto+= $usuario | select-object @{Expression={$usuario};Label="Usuario"},@{Expression={$maquina};Label="Equipo"},@{Expression={$tipo};Label="Evento"},@{Expression={$fecha};Label="Fecha"},@{Expression={$Hora};Label="Hora"},@{Expression={$nombredc};Label="DC"}
}
}
}
}
else{
# Si el domain controller no esta disponible
write-host "El domain controller $nombredc esta OFFLINE" -backgroundcolor "red" -ForegroundColor black
}
$logDCS+=$cuentadc|select-object @{Expression={$nombredc};Label="DC"}, @{Expression={$online};Label="Online"}, @{Expression={$cuentaeventos};Label="Eventos"}
$cuentadc+=1
}
# Exporta el log de los usuarios bloqueados a CSV
$logcompleto| export-csv ./eventos/Log_Bloqueados.csv
# Exporta el log de estado de los DCs a CSV
$logdcs | export-csv ./eventos/log_dcs.csv
##Fin

Esto fue todo por hoy, si tienen alguna duda, comentario o sugerencia pueden escribirme mas abajo.

Hasta la próxima!



Show-ProgressBar

Hoy voy a mostrar una pequeña función que suelo usar cuando corro algún ciclo largo, mas que nada para poder seguirlo paso a paso mientras se ejecuta.

Los únicos datos que si o si hay que pasarle son los números actual y el total de registros del ciclo que se esta recorriendo.

function show-progressbar([int]$actual,[int]$completo,[string]$status,[string]$Activity)
{
$porcentaje=($actual/$completo)*100
# Si no le pasamos un parametro $Status, pone uno generico
if (!$status){
$status="Buscando datos $actual de $completo"
}
# Si no le pasamos un parametro $Activity, pone uno generico
if (!$Activity){
$Activity="Obteniendo Resultados"
}
Write-Progress -Activity $Activity -status $status -percentComplete $porcentaje
}
view raw Show-ProgressBar.ps1 hosted with ❤ by GitHub

En los proximos scripts voy a usar esta pequeña funcion, asi que preferia presentarla primero antes de marear a algun colgado.

Hasta la próxima!