Encontrar duplicados SID en Active Directory en Windows

Debemos conectar con el controlador de dominio que tiene el rol RID master y ejecutar cmd como administrador:

C:\Windows\system32>ntdsutil

ntdsutil: security account management

Mantenimiento de la cuenta de seguridad: connect to server DC1

Mantenimiento de la cuenta de seguridad: check duplicate sid

................

La comprobación de SID duplicados se ha completado correctamente. Compruebe si hay

duplicados en dupsid.log

Mantenimiento de la cuenta de seguridad: quit

ntdsutil: quit

Registro de Creación de Usuarios en el Directorio Activo

Cuando el Directorio Activo es administrado por un grupo de personas, y necesitamos saber quien ha realizado o realiza alguna tarea concreta, este script nos ayuda a mantener un registro de quien/quienes crean usuarios en el Directorio Activo de nuestro dominio.

Consideraciones para la ejecución del script:

  1. Habilitar auditoria en los controladores de dominio
  2.  El evento que registra la creación de usuarios es el 4720 de la parte de seguridad del visor de eventos.
  3. Ejecutar el script en los controladores de dominio si no se tiene habilitado WMI en dichos DC.
  4. El script registra todos los usuarios que se han creado en el dia anterior.
  5. El script registra el objeto usuario que se ha creado, la fecha en la que se ha creado y el objeto que crea dicha cuenta de usuario.

Script de Powershell:

param( [switch]$LastLogonOnly,[switch]$OuOnly)
############## Find EventID 4720 with user's requesting Kerberos TGT, skipping Exchange Health Mailbox request and extracting Users/Client names,IP Addresses ####

Set-ExecutionPolicy Unrestricted -Force

#Create directorys if not exists
New-Item -ItemType Directory -Force -Path "C:\scripts" | Out-Null

New-Item -ItemType Directory -Force -Path "C:\scripts\usuariosCreados" | Out-Null

Remove-Item "C:\scripts\usuariosCreados\usuariosCreados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -Force -ErrorAction SilentlyContinue

$Domain = (Get-WmiObject Win32_Computersystem).domain

$read_log={
Param ($OuOnly,$Domain) ## Define parameter to pass maxevent to scripblock
$EventInfo=Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4720} | where {$_.Message -notmatch "SM_" } | where { $_.Message -notmatch "\$" } |
select Message, TimeCreated , MachineName

$EventInfo | foreach {
############# Find the User account in AD and if not found, throw and exception ###########
$Full_User_Property=0
Try ## Need Try statement to test and surpress error
{
$Full_User_Property = (Get-AdUser $_.usuario -Properties *)
$_."Localizacion" = $Full_User_Property.CanonicalName.TrimStart($Domain).SubString(1)
}
catch
{ } ## The $_."User Location" is not passed to catch statement thus needing another below statement to set value"

Return $_
}
}

########### Job starts to query replica domain controllers #############
$result=@()
$RemoteJob=@()

#Importa modulo de AD
Import-Module ActiveDirectory
$date= ((Get-Date).AddDays(-1))
$dateString = $date.ToLongDateString()

## Make array of remote jobs

$DomainControllers = (Get-ADDomainController -Filter { isGlobalCatalog -eq $true -or isGlobalCatalog -eq $false}).Name

############### Start the Local Job and Remote Job to find the event id ################
$LocalJobExists=0
If ($DomainControllers -contains $(hostname)) ## Check if the computer running the script is Domain Controller itself
{
$LocalJob = Start-Job -scriptblock $read_log -ArgumentList $OuOnly,$Domain;$LocalJobExists=1 ## If so, start job to query local domain controller
}

$DomainControllers | where {$_ -ne $(hostname)} | foreach {
## Start remote jobs on each other domain controllers
$RemoteJob+= Invoke-Command -ComputerName $_ -ScriptBlock $read_log -ArgumentList $OuOnly,$Domain -AsJob -ErrorAction SilentlyContinue
}

If ($LocalJobExists)
{
$result = $LocalJob | Wait-Job | Receive-Job; Remove-Job $LocalJob ## If the computer running the script is not a domain controller(may be RSAT installed), then all jobs will be remote jobs

}

$resultDomainController=@()
$RemoteJob | foreach {
$resultDomainController= $resultDomainController + $_ | Wait-Job -ErrorAction SilentlyContinue -Force | Receive-Job -ErrorAction SilentlyContinue;
Remove-Job $_ -ErrorAction SilentlyContinue -Force }
## Wait and Receive remote jobs on each remote DCs and add to Local job result

forEach ($a in $result){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$admin = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $admin[0].Line.Split(":")
$user = Select-String C:\scripts\message.txt -pattern "Nombre principal de usuario:"
$nameUser = $user.Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioCreado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosCreados\usuariosCreados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioCreado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosCreados\usuariosCreados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

forEach ($a in $resultDomainController){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$admin = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $admin[0].Line.Split(":")
$user = Select-String C:\scripts\message.txt -pattern "Nombre principal de usuario:"
$nameUser = $user.Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioCreado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosCreados\usuariosCreados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioCreado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosCreados\usuariosCreados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

Registro de Borrado de Usuarios en el Directorio Activo

Cuando el Directorio Activo es administrado por un grupo de personas, y necesitamos saber quien ha realizado o realiza alguna tarea concreta, este script nos ayuda a mantener un registro de quien/quienes eliminan usuarios en el Directorio Activo de nuestro dominio.

Consideraciones para la ejecución del script:

  1. Habilitar auditoria en los controladores de dominio
  2.  El evento que registra la eliminación de usuarios es el 4726 de la parte de seguridad del visor de eventos.
  3. Ejecutar el script en los controladores de dominio si no se tiene habilitado WMI en dichos DC.
  4. El script registra todos los usuarios que se han eliminado en el día anterior.
  5. El script registra el objeto usuario que se ha eliminado, la fecha en la que se ha eliminado y el objeto que elimina dicha cuenta de usuario.

Script de Powershell:

 

param( [switch]$LastLogonOnly,[switch]$OuOnly)
############## Find EventID 4726 with user's requesting Kerberos TGT, skipping Exchange Health Mailbox request and extracting Users/Client names,IP Addresses ####

Set-ExecutionPolicy Unrestricted -Force

#Create directorys if not exists
New-Item -ItemType Directory -Force -Path "C:\scripts" | Out-Null

New-Item -ItemType Directory -Force -Path "C:\scripts\usuariosEliminados" | Out-Null

Remove-Item "C:\scripts\usuariosEliminados\usuariosEliminados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -Force -ErrorAction SilentlyContinue

$Domain = (Get-WmiObject Win32_Computersystem).domain

$read_log={
Param ($OuOnly,$Domain) ## Define parameter to pass maxevent to scripblock
$EventInfo=Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4726} | where {$_.Message -notmatch "SM_" } | where { $_.Message -notmatch "\$" } |
select Message, TimeCreated , MachineName

$EventInfo | foreach {
############# Find the User account in AD and if not found, throw and exception ###########
$Full_User_Property=0
Try ## Need Try statement to test and surpress error
{
$Full_User_Property = (Get-AdUser $_.usuario -Properties *)
$_."Localizacion" = $Full_User_Property.CanonicalName.TrimStart($Domain).SubString(1)
}
catch
{ } ## The $_."User Location" is not passed to catch statement thus needing another below statement to set value"

Return $_
}
}

########### Job starts to query replica domain controllers #############
$result=@()
$RemoteJob=@()

#Importa modulo de AD
Import-Module ActiveDirectory
$date= ((Get-Date).AddDays(-2))
$dateString = $date.ToLongDateString()

## Make array of remote jobs

$DomainControllers = (Get-ADDomainController -Filter { isGlobalCatalog -eq $true -or isGlobalCatalog -eq $false}).Name

############### Start the Local Job and Remote Job to find the event id ################
$LocalJobExists=0
If ($DomainControllers -contains $(hostname)) ## Check if the computer running the script is Domain Controller itself
{
$LocalJob = Start-Job -scriptblock $read_log -ArgumentList $OuOnly,$Domain;$LocalJobExists=1 ## If so, start job to query local domain controller
}

$DomainControllers | where {$_ -ne $(hostname)} | foreach {
## Start remote jobs on each other domain controllers
$RemoteJob+= Invoke-Command -ComputerName $_ -ScriptBlock $read_log -ArgumentList $OuOnly,$Domain -AsJob -ErrorAction SilentlyContinue
}
If ($LocalJobExists)
{
$result = $LocalJob | Wait-Job -ErrorAction SilentlyContinue -Force | Receive-Job -ErrorAction SilentlyContinue ;
Remove-Job $LocalJob -ErrorAction SilentlyContinue -Force ## If the computer running the script is not a domain controller(may be RSAT installed), then all jobs will be remote jobs

}

$resultDomainController=@()
$RemoteJob | foreach {
$resultDomainController= $resultDomainController + $_ | Wait-Job -ErrorAction SilentlyContinue -Force | Receive-Job -ErrorAction SilentlyContinue;
Remove-Job $_ -ErrorAction SilentlyContinue -Force }
## Wait and Receive remote jobs on each remote DCs and add to Local job result

forEach ($a in $result){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$user = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $user[0].Line.Split(":")
$nameUser = $user[1].Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioEliminado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosEliminados\usuariosEliminados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioEliminado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosEliminados\usuariosEliminados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

forEach ($a in $resultDomainController){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$user = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $user[0].Line.Split(":")
$nameUser = $user[1].Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioEliminado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosEliminados\usuariosEliminados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioEliminado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\usuariosEliminados\usuariosEliminados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

Registro de cuentas deshabilitadas en el Directorio Activo

Cuando el Directorio Activo es administrado por un grupo de personas, y necesitamos saber quien ha realizado o realiza alguna tarea concreta, este script nos ayuda a mantener un registro de quien/quienes deshabilitan objetos usuarios en el Directorio Activo de nuestro dominio.

Consideraciones para la ejecución del script:

  1. Habilitar auditoria en los controladores de dominio
  2.  El evento que registra la eliminación de usuarios es el 4725 de la parte de seguridad del visor de eventos.
  3. Ejecutar el script en los controladores de dominio si no se tiene habilitado WMI en dichos DC.
  4. El script registra todos los usuarios que se han habilitado en el día anterior.
  5. El script registra el objeto usuario que se ha deshabilitado, la fecha en la que se ha deshabilitado y la cuenta administrativa que deshabilita dicha cuenta de usuario.

Script:

 

param( [switch]$LastLogonOnly,[switch]$OuOnly)
############## Find EventID 4725 with user's requesting Kerberos TGT, skipping Exchange Health Mailbox request and extracting Users/Client names,IP Addresses ####

Set-ExecutionPolicy Unrestricted -Force

#Create directorys if not exists
New-Item -ItemType Directory -Force -Path "C:\scripts" | Out-Null

New-Item -ItemType Directory -Force -Path "C:\scripts\cuentasUsuariosDeshabilitadas" | Out-Null

Remove-Item "C:\scripts\cuentasUsuariosDeshabilitadas\cuentasUsuariosDeshabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -Force -ErrorAction SilentlyContinue

$Domain = (Get-WmiObject Win32_Computersystem).domain

$read_log={
Param ($OuOnly,$Domain) ## Define parameter to pass maxevent to scripblock
$EventInfo=Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4725} | where {$_.Message -notmatch "SM_" } | where { $_.Message -notmatch "\$" } |
select Message, TimeCreated , MachineName

$EventInfo | foreach {
############# Find the User account in AD and if not found, throw and exception ###########
$Full_User_Property=0
Try ## Need Try statement to test and surpress error
{
$Full_User_Property = (Get-AdUser $_.usuario -Properties *)
$_."Localizacion" = $Full_User_Property.CanonicalName.TrimStart($Domain).SubString(1)
}
catch
{ } ## The $_."User Location" is not passed to catch statement thus needing another below statement to set value"

Return $_
}
}

########### Job starts to query replica domain controllers #############
$result=@()
$RemoteJob=@()

#Importa modulo de AD
Import-Module ActiveDirectory
$date= ((Get-Date).AddDays(-1))
$dateString = $date.ToLongDateString()

## Make array of remote jobs

$DomainControllers = (Get-ADDomainController -Filter { isGlobalCatalog -eq $true -or isGlobalCatalog -eq $false}).Name

############### Start the Local Job and Remote Job to find the event id ################
$LocalJobExists=0
If ($DomainControllers -contains $(hostname)) ## Check if the computer running the script is Domain Controller itself
{
$LocalJob = Start-Job -scriptblock $read_log -ArgumentList $OuOnly,$Domain;$LocalJobExists=1 ## If so, start job to query local domain controller
}

$DomainControllers | where {$_ -ne $(hostname)} | foreach {
## Start remote jobs on each other domain controllers
$RemoteJob+= Invoke-Command -ComputerName $_ -ScriptBlock $read_log -ArgumentList $OuOnly,$Domain -AsJob -ErrorAction SilentlyContinue
}

If ($LocalJobExists)
{
$result = $LocalJob | Wait-Job | Receive-Job; Remove-Job $LocalJob ## If the computer running the script is not a domain controller(may be RSAT installed), then all jobs will be remote jobs

}

$resultDomainController=@()
$RemoteJob | foreach {
$resultDomainController= $resultDomainController + $_ | Wait-Job -ErrorAction SilentlyContinue -Force | Receive-Job -ErrorAction SilentlyContinue;
Remove-Job $_ -ErrorAction SilentlyContinue -Force }
## Wait and Receive remote jobs on each remote DCs and add to Local job result

forEach ($a in $result){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$user = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $user[0].Line.Split(":")
$nameUser = $user[1].Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioDeshabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosDeshabilitadas\cuentasUsuariosDeshabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioDeshabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosDeshabilitadas\cuentasUsuariosDeshabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

forEach ($a in $resultDomainController){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$user = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $user[0].Line.Split(":")
$nameUser = $user[1].Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioDeshabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosDeshabilitadas\cuentasUsuariosDeshabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioDeshabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosDeshabilitadas\cuentasUsuariosDeshabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

Relación entre usuarios y máquinas validado en el Directorio Activo

Para tener la relación entre los usuarios que se encuentran registrados en el Directorio Activo y las maquinas en las cuales se validan esos usuarios, aquí se encuentra un script que realiza esa tarea.Consideraciones:

  1. En primer lugar el evento que registra dicha información es el número 4768, en la parte de seguridad del visor de eventos, verificar que estamos registrando esos eventos en el AD.
  2. Script está generado para Controladores de Dominio instalados en español.
  3. Si no se tiene habilitado WMI en los controladores de dominio, se tiene que ejecutar en un DC siempre.
  4. El numero de eventos por defecto que se leen del visor de eventos son 1000.
  5. La información la almacena en un fichero, que hay que poner como salida del script.

Script de PowerShell:

<#
.SYNOPSIS
Script that will list the logon information of AD users.

.DESCRIPTION
This script will list the AD users logon information with their logged on computers by inspecting the Kerberos TGT Request
Events(EventID 4768) from domain controllers. Not Only User account Name is fetched, but also users OU path and Computer
Accounts are retrieved. You can also list the history of last logged on users. In Environment where Exchange Servers are
used, the exchange servers authentication request for users will also be logged since it also uses EventID (4768) to for
TGT Request. You can also export the result to CSV file format. Powershell version 4.0 is needed to use the script.
You can Define the following parameters to suite your need:
-MaxEvent Specify the number of all (4768) events to search for TGT Requests. Default is 1000.
-LastLogonOnly Display only the history of last logon users.
-OuOnly Do not display the full path of users/computers. Only OU is displayed.
Author: phyoepaing3.142@gmail.com
Country: Myanmar(Burma)
Released Date: 08/29/2016

Example usage:
.\Get_AD_Users_Logon_History.ps1 -MaxEvent 800 -LastLogonOnly -OuOnly

.EXAMPLE
.\Get_AD_Users_Logon_History.ps1 | Format-Table * -Auto
This command will retrieve AD users logon within default 1000 EventID-4768 events and display the results as table.

.EXAMPLE
.\Get_AD_Users_Logon_History.ps1 -MaxEvent 500 -LastLogonOnly -OuOnly
This command will retrieve AD users logon within 500 EventID-4768 events and show only the last logged users with their
related logged on computers. Only OU name is displayed in results.

.EXAMPLE
.\Get_AD_Users_Logon_History.ps1 | Export-Csv Users_Loggedon_History.csv
This command will retrieve AD users logon within default 1000 EventID-4768 events and export the result to CSV file.

.PARAMETER MaxEvent
This paraemeter will specify the number of EventID-4768 events to look for.

.PARAMETER LastLogonOnly
This paraemeter will display the history of last logged on users in descending order.

.PARAMETER OuOnly
This paraemeter will show only the OU names for users/computers but not the full path.

.LINK
You can find this script and more at: https://www.sysadminplus.blogspot.com/
#>
#Set-ExecutionPolicy RemoteSigned -scope Process
param( [switch]$LastLogonOnly,[switch]$OuOnly,[int]$MaxEvent=1000)
############## Find EventID 4768 with user's requesting Kerberos TGT, skipping Exchange Health Mailbox request and extracting Users/Client names,IP Addresses ####
$Domain = (Get-WmiObject Win32_Computersystem).domain

$read_log={
Param ($MaxEvent,$OuOnly,$Domain) ## Define parameter to pass maxevent to scripblock
$EventInfo=Get-WinEvent -FilterHashTable @{LogName="Security";ID=4768} -MaxEvents 2000 | select -first $MaxEvent | where {$_.Message -notmatch "SM_" } | where { $_.Message -notmatch "\$" } |
select @{N="DC";Exp={$_.MachineName}},
@{N="Hora Loggin";Exp={$_.TimeCreated}},
@{N="Usuario"; Exp={ $SplitAccountName=(($_.Message -Split "\n") -match "Nombre de cuenta") -split ':';$SplitAccountName[$SplitAccountName.Length-1].Trim() }},
@{N="Localizacion";Exp={}}, @{N="Maquina";Exp={}},
@{N="IP Address"; Exp={if((($_.Message -split "\n") -match "Dirección de cliente:").Trim() -match "::1" ) {"localhost"}
else { $SplitClientAddress=(($_.Message -Split "\n") -match "Dirección de cliente") -split ':'; $splitClientAddress[$splitClientAddress.Length-1].Trim() }}},
@{N="Computer Location";Exp={}}

$EventInfo | foreach {
$IPAddress=$_."Dirección de cliente:"

if ($_."IP Address" -eq "localhost")
{ $Client_Name=[system.net.dns]::GetHostbyName($env:computername).hostname }
else
{
###### Resolve the PTR record to find AD computer information ################
#$Client_Name=(Resolve-DnsName $_."IPAddress").NameHost
if ((Resolve-dnsname $_."IP Address" -Type PTR -TcpOnly -DnsOnly -ErrorAction "SilentlyContinue").Type -eq "PTR")
{
$Client_Name = (Resolve-dnsname $_."IP Address" -Type PTR -TcpOnly -DnsOnly).NameHost

}
else
{ $Client_Name = "NO ENCONTRADO" }

}
$_."DC"=($_."DC" -split "."+$Domain)[0] ##Uncomment this line if you want to strip off domain name in "Authenticated DC" list
$user=$_.usuario
############# Find the User account in AD and if not found, throw and exception ###########
$Full_User_Property=0
Try ## Need Try statement to test and surpress error
{
$Full_User_Property = (Get-AdUser $_.usuario -Properties *)
$_."Localizacion" = $Full_User_Property.CanonicalName.TrimStart($Domain).SubString(1)
}
catch
{ } ## The $_."User Location" is not passed to catch statement thus needing another below statement to set value"
If (!$Full_User_Property)
{ $_."Localizacion"="NO ENCONTRADO" }
$Full_User_Property=0

If($OuOnly -AND ($_."Localizacion" -ne "NO ENCONTRADO"))
{
$_."Localizacion"= $_."Localizacion".Remove($_."Localizacion".LastIndexOf("/")) ##Trim the last user name part if -OuOnly flag is set
}

$_."Maquina"=($Client_Name -split "."+$Domain)[0] ## remove the domain suffix
########## Find the Computer account in AD and if not found, throw an exception ###########
$Full_Workstation_Property = 0
Try
{
$Full_Workstation_Property = Get-AdComputer $_."Maquina" -Properties *
$_."Computer Location"= $Full_Workstation_Property.CanonicalName.TrimStart($Domain).SubString(1)
}
catch
{ }
########## Here the catch exception does not work in Invoke session so we need to manually set the "NOT FOUND" value ######
If (!$Full_Workstation_Property)
{ $_."Computer Location" = "NOT FOUND"}
$Full_Workstation_Property=0

If ($OuOnly -AND ($_."Computer Location" -ne "NOT FOUND")) ##Trim the last computer part if -OuOnly flag is set
{
$_."Computer Location"=$_."Computer Location".Remove($_."Computer Location".LastIndexOf("/"))
}
Return $_
}
}

########### Job starts to query replica domain controllers #############
$result=@()
$RemoteJob=@()

#Importa modulo de AD
Import-Module ActiveDirectory
## Make array of remote jobs

$DomainControllers = (Get-ADDomainController -Filter { isGlobalCatalog -eq $true -or isGlobalCatalog -eq $false}).Name
############### Start the Local Job and Remote Job to find the event id ################
$LocalJobExists=0
If ($DomainControllers -contains $(hostname)) ## Check if the computer running the script is Domain Controller itself
{
$LocalJob = Start-Job -scriptblock $read_log -ArgumentList $MaxEvent,$OuOnly,$Domain;$LocalJobExists=1 ## If so, start job to query local domain controller
}

$DomainControllers | where {$_ -ne $(hostname)} | foreach { ## Start remote jobs on each other domain controllers
$RemoteJob+= Invoke-Command -ComputerName $_ -ScriptBlock $read_log -ArgumentList $MaxEvent,$OuOnly,$Domain -AsJob
}

If ($LocalJobExists)
{
$result = $LocalJob | Wait-Job | Receive-Job; Remove-Job $LocalJob ## If the computer running the script is not a domain controller(may be RSAT installed), then all jobs will be remote jobs
}

$RemoteJob | foreach { $result+=$_ | Wait-Job | Receive-Job ; Remove-Job $_} ## Wait and Receive remote jobs on each remote DCs and add to Local job result

If ($LastLogonOnly)
{
$result | Sort-Object "LoggedOn Time" -Descending | Group-Object User | foreach { $_ | Select -ExpandProperty Group | select * -First 1 -ExcludeProperty PsComputerName,RunSpaceID,PsShowComputerName } ## the Last LoggedOn time of Each User
}
else
{
$result | Sort-Object "LoggedOn Time" -Descending | Select * -ExcludeProperty PsComputerName,RunSpaceID,PsShowComputerName ## Normal Results
}

 

Obtener objetos de equipos obsoletos del Directorio Activo

En los dominios se van quedando muchos objetos obsoletos que ya no se usan o no se han eliminado bien del directorio activo. Para hacer limpieza en el dominio de los objetos equipos que se han quedado y ya no se usan, podemos obtener un listado en función de los días que indiquemos que lleven sin utilizarse. Con este comando:

dsquery computer "OU=XXXX,DC=xxxxx,DC=xxxx,DC=xx"  -inactive 52 -limit 0 |out-file "C:\equiposobsoletos\resultado.txt"

En el parámetro “inactive” indicamos el numero de días que lleva como mínimo sin usarse. En este ejemplo sacará los objetos equipos que lleven más de 52 días sin usarse.

Habilitar el atributo “El usuario no puede cambiar la contraseña” de varios usuarios en Directorio Activo

Para habilitar el atributo “El usuario no puede cambiar la contraseña” de todos los usuarios de una OU del dominio se haría con el siguiente script:

Import-Module Activedirectory

$fecha= Get-Date -Format M-d-yyyy
$fecha>C:\script\PonerAtributoPassword\Usuarios.txt

$Users1 = (Get-ADUser –filter * -SearchBase "OU=UnidadOrganizativa,DC=Dominio,DC=es" -properties * -Server servidor.dominio.es:3268| select-object UserprincipalName,SamAccountName)

Foreach ($User in $Users1) {
            Set-ADUser -Identity $user.SamAccountName -CannotChangePassword $true -PasswordNeverExpires $true -Server servidor.dominio.es
            $user>>C:\script\PonerAtributoPassword\Usuarios.txt
}

 

Registro de cuentas habilitadas en el Directorio Activo

Cuando el Directorio Activo es administrado por un grupo de personas, y necesitamos saber quien ha realizado o realiza alguna tarea concreta, este script nos ayuda a mantener un registro de quien/quienes habilitan objetos usuarios en el Directorio Activo de nuestro dominio.

Consideraciones para la ejecución del script:

  1. Habilitar auditoria en los controladores de dominio
  2.  El evento que registra la eliminación de usuarios es el 4722 de la parte de seguridad del visor de eventos.
  3. Ejecutar el script en los controladores de dominio si no se tiene habilitado WMI en dichos DC.
  4. El script registra todos los usuarios que se han habilitado en el día anterior.
  5. El script registra el objeto usuario que se ha habilitado, la fecha en la que se ha habilitado y la cuenta administrativa que habilita dicha cuenta de usuario.

Script:

 

param( [switch]$LastLogonOnly,[switch]$OuOnly)
############## Find EventID 4722 with user's requesting Kerberos TGT, skipping Exchange Health Mailbox request and extracting Users/Client names,IP Addresses ####

Set-ExecutionPolicy Unrestricted -Force

#Create directorys if not exists
New-Item -ItemType Directory -Force -Path "C:\scripts" | Out-Null

New-Item -ItemType Directory -Force -Path "C:\scripts\cuentasUsuariosHabilitadas" | Out-Null

Remove-Item "C:\scripts\cuentasUsuariosHabilitadas\cuentasUsuariosHabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -Force -ErrorAction SilentlyContinue

$Domain = (Get-WmiObject Win32_Computersystem).domain

$read_log={
Param ($OuOnly,$Domain) ## Define parameter to pass maxevent to scripblock
$EventInfo=Get-WinEvent -FilterHashTable @{LogName="Security"; ID=4722} | where {$_.Message -notmatch "SM_" } | where { $_.Message -notmatch "\$" } |
select Message, TimeCreated , MachineName

$EventInfo | foreach {
############# Find the User account in AD and if not found, throw and exception ###########
$Full_User_Property=0
Try ## Need Try statement to test and surpress error
{
$Full_User_Property = (Get-AdUser $_.usuario -Properties *)
$_."Localizacion" = $Full_User_Property.CanonicalName.TrimStart($Domain).SubString(1)
}
catch
{ } ## The $_."User Location" is not passed to catch statement thus needing another below statement to set value"

Return $_
}
}

########### Job starts to query replica domain controllers #############
$result=@()
$RemoteJob=@()

#Importa modulo de AD
Import-Module ActiveDirectory
$date= ((Get-Date).AddDays(-1))
$dateString = $date.ToLongDateString()

## Make array of remote jobs

$DomainControllers = (Get-ADDomainController -Filter { isGlobalCatalog -eq $true -or isGlobalCatalog -eq $false}).Name

############### Start the Local Job and Remote Job to find the event id ################
$LocalJobExists=0
If ($DomainControllers -contains $(hostname)) ## Check if the computer running the script is Domain Controller itself
{
$LocalJob = Start-Job -scriptblock $read_log -ArgumentList $OuOnly,$Domain;$LocalJobExists=1 ## If so, start job to query local domain controller
}

$DomainControllers | where {$_ -ne $(hostname)} | foreach {
## Start remote jobs on each other domain controllers
$RemoteJob+= Invoke-Command -ComputerName $_ -ScriptBlock $read_log -ArgumentList $OuOnly,$Domain -AsJob -ErrorAction SilentlyContinue
}

If ($LocalJobExists)
{
$result = $LocalJob | Wait-Job | Receive-Job; Remove-Job $LocalJob ## If the computer running the script is not a domain controller(may be RSAT installed), then all jobs will be remote jobs

}

$resultDomainController=@()
$RemoteJob | foreach {
$resultDomainController= $resultDomainController + $_ | Wait-Job -ErrorAction SilentlyContinue -Force | Receive-Job -ErrorAction SilentlyContinue;
Remove-Job $_ -ErrorAction SilentlyContinue -Force }
## Wait and Receive remote jobs on each remote DCs and add to Local job result

forEach ($a in $result){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$user = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $user[0].Line.Split(":")
$nameUser = $user[1].Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioHabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosHabilitadas\cuentasUsuariosHabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioHabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosHabilitadas\cuentasUsuariosHabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

forEach ($a in $resultDomainController){
if ($a.TimeCreated.ToLongDateString() -eq $dateString) {
#Manipulate manually message
$message = $a.message
$message | Out-File -FilePath "C:\scripts\message.txt"
$user = Select-String C:\scripts\message.txt -pattern "Nombre de cuenta:"
$nameAdmin = $user[0].Line.Split(":")
$nameUser = $user[1].Line.Split(":")
Remove-Item C:\scripts\message.txt -Force -ErrorAction SilentlyContinue

If ($LastLogonOnly)
{
$(
[PSCustomObject]@{UsuarioHabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosHabilitadas\cuentasUsuariosHabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append
}
else
{
$(
[PSCustomObject]@{UsuarioHabilitado=$nameUser[1].Trim();Administrador=$nameAdmin[1].Trim();FechayHora=$a.TimeCreated.ToShortDateString() +","+ $a.TimeCreated.ToShortTimeString();NombreMaquina=$a.MachineName}
) | Export-Csv "C:\scripts\cuentasUsuariosHabilitadas\cuentasUsuariosHabilitados_$((Get-Date).AddDays(-1).ToString('dd-MM-yyyy')).csv" -NoTypeInformation -Append

}
}
}

Mensaje de error si selecciona un controlador de dominio cuando se utiliza el Asistente para modelado de directivas de grupo: “Acceso denegado”, en caso de tener los permisos delegados

Método 1: Si tenemos pocos controladores de dominio, realizarlo en cada uno

  1. Haga clic en Inicio, haga clic en Ejecutar y ponga:
    c: \WINDOWS\system32\Com\comexp.msc y, a continuación, haga clic en Aceptar.
  2. En el panel izquierdo, expanda Servicios de componente y, a continuación, expanda equipos.
  3. Haga clic en Mi PC y, a continuación, haga clic en:
    Propiedades.
  4. En la ficha Seguridad COM, haga clic en:
    Editar límites en el campo Inicio y permisos de activación .
  5. Haga clic en el nombre de usuario en el campo nombres de grupos o usuarios qué desea poder ejecutar el Asistente para modelado de directivas de grupo y, a continuación, haga clic para seleccionar Permitir para el:
    Permisos de Activación remota .
  6. Haga clic en Aceptar dos veces.

Método 2: Si tenemos muchos controladores de dominio del dominio

  1. Crear una nueva directiva de grupo en la unidad organizativa (OU) del controlador de dominio.
  2. En la consola de directivas de grupo de controladores de dominio, seleccione:
    Configuración del equipo\Configuración de Windows\Configuración de seguridad\directivas locales\Opciones de seguridad.
  3. En la lista de directivas disponibles, haga doble clic en:
    DCOM: restricciones de inicio de equipo en sintaxis de lenguaje de definición de descriptores de seguridad (SDDL). Haga clic en modificar seguridad, haga clic en el nombre de usuario en el campo nombres de grupos o usuarios que desea que puedan ejecutar el Asistente para modelado de directivas de grupo y, a continuación, haga clic para seleccionar: Permitir para el permiso Activación remota.
  4. Haga clic en Aceptar dos veces.
  5. Editor de objetos de directiva de grupo de salida

Delegar permisos para generar datos de Modelación de directivas de grupo

Para delegar permisos para generar datos de Modelación de directivas de grupo:

  1. En el árbol de consola de Consola de administración de directivas de grupo (GPMC), haga clic en el dominio o la unidad organizativa para los que desee delegar permisos de Modelación de directivas de grupo.
  2. En el panel de resultados, haga clic en la ficha Delegación.
  3. En el cuadro Permisos, seleccione Realizar análisis de Modelación de directivas de grupo para agregar un nuevo grupo o usuario a la lista de permisos.
  4. En la ficha Delegación, haga clic en Agregar.
  5. En el cuadro de diálogo Seleccionar usuarios, equipos o grupos, haga clic en Tipos de objeto, seleccione los tipos de objeto en los que desea delegar permisos para el dominio, sitio o unidad organizativa y, después, haga clic en Aceptar.
  6. Haga clic en Ubicaciones, seleccione Todo el directorio, o bien el dominio o la unidad organizativa que contiene el objeto en el que desea delegar los permisos y, a continuación, haga clic en Aceptar.
  7. En el cuadro e­­scriba el nombre de objeto a seleccionar, busque el nombre del objeto en el que desea delegar los permisos mediante una de las siguientes acciones:
    • Si conoce el nombre, escríbalo y haga clic de nuevo en Aceptar.
    • Para buscar el nombre, haga clic en Opciones avanzadas, escriba los criterios de búsqueda, haga clic en Buscar ahora, seleccione el nombre de la lista desplegable, haga clic en Aceptar y vuelva a hacer clic en Aceptar.
  1. En el cuadro de diálogo Agregar grupo o usuario, en la lista desplegable Permisos, seleccione el nivel en el que desea que se apliquen los permisos para este grupo o usuario y, a continuación, haga clic en Aceptar.