PowerShell: buscar equipos en todo el bosque de Active Directory (Entire Directory) incluso desde equipos fuera de dominio

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.

Índice

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

ComponentePara qué sirveCó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 DirectoryServicesBú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 lecturaConsultar objetos de equipo en todos los dominios del bosque.Usa Get-Credential. Recomendado: UPN (usuario@dominio).
ConectividadPuertos 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) con Get-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

CriterioOpción A (ActiveDirectory)Opción B (Catálogo Global)
Dependencia de ADWSSí, 9389/TCPNo, usa 3268/3269
Equivalencia “Entire Directory”Se logra recorriendo los dominiosEquivalente natural del GC
Facilidad de filtrosMuy alta con -Filter/-LDAPFilterLDAP estándar, potente
Atributos disponiblesCompleto, incl. propiedades calculadasParcial (Partial Attribute Set)
Seguridad de transporteADWS cifra por defectoPreferir 3269 (SSL)
Complejidad del scriptBajaMedia

Puertos a tener en cuenta

ServicioPuerto/TCPUsoOpción
AD Web Services9389Cmdlets del módulo ActiveDirectoryA
Global Catalog3268Búsqueda forestal sin cifradoB
Global Catalog sobre SSL3269Búsqueda forestal cifradaB

Rendimiento, límites y buenas prácticas

  • Paginación: usa PageSize = 1000 en DirectorySearcher (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, consulta lastLogon 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

EscenarioOpció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) con lastLogonTimestamp (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.

Índice