Besoin de retrouver des ordinateurs à l’échelle de toute une forêt Active Directory depuis PowerShell — même si votre poste n’est pas joint au domaine ciblé ? Voici une méthode fiable et sécurisée basée sur le catalogue global (ports 3268/3269), avec scripts prêts à l’emploi et bonnes pratiques.
Vue d’ensemble de la question
Dans la console Active Directory Users & Computers, l’option « Entire Directory » interroge le catalogue global (Global Catalog, GC) afin de chercher dans tous les domaines d’une même forêt. L’objectif ici est de reproduire ce comportement en PowerShell, idéalement sans être membre des domaines interrogés : il suffit de disposer de droits de lecture et d’un accès réseau aux contrôleurs de domaine qui hébergent le GC.
Deux approches efficaces
Approche express (recommandée)
Cette approche tire parti du préfixe GC://
compris par le module ActiveDirectory. Elle est concise et couvre toute la forêt sans que vous ayez à définir une base de recherche spécifique.
Import-Module ActiveDirectory
# 1) Saisir des identifiants d'un compte ayant au moins "Read" dans la forêt ciblée
$cred = Get-Credential # DOMAINE\utilisateur + mot de passe
# 2) Choisir un contrôleur qui fait office de Global Catalog dans la forêt
$gc = (Get-ADForest -Credential $cred).GlobalCatalogs | Select-Object -First 1
# 3) Interroger le GC sur l'ensemble de la forêt (aucun SearchBase requis)
Get-ADComputer -Server "GC://$gc" ` -LDAPFilter "(objectClass=computer)"`
-Credential $cred `
-Properties DNSHostName,OperatingSystem,LastLogonDate |
Select-Object Name, DNSHostName, OperatingSystem, LastLogonDate
Pourquoi « GC:// » ? Le préfixe demande à PowerShell d’utiliser le catalogue global comme source de vérité ; la recherche se propage alors à travers tous les domaines de la forêt. Évitez dans ce cas de fixer -SearchBase
sur un DN de domaine précis, sinon vous restreindrez la portée à ce seul domaine.
Approche pas‑à‑pas (détaillée)
Si vous préférez dérouler chaque étape, ou si vous devez expliquer la démarche, suivez ce guide détaillé.
Étape | Commandes & explications | Notes utiles |
---|---|---|
Charger le module AD | Import-Module ActiveDirectory | RSAT (ou ADWS) requis. Sur un serveur membre, le module est souvent déjà présent. |
Récupérer des identifiants sécurisés | $cred = Get-Credential # DOMAINE\utilisateur | Évite tout mot de passe en clair. Fonctionne même si votre machine n’est pas jointe au domaine. |
Localiser un contrôleur catalogue global | $gc = (Get-ADForest -Credential $cred).GlobalCatalogs | Select-Object -First 1 | Le GC voit tous les domaines de la forêt. Les ports usuels : 3268 (LDAP), 3269 (LDAPS). |
Définir (ou non) une base de recherche | # <Optionnel> Si vous utilisez "GC://", laissez PowerShell faire : # Pas de -SearchBase = recherche forêt entière. # Si vous ciblez un domaine spécifique, utilisez son DN : $rootDN = (Get-ADRootDSE -Server $gc -Credential $cred).defaultNamingContext | Avec GC:// , omettre -SearchBase garantit une portée « Entire Directory ». |
Lancer la recherche | # Recherche forêt entière (recommandé) Get-ADComputer -Server "GC://$gc" ` -LDAPFilter "(objectClass=computer)" ` -Credential $cred ` -Properties DNSHostName,OperatingSystem | Select-Object Name, DNSHostName, OperatingSystem # Variante ciblant un domaine précis (moins large) Get-ADComputer -LDAPFilter "(objectClass=computer)" ` -SearchBase $rootDN -SearchScope Subtree` -Server "$gc:3268" -Credential $cred | Select-Object Name, DNSHostName, OperatingSystem | Ajoutez un export si besoin : | Export-Csv .\Ordinateurs.csv -NoTypeInformation -Encoding UTF8 |
Points clés à retenir
- Port 3268 : force l’interrogation du catalogue global (cherche dans toute la forêt). Utilisez 3269 pour LDAPS si une couche TLS est exigée.
- Filtre LDAP : adaptez le critère :
(cn=*WEB*)
,(dNSHostName=*.corp.local)
,(operatingsystem=*Server 2022*)
, etc. - Évolutivité : filtrez côté serveur avec
-LDAPFilter
, limitez les attributs (-Properties
) et utilisez-ResultSetSize
si nécessaire. - Pas de privilèges élevés : des droits de lecture suffisent sur le GC ; nul besoin d’être admin du domaine.
Cheat‑sheet des filtres utiles
Objectif | Exemple de filtre LDAP | Remarques |
---|---|---|
Toutes les machines | (objectClass=computer) | Point de départ classique. |
Nom partiel | (&(objectClass=computer)(cn=*PROD*)) | Jokers * acceptés. |
Par OS | (&(objectClass=computer)(operatingsystem=*Server 2022*)) | operatingsystem est dans le GC. |
Hôtes joints (DNS) | (&(objectClass=computer)(dNSHostName=*.eu.corp)) | Utile pour des suffixes DNS par région. |
Dernière connexion récente | (&(objectClass=computer)(lastLogonTimestamp>=132669792000000000)) | Valeur en FILETIME. lastLogonTimestamp est répliqué (granularité ~14 jours). |
Machines désactivées | (&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=2)) | Test du flag ACCOUNTDISABLE. |
Script prêt à l’emploi
Le script ci‑dessous encapsule la recherche « Entire Directory » et gère la tolérance aux pannes entre plusieurs GC.
function Find-ADForestComputer {
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)]
[string] $LDAPFilter = '(objectClass=computer)',
```
[Parameter(Mandatory=$false)]
[string[]] $Properties = @('Name','DNSHostName','OperatingSystem','LastLogonDate','whenCreated','DistinguishedName'),
[Parameter(Mandatory=$false)]
[int] $ResultSetSize = 0, # 0 = illimité (par défaut)
[Parameter(Mandatory=$false)]
[int] $PageSize = 1000,
[Parameter(Mandatory=$false)]
[pscredential] $Credential
```
)
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
throw 'Le module ActiveDirectory (RSAT) est requis.'
}
Import-Module ActiveDirectory -ErrorAction Stop
if (-not $Credential) {
$Credential = Get-Credential -Message 'Identifiants avec droits de lecture (Read) sur la forêt'
}
$forest = Get-ADForest -Credential $Credential
$gcs = $forest.GlobalCatalogs | Sort-Object -Unique
if (-not $gcs) { throw 'Aucun Global Catalog trouvé dans la forêt.' }
foreach ($gc in $gcs) {
try {
Write-Verbose "Interrogation du GC $gc…"
$args = @{
Server = "GC://$gc"
LDAPFilter = $LDAPFilter
Credential = $Credential
Properties = $Properties
ResultSetSize = $ResultSetSize
ResultPageSize = $PageSize
}
$res = Get-ADComputer @args
if ($res) { return $res }
} catch {
Write-Warning "Échec sur $gc : $($_.Exception.Message)"
continue
}
}
Write-Verbose 'Aucun résultat ou tous les GC ont échoué.'
return @()
}
# Exemples d'usage :
# 1) Toutes les machines avec un nom partiel
# Find-ADForestComputer -LDAPFilter '(&(objectClass=computer)(cn=*WEB*))' | Select Name,DNSHostName
# 2) Windows Server 2022 uniquement + export
# Find-ADForestComputer -LDAPFilter '(&(objectClass=computer)(operatingsystem=*Server 2022*))' |
# Select Name,OperatingSystem,DNSHostName,LastLogonDate |
# Export-Csv .\Serveurs2022.csv -NoTypeInformation -Encoding UTF8
Comprendre le catalogue global
- Rôle : le GC héberge un index de tous les objets de tous les domaines de la forêt. Il réplique un sous‑ensemble d’attributs (Partial Attribute Set).
- Ports : 3268 (LDAP en clair dans le tunnel Kerberos/NTLM), 3269 (LDAPS/TLS). Dans des environnements stricts, préférez 3269 et assurez‑vous d’avoir une PKI et des certificats valides sur les DC.
- Limites : certains attributs ne sont pas présents dans le GC. Si vous avez besoin d’un attribut hors PAS, faites une deuxième lecture ciblée contre le DC du domaine de l’objet (en vous servant de son
DistinguishedName
ou duCanonicalName
).
Bonnes pratiques et compléments utiles
- Identifiants : n’écrivez jamais de mot de passe en clair. Utilisez
Get-Credential
, SecretManagement ou un coffre (Azure Key Vault, KeePass, etc.). - Droits : un compte standard avec la permission Read suffit. La lecture du GC ne nécessite pas de groupes privilégiés.
- Filtrer tôt : mettez le maximum de conditions dans
-LDAPFilter
pour réduire le volume renvoyé. - Limiter les attributs : ne demandez que ce dont vous avez besoin via
-Properties
. Exemple :DNSHostName,OperatingSystem,LastLogonDate
. - Paginer : pour de très grands annuaires, fixez
-ResultPageSize 1000
et utilisez-ResultSetSize
pour borner le nombre total de résultats. - Export : systématisez un export CSV encodé en UTF‑8 pour la réutilisation :
| Export-Csv .\InventaireAD.csv -NoTypeInformation -Encoding UTF8
. - Alternative sans module AD :
DirectorySearcher
(.NET) ou ADSI avec le préfixeGC://
, pratique sur des machines sans RSAT. - Compatibilité : avec RSAT ≥ 5.1,
-Server "GC://<hôte>"
est parfaitement supporté par les cmdletsGet-AD*
.
Exemples concrets
Inventaire rapide par système d’exploitation
Find-ADForestComputer -LDAPFilter '(&(objectClass=computer)(operatingsystem=*Windows Server*))' -Properties Name,OperatingSystem |
Group-Object OperatingSystem |
Sort-Object Count -Descending |
Select-Object @{n='OS';e={$_.Name}}, Count
Liste des ordinateurs inactifs depuis plus de 90 jours
Le GC réplique lastLogonTimestamp (granularité d’environ 9 à 14 jours). Exemple :
$days = 90
$threshold = (Get-Date).AddDays(-$days)
Find-ADForestComputer -LDAPFilter '(&(objectClass=computer)(lastLogonTimestamp=*))' -Properties Name,LastLogonDate,DistinguishedName |
Where-Object { $_.LastLogonDate -lt $threshold } |
Select-Object Name, LastLogonDate, DistinguishedName |
Export-Csv .\Ordinateurs_Inactifs.csv -NoTypeInformation -Encoding UTF8
Recherche par suffixe DNS
Find-ADForestComputer -LDAPFilter '(&(objectClass=computer)(dNSHostName=*.emea.corp.local))' |
Select-Object Name, DNSHostName
Alternative sans RSAT : .NET DirectorySearcher
Utile sur une machine sans module ActiveDirectory ; fonctionne avec le préfixe GC://
et supporte LDAPS si vous en avez besoin.
# Recherche forêt entière via DirectorySearcher
$cred = Get-Credential
# Connexion GC (LDAP 3268). Pour LDAPS, remplacez par "GC://:3269" et ajoutez AuthenticationTypes.SecureSocketsLayer.
$gcHost = (([DirectoryServices.ActiveDirectory.Forest]::GetForest()).GlobalCatalogs)[0].Name
$root = New-Object System.DirectoryServices.DirectoryEntry("GC://$gcHost", $cred.UserName, $cred.GetNetworkCredential().Password)
$ds = New-Object System.DirectoryServices.DirectorySearcher($root)
$ds.PageSize = 1000
$ds.Filter = "(&(objectClass=computer)(cn=*))"
$ds.PropertiesToLoad.AddRange(@('cn','dNSHostName','operatingSystem')) | Out-Null
$computers = $ds.FindAll() | ForEach-Object {
[PSCustomObject]@{
Name = $*.Properties['cn']
DNSHostName = $*.Properties['dnshostname']
OperatingSystem = $_.Properties['operatingsystem']
}
}
$computers | Format-Table -AutoSize
Performance : recommandations
- Filtrage serveur : préférez
-LDAPFilter
(ou le filtre DirectorySearcher) à unWhere-Object
côté client. - Attributs : ne demandez que l’essentiel pour éviter des paquets LDAP volumineux.
- Par lots : si vous enchaînez des enrichissements par domaine, stockez d’abord
Name
/DistinguishedName
, puis relisez les attributs non répliqués par domaine pour amortir le coût réseau. - Paralléliser prudemment : sur des forêts immenses, vous pouvez paralléliser par OU ou par domaine, en respectant les limites du DC et de votre réseau (ne saturez pas les GC).
Dépannage : erreurs fréquentes
Symptôme | Cause probable | Correctif |
---|---|---|
The server is not operational | Port 3268/3269 bloqué, résolutions DNS incorrectes | Ouvrir les ports vers les GC ; vérifier le DNS du poste et les FQDN des DC/GC |
Cannot find an object with identity ... | Filtre trop restrictif ou -SearchBase réduit la portée au seul domaine | Supprimer -SearchBase avec GC:// ; détendre le filtre |
Logon failure / Access denied | Compte sans droits de lecture, mot de passe erroné, horloge désynchronisée | Utiliser un compte de la forêt, corriger l’heure (Kerberos), tester avec nltest /dsgetdc: |
Attributs vides alors qu’ils existent | Attribut hors Partial Attribute Set du GC | Relire l’objet sur son DC de domaine avec -Server ciblé |
Bonnes pratiques de sécurité
- Chiffrement : si l’organisation l’exige, utilisez LDAPS (port 3269) et des certificats validés par la PKI interne.
- Moindre privilège : stockez vos scripts sans identifiants et demandez les secrets à l’exécution (
Get-Credential
) ou via un coffre. - Traçabilité : consignez l’usage et l’export (nom du fichier, horodatage) dans un log simple.
FAQ éclair
Est‑ce que le GC couvre des forêts différentes ? Non. Le GC est propre à une forêt. Pour des recherches inter‑forêts, interrogez chaque forêt (et son GC) séparément.
Dois‑je être membre d’un domaine ? Non, tant que vous fournissez des identifiants valides et que le réseau/DNS permet d’atteindre les GC.
Quelle est la différence entre -Filter
et -LDAPFilter
? -Filter
est la syntaxe PowerShell des cmdlets AD ; -LDAPFilter
parle nativement LDAP. Pour des cas avancés (bitwise, FILETIME, jokers), -LDAPFilter
est souvent plus précis.
Résumé
Pour reproduire « Entire Directory » en PowerShell : ciblez un hôte Global Catalog, utilisez -Server "GC://<hôte>"
, évitez de fixer -SearchBase
si vous souhaitez bien couvrir toute la forêt, filtrez côté serveur avec -LDAPFilter
et ne rapatriez que les attributs utiles. Les exemples et le script Find-ADForestComputer
ci‑dessus fournissent une base solide, sécurisée et performante pour vos inventaires et vos recherches.