Aprende dos métodos confiables para buscar equipos en todo el bosque de Active Directory (igual que “Entire Directory” en ADUC), incluso desde un equipo que no está unido al dominio. Incluyo scripts listos para copiar, filtros útiles, puertos requeridos, consejos de seguridad y resolución de errores.
Qué vas a lograr
Al finalizar tendrás scripts de PowerShell probados para:
- Listar equipos a nivel de bosque, sin limitarte a un solo dominio u OU.
- Ejecutar la búsqueda desde una máquina no unida al dominio.
- Elegir entre dos enfoques: cmdlets nativos del módulo
ActiveDirectory
o consulta directa al Catálogo Global (GC). - Filtrar por nombre, sistema operativo y atributos clave; exportar a CSV/JSON.
Requisitos previos
Componente | Para qué sirve | Cómo verificar/instalar |
---|---|---|
RSAT con “Active Directory module for Windows PowerShell” | Usar cmdlets como Get-ADForest y Get-ADComputer (Opción A). | Get-Module ActiveDirectory -ListAvailable Si no aparece: Add-WindowsCapability -Online -Name RSAT.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 |
.NET DirectoryServices | Búsqueda LDAP/GC sin depender de ADWS (Opción B). | Disponible por defecto en Windows con .NET Framework/PowerShell 5.1. En PowerShell 7, utilizable en Windows. |
Credenciales con permisos de lectura | Consultar objetos de equipo en todos los dominios del bosque. | Usa Get-Credential . Recomendado: UPN (usuario@dominio ). |
Conectividad | Puertos abiertos hacia los DCs/GCs. | ADWS 9389/TCP (Opción A), GC 3268/TCP o 3269/TCP SSL (Opción B). |
Opción A — Recorrido de todos los dominios del bosque con el módulo ActiveDirectory
Ventajas: usa cmdlets nativos, manejo sencillo de credenciales, resultados enriquecidos (propiedades calculadas).
Cuándo usarla: cuando puedes alcanzar ADWS (puerto 9389) en los controladores de dominio.
# Requisitos: RSAT con "Active Directory module for Windows PowerShell"
Import-Module ActiveDirectory
1) Captura de credenciales (no las hardcodes)
$cred = Get-Credential # formato: DOMINIO\usuario o usuario@dominio
2) Punto de entrada (dominio raíz del bosque o un DC alcanzable)
$server = "contoso.com" # cámbialo por tu dominio raíz o un DC
3) Patrón de búsqueda (como en ADUC; * = comodín)
$namePattern = "PC-123"
4) Enumerar dominios del bosque y buscar equipos en cada uno
$forest = Get-ADForest -Server $server -Credential $cred
$results = foreach ($domain in $forest.Domains) {
Get-ADComputer -Server $domain -Credential $cred `
-Filter "Name -like '$namePattern'" `
-Properties DNSHostName, OperatingSystem, IPv4Address, LastLogonDate |
Select-Object Name, DNSHostName, OperatingSystem, IPv4Address, LastLogonDate,
@{n='Domain'; e={$domain}}
}
5) Mostrar y/o exportar resultados
$results | Sort-Object Domain, Name | Format-Table -AutoSize
Opcional:
$results | Export-Csv -Path "C:\Temp\Equipos_Encontrados.csv" -NoTypeInformation -Encoding UTF8
Notas útiles
- Cambia
$namePattern
por*
si deseas inventario completo de equipos. - Agrega atributos a
-Properties
según necesites (whenCreated
,Enabled
,OperatingSystemVersion
, etc.). - Si tu cuenta no ve todos los dominios/objetos, pide al equipo de AD permisos de lectura a nivel de bosque.
Variantes prácticas con cmdlets de AD
# Filtrar por sistema operativo (ej. Windows 11)
$ldapFilter = "(&(objectCategory=computer)(operatingSystem=Windows 11*))"
Get-ADComputer -Server "contoso.com" -Credential $cred -LDAPFilter $ldapFilter -Properties OperatingSystem |
Select-Object Name, OperatingSystem
Limitar por OU en un dominio concreto (no es "Entire Directory", pero útil si quieres acotar):
\$ou = "OU=Equipos,OU=Prod,DC=contoso,DC=com"
Get-ADComputer -Server "contoso.com" -Credential \$cred -Filter \* -SearchBase \$ou -Properties DNSHostName |
Select-Object Name, DNSHostName
Opción B — Búsqueda directa en Catálogo Global (equivalente a “Entire Directory”)
Ventajas: una única búsqueda forestal; no depende de ADWS, basta con LDAP/GC.
Cuándo usarla: cuando solo tengas acceso al Catálogo Global (puerto 3268) o prefieras replicar exactamente la experiencia de ADUC.
En este enfoque evitamos los cmdlets de AD para la resolución del GC y usamos .NET DirectoryServices, lo que lo hace válido aun con el puerto 9389 bloqueado.
# Requisitos: .NET DirectoryServices en Windows (PowerShell 5.1/7)
$cred = Get-Credential # DOMINIO\usuario o UPN (recomendado)
$forestName = "contoso.com" # dominio raíz del bosque o sufijo UPN
$namePattern = "PC-123" # comodines como en ADUC
$useSslGc = $true # GC seguro (3269) si hay PKI en los DCs
1) Descubrir un Global Catalog del bosque SIN ADWS
Add-Type -AssemblyName System.DirectoryServices
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
Add-Type -AssemblyName System.DirectoryServices.Protocols
Add-Type -AssemblyName System.DirectoryServices.DirectoryServices
$dcUser = $cred.UserName
$dcPwd = $cred.GetNetworkCredential().Password
DirectoryContext para el bosque (LDAP, no ADWS)
$ctx = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext(
[System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Forest,
$forestName, $dcUser, $dcPwd)
$forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ctx)
$gc = $forest.FindGlobalCatalog()
$gcHost = $gc.Name # p.ej. dc01.contoso.com
$gcPort = if ($useSslGc) { 3269 } else { 3268 }
Derivar DN raíz del bosque vía RootDSE del propio GC
$rootDse = New-Object System.DirectoryServices.DirectoryEntry(
"LDAP://$($gcHost):$gcPort/RootDSE", $dcUser, $dcPwd)
$rootDN = $rootDse.Properties[0]
2) Preparar conexión a GC:// con credenciales
$entry = New-Object System.DirectoryServices.DirectoryEntry(
"GC://$($gcHost):$gcPort/$rootDN", $dcUser, $dcPwd)
$searcher = New-Object System.DirectoryServices.DirectorySearcher($entry)
$searcher.PageSize = 1000
$searcher.SearchScope = [System.DirectoryServices.SearchScope]::Subtree
$searcher.ClientTimeout = [TimeSpan]::FromSeconds(60)
$searcher.ServerTimeLimit = [TimeSpan]::FromSeconds(60)
Filtro: equipos + patrón de nombre (igual que ADUC)
$searcher.Filter = "(&(objectCategory=computer)(name=$namePattern))"
@('name','dnshostname','operatingsystem','lastlogontimestamp','distinguishedname') |
ForEach-Object { [void]$searcher.PropertiesToLoad.Add($_) }
3) Ejecutar búsqueda y mapear propiedades de salida
$results = $searcher.FindAll() | ForEach-Object {
[PSCustomObject]@{
Name = $_.Properties[0]
DNSHostName = $_.Properties[0]
OperatingSystem = $_.Properties[0]
LastLogonTimestamp = if ($_.Properties['lastlogontimestamp']) {
[DateTime]::FromFileTime[0]
} else { $null }
DistinguishedName = $_.Properties[0]
}
}
$results | Sort-Object Name | Format-Table -AutoSize
Opcional: exportar
$results | Export-Csv -Path "C:\Temp\EquiposEncontradosGC.csv" -NoTypeInformation -Encoding UTF8
Notas clave
- El GC devuelve un conjunto parcial de atributos. Si necesitas más, resuelve por DN con
Get-ADComputer -Identity <DN> -Server <dominio>
donde toque. - Para proteger credenciales, usa 3269/TCP (GC sobre SSL). Requiere certificados en los DCs. Si no hay PKI lista, usa 3268/TCP entendiendo el riesgo.
- Si no logras resolver el GC automáticamente, define
$gcHost
manualmente o usa SRV DNS:(Resolve-DnsName -Type SRV "gc.tcp.$forestName").NameTarget
.
Cómo ejecutarlo desde una máquina fuera de dominio
- DNS: apunta la máquina al DNS corporativo (o usa FQDN de DC/GC). Sin resolución correcta, nada funciona.
- Credenciales: usa UPN (
usuario@dominio
) conGet-Credential
. - Firewall: valida puertos según la opción elegida (ver más abajo).
- Hora: asegúrate de que el reloj del equipo esté razonablemente sincronizado para evitar rechazos de autenticación.
Comprobación rápida de conectividad
# ADWS (Opción A)
Test-NetConnection -ComputerName dc01.contoso.com -Port 9389
GC en claro (Opción B)
Test-NetConnection -ComputerName dc01.contoso.com -Port 3268
GC sobre SSL (Opción B recomendado)
Test-NetConnection -ComputerName dc01.contoso.com -Port 3269
Comparativa y elección
Criterio | Opción A (ActiveDirectory) | Opción B (Catálogo Global) |
---|---|---|
Dependencia de ADWS | Sí, 9389/TCP | No, usa 3268/3269 |
Equivalencia “Entire Directory” | Se logra recorriendo los dominios | Equivalente natural del GC |
Facilidad de filtros | Muy alta con -Filter /-LDAPFilter | LDAP estándar, potente |
Atributos disponibles | Completo, incl. propiedades calculadas | Parcial (Partial Attribute Set) |
Seguridad de transporte | ADWS cifra por defecto | Preferir 3269 (SSL) |
Complejidad del script | Baja | Media |
Puertos a tener en cuenta
Servicio | Puerto/TCP | Uso | Opción |
---|---|---|---|
AD Web Services | 9389 | Cmdlets del módulo ActiveDirectory | A |
Global Catalog | 3268 | Búsqueda forestal sin cifrado | B |
Global Catalog sobre SSL | 3269 | Búsqueda forestal cifrada | B |
Rendimiento, límites y buenas prácticas
- Paginación: usa
PageSize = 1000
enDirectorySearcher
(Opción B) y evita traer atributos innecesarios. - Agrupa resultados: si el inventario es masivo, escribe a CSV conforme vas procesando (evita mantener todo en memoria).
- Fechas de último inicio de sesión:
lastLogonTimestamp
replica con latencia; si necesitas precisión por DC, consultalastLogon
en cada DC, pero no está replicado. - Filtros específicos: primero acota por pattern de nombre o por SO para reducir la carga.
Seguridad y cumplimiento
- Credenciales: jamás las hardcodes. Siempre
Get-Credential
o almacén seguro (DPAPI, SecretManagement). - Transporte: en GC, prefiere 3269/TCP si tu entorno tiene certificados válidos en los DCs.
- Mínimo privilegio: con permisos de lectura basta para esta tarea; evita cuentas de alto privilegio.
- Registro: si exportas datos, revisa políticas de protección de información (PII, clasificación interna).
Tratamiento de errores y tiempos de espera
try {
Import-Module ActiveDirectory -ErrorAction Stop```
$cred = Get-Credential
$server = "contoso.com"
$namePattern = "*"
$forest = Get-ADForest -Server $server -Credential $cred
$results = foreach ($domain in $forest.Domains) {
try {
Get-ADComputer -Server $domain -Credential $cred `
-Filter "Name -like '$namePattern'" `
-Properties DNSHostName, OperatingSystem, LastLogonDate
}
catch {
Write-Warning "Fallo consultando $domain: $($_.Exception.Message)"
}
}
$results | Select-Object Name, DNSHostName, OperatingSystem, LastLogonDate |
Sort-Object Name | Format-Table -AutoSize
```
}
catch {
Write-Error "No fue posible cargar módulo AD o consultar el bosque: \$($\_.Exception.Message)"
}
Exportación e integración
# Exportar a CSV
$results | Export-Csv -Path "C:\Temp\Equipos_Forest.csv" -NoTypeInformation -Encoding UTF8
Exportar a JSON (útil para APIs/automatizaciones)
\$results | ConvertTo-Json -Depth 4 | Out-File "C:\Temp\Equipos\_Forest.json" -Encoding UTF8
Filtros habituales
Escenario | Opción A (ejemplo) | Opción B (ejemplo LDAP) |
---|---|---|
Por nombre | -Filter "Name -like 'PC-123'" | (name=PC-123) |
Por SO Windows 11 | -LDAPFilter "(&(objectCategory=computer)(operatingSystem=Windows 11*))" | (&(objectCategory=computer)(operatingsystem=Windows 11*)) |
Solo equipos con FQDN | -Properties DNSHostName | (dnshostname=*) |
Errores frecuentes a evitar
- No antepongas
LDAP://
al usar-SearchBase
con cmdlets de AD; debe ser un DN (por ej.DC=contoso,DC=com
). - No supongas que el GC devuelve todos los atributos; solicita solo lo que vayas a usar y resuelve por DN si falta algo.
- No confundas
lastLogonDate
(propiedad calculada en el módulo AD) conlastLogonTimestamp
(valor replicado). - No dejes el equipo sin DNS corporativo; la resolución es crítica para Kerberos/LDAP.
Bloques rápidos listos para copiar
Recorrido por dominios con ActiveDirectory
Import-Module ActiveDirectory
$cred = Get-Credential
$forest = Get-ADForest -Server "contoso.com" -Credential $cred
$pattern = "*"
$items = foreach ($d in $forest.Domains) {
Get-ADComputer -Server $d -Credential $cred -Filter "Name -like '$pattern'" `
-Properties DNSHostName, OperatingSystem, LastLogonDate |
Select-Object Name, DNSHostName, OperatingSystem, LastLogonDate, @{n='Domain';e={$d}}
}
$items | Sort-Object Domain,Name | Format-Table -AutoSize
Búsqueda forestal directa en GC
$cred = Get-Credential
$forestName = "contoso.com"
$pattern = "*"
$ctx = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext(
'Forest', $forestName, $cred.UserName, $cred.GetNetworkCredential().Password)
$gc = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ctx).FindGlobalCatalog()
$host = $gc.Name; $port = 3269
$rootDse = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$host`:$port/RootDSE", $cred.UserName, $cred.GetNetworkCredential().Password)
$rootDN = $rootDse.Properties[0]
$entry = New-Object System.DirectoryServices.DirectoryEntry("GC://$host`:$port/$rootDN", $cred.UserName, $cred.GetNetworkCredential().Password)
$ds = New-Object System.DirectoryServices.DirectorySearcher($entry)
$ds.PageSize=1000; $ds.Filter = "(&(objectCategory=computer)(name=$pattern))"
@('name','dnshostname','operatingsystem') | % { [void]$ds.PropertiesToLoad.Add($_) }
$ds.FindAll() | % { [pscustomobject]@{Name=$.Properties[0]; DNSHostName=$.Properties[0]; OS=$_.Properties[0]} } |
Sort-Object Name | Format-Table -AutoSize
Preguntas frecuentes
¿Necesito que la máquina esté unida al dominio?
No. Ambas opciones aceptan credenciales explícitas. Debes asegurar DNS y puertos.
¿Cuál es más fiel a “Entire Directory” de ADUC?
La consulta contra el Catálogo Global (Opción B) es el equivalente natural.
¿Y si solo tengo parte del bosque accesible?
La Opción A recorrerá dominios alcanzables vía ADWS; la B devolverá lo que el GC exponga y lo que tu cuenta pueda leer. Comprueba firewalls y rutas.
¿Puedo combinar ambas?
Sí. Usa el GC para localizar rápidamente candidatos y, con los DN, obtén detalles completos con Get-ADComputer
en el dominio correspondiente.
Conclusión
Con estas dos rutas tienes cubiertos los escenarios habituales: cmdlets nativos cuando ADWS está disponible y consulta directa al Catálogo Global cuando necesitas un barrido forestal sin depender de ADWS. Los ejemplos están pensados para copiar y ejecutar tal cual, con especial cuidado en seguridad, rendimiento y exportación. Adáptalos a tu DNS, certificados y políticas, y conseguirás resultados equivalentes a “Entire Directory” de la GUI con la flexibilidad de PowerShell.
Con cualquiera de las dos opciones obtendrás una búsqueda de equipos a nivel de bosque completo, equivalente a la opción Entire Directory de la consola ADUC.