Chapitre 916 minIntermédiaire
PowerShell pour administrer Active Directory et Windows Server
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- trouver et utiliser les cmdlets des modules d'administration ;
- interroger l'annuaire avec -Filter, -SearchBase et -Properties ;
- modifier des objets en masse sans risque grâce à -WhatIf ;
- administrer à distance avec WinRM et sécuriser l'usage de PowerShell.
4 exercices corrigés
PowerShell est le langage de commande et de script de Microsoft : chaque rôle de Windows Server (Active Directory, DNS, DHCP, partages, GPO) fournit un module de commandes, les cmdlets, qui permettent de tout faire et surtout de l'automatiser. Ce chapitre donne les commandes d'administration à connaître, la façon d'interroger et de modifier l'annuaire en masse, l'administration à distance avec WinRM et SSH, et les règles de sécurité de PowerShell. Les bases du langage (variables, conditions, boucles, fonctions) sont dans le cours Scripts PowerShell et Bash, qui écrit aussi un script complet de création de comptes depuis un CSV.
Windows PowerShell 5.1 et PowerShell 7
Deux PowerShell cohabitent sur un serveur :
| Windows PowerShell 5.1 | PowerShell 7 | |
|---|---|---|
| Présent | Installé avec Windows, sur tous les serveurs | À installer (MSI, winget install Microsoft.PowerShell) |
| Exécutable | powershell.exe | pwsh.exe |
| Évolution | Corrigé, plus développé | Développé activement, multiplateforme (Windows, Linux, macOS) |
| Modules d'administration Windows | Tous | La plupart directement, les autres par une couche de compatibilité |
Pour l'administration de Windows Server, les deux conviennent. Les commandes de ce cours fonctionnent dans Windows PowerShell 5.1, celui que tu trouveras sur n'importe quel serveur, y compris le jour de l'examen. Windows PowerShell 2.0, très ancien et sans les protections récentes, a été retiré de Windows Server 2025.
Les modules d'administration
| Module | Ce qu'il administre | Exemples de cmdlets |
|---|---|---|
ActiveDirectory | Comptes, groupes, OU, ordinateurs, domaine | Get-ADUser, New-ADGroup, Search-ADAccount |
GroupPolicy | GPO | New-GPO, New-GPLink, Get-GPOReport |
DnsServer | Serveur DNS | Add-DnsServerResourceRecordA, Get-DnsServerZone |
DhcpServer | Serveur DHCP | Add-DhcpServerv4Scope, Get-DhcpServerv4Lease |
SmbShare | Partages | New-SmbShare, Get-SmbSession |
NetTCPIP, DnsClient | Configuration réseau | New-NetIPAddress, Resolve-DnsName |
ServerManager | Rôles et fonctionnalités | Install-WindowsFeature |
DFSN, DFSR | Espaces de noms et réplication DFS | New-DfsnFolder, Get-DfsrBacklog |
LAPS | Windows LAPS | Get-LapsADPassword |
Sur un contrôleur de domaine, les modules sont installés avec le rôle. Sur un poste d'administration, ils viennent avec les outils RSAT (chapitre 1).
Trouver la bonne commande
Les cmdlets suivent la forme Verbe-Nom, avec des verbes normalisés : Get (lire), New (créer), Set (modifier), Remove (supprimer), Add (ajouter à un ensemble), Enable et Disable, Install, Test. Si tu sais ce que tu veux faire, tu sais à peu près comment s'appelle la commande.
Get-Command -Module ActiveDirectory -Verb Get # les commandes de lecture du module AD
Get-Command *DnsServerResourceRecord* # tout ce qui touche aux enregistrements DNS
Get-Help New-ADUser -Examples # des exemples prêts à adapter
Get-Help New-ADUser -Parameter AccountExpirationDate
Update-Help -UICulture en-US # télécharger l'aide complète (en anglais)
Le pipeline : des objets, pas du texte
Une cmdlet renvoie des objets, avec des propriétés. Le pipeline | transmet ces objets à la commande suivante, qui les filtre, les trie ou les exporte, sans jamais découper du texte.
# Les 5 utilisateurs les plus récemment créés, avec leur date de création
Get-ADUser -Filter * -Properties whenCreated |
Sort-Object whenCreated -Descending |
Select-Object -First 5 Name, SamAccountName, whenCreated
# Nombre de comptes par service
Get-ADUser -Filter * -Properties Department |
Group-Object Department | Select-Object Name, Count
# Exporter un annuaire simple pour l'accueil
Get-ADUser -Filter "Enabled -eq 'True'" -Properties Department, OfficePhone |
Select-Object Name, Department, OfficePhone |
Export-Csv ".\annuaire.csv" -Delimiter ";" -NoTypeInformation -Encoding UTF8
Get-Member affiche les propriétés et méthodes d'un objet : Get-ADUser jlefevre -Properties * | Get-Member.
Interroger l'annuaire
Les cmdlets Get-AD* acceptent trois façons de cibler.
-Identitypour un objet précis : son nom d'ouverture de session, son DN, son SID ou son GUID.-Filterpour une recherche, avec une syntaxe proche de PowerShell :-eq,-ne,-like,-gt,-lt,-and,-or. Les valeurs texte se mettent entre apostrophes.-LDAPFilterpour une recherche en syntaxe LDAP, la même que celle des applications :(&(objectClass=user)(department=Comptabilité)).
-SearchBase limite la recherche à une OU, et -Properties demande les attributs qui ne sont pas renvoyés par défaut. Par défaut, Get-ADUser ne renvoie qu'une dizaine de propriétés (nom, DN, SID, état, UPN…) : pour filtrer ou afficher le service, le téléphone ou la dernière connexion, il faut les demander.
# Un compte, avec tous ses attributs
Get-ADUser -Identity "jlefevre" -Properties *
# Les comptes d'une OU
Get-ADUser -Filter * -SearchBase "OU=Comptabilite,OU=Utilisateurs,OU=Sorbier,DC=ad,DC=sorbier,DC=example"
# Les comptes du service Comptabilité, où qu'ils soient
Get-ADUser -Filter "Department -eq 'Comptabilité'" -Properties Department
# Les comptes dont le mot de passe n'expire jamais (à justifier un par un)
Get-ADUser -Filter 'PasswordNeverExpires -eq $true' -Properties PasswordNeverExpires |
Select-Object Name, SamAccountName
# Les comptes désactivés, les comptes expirés, les comptes verrouillés
Search-ADAccount -AccountDisabled -UsersOnly
Search-ADAccount -AccountExpired
Search-ADAccount -LockedOut
# Les membres d'un groupe, groupes imbriqués dépliés, et les groupes d'un compte
Get-ADGroupMember -Identity "Admins du domaine" -Recursive | Select-Object Name, ObjectClass
Get-ADPrincipalGroupMembership -Identity "kmorel" | Select-Object Name
# Les ordinateurs sous Windows 10 encore présents dans le parc
Get-ADComputer -Filter "OperatingSystem -like '*Windows 10*'" -Properties OperatingSystem |
Select-Object Name, OperatingSystem
Modifier en masse, prudemment
Les cmdlets Set-, Move-, Enable-, Disable- acceptent les objets venus du pipeline. Une seule ligne peut modifier mille comptes : c'est sa force et son danger.
# Corriger l'attribut Company de tous les comptes de l'OU Sorbier
Get-ADUser -Filter * -SearchBase "OU=Utilisateurs,OU=Sorbier,DC=ad,DC=sorbier,DC=example" |
Set-ADUser -Company "Sorbier Logistique" -WhatIf
# Réinitialiser un mot de passe et forcer son changement
Set-ADAccountPassword -Identity "jlefevre" -Reset -NewPassword (Read-Host -AsSecureString "Nouveau mot de passe")
Set-ADUser -Identity "jlefevre" -ChangePasswordAtLogon $true
# Déplacer tous les postes restés dans le conteneur Computers vers l'OU des postes
Get-ADComputer -Filter * -SearchBase "CN=Computers,DC=ad,DC=sorbier,DC=example" |
Move-ADObject -TargetPath "OU=Postes-fixes,OU=Ordinateurs,OU=Sorbier,DC=ad,DC=sorbier,DC=example"
Un script complet : les comptes inactifs
Chaque mois, Chloé Lambert doit désactiver les comptes utilisateurs inactifs depuis plus de 90 jours, sauf les comptes de service, et garder une trace. Le script :
# Désactive les comptes inactifs depuis plus de 90 jours et produit un rapport.
param(
[int]$Jours = 90,
[string]$SearchBase = "OU=Utilisateurs,OU=Sorbier,DC=ad,DC=sorbier,DC=example",
[switch]$Appliquer # sans ce paramètre, le script ne fait que lister
)
$limite = (Get-Date).AddDays(-$Jours)
$comptes = Get-ADUser -Filter "Enabled -eq 'True'" -SearchBase $SearchBase -Properties LastLogonDate, whenCreated |
Where-Object {
# Jamais connecté : on regarde la date de création ; sinon la dernière connexion
$reference = if ($_.LastLogonDate) { $_.LastLogonDate } else { $_.whenCreated }
$reference -lt $limite -and $_.SamAccountName -notlike "svc-*"
}
foreach ($c in $comptes) {
if ($Appliquer) {
Disable-ADAccount -Identity $c
Set-ADUser -Identity $c -Description "Désactivé le $(Get-Date -Format 'dd/MM/yyyy') : inactif depuis $Jours jours"
}
}
$comptes | Select-Object Name, SamAccountName, LastLogonDate, whenCreated |
Export-Csv ".\inactifs-$(Get-Date -Format 'yyyy-MM-dd').csv" -Delimiter ";" -NoTypeInformation -Encoding UTF8
Write-Output "$($comptes.Count) compte(s) inactif(s) depuis $Jours jours. Appliqué : $Appliquer"
On le lance d'abord sans -Appliquer (.\Desactiver-Inactifs.ps1), on lit le rapport, puis avec (.\Desactiver-Inactifs.ps1 -Appliquer). Le comportement par défaut ne modifie rien : un script d'administration doit être sûr quand on le lance sans réfléchir. Une tâche planifiée peut ensuite l'exécuter chaque mois avec un compte de service géré (chapitre 13).
Administrer à distance : WinRM et SSH
Avec WinRM. La communication à distance PowerShell (remoting) passe par le service WinRM, sur le port TCP 5985 (HTTP, mais le contenu est chiffré par Kerberos dans un domaine) ou 5986 (HTTPS). Elle est activée par défaut sur Windows Server ; sur un poste, Enable-PSRemoting l'active.
# Une session interactive sur un serveur, comme en SSH
Enter-PSSession -ComputerName srv-ad2
# ... commandes exécutées sur srv-ad2 ...
Exit-PSSession
# Une commande sur plusieurs serveurs à la fois : l'état des services clés des contrôleurs
Invoke-Command -ComputerName srv-ad, srv-ad2 -ScriptBlock {
Get-Service NTDS, DNS, Netlogon, Kdc, DFSR | Select-Object Name, Status
}
Invoke-Command renvoie les résultats avec une propriété PSComputerName qui indique quel serveur a répondu. Dans un domaine, l'authentification se fait par Kerberos : pas de mot de passe à saisir si ton compte a les droits sur la machine distante.
Avec SSH. PowerShell 7 sait aussi passer par SSH (Enter-PSSession -HostName srv-ad2 -UserName "SORBIER\adm0-amercier"). C'est utile pour administrer des serveurs Linux et Windows de la même façon, et Windows Server 2025 installe le serveur OpenSSH par défaut (il reste à activer le service).
Le principe du moindre privilège à distance. Le mécanisme JEA (Just Enough Administration) crée des points de connexion PowerShell restreints : un technicien du support peut s'y connecter pour redémarrer le service d'impression ou consulter des journaux, sans être administrateur du serveur. Toutes ses commandes sont journalisées. C'est l'alternative propre au « on lui donne les droits d'administrateur, ce sera plus simple ».
Sécuriser l'usage de PowerShell
PowerShell est aussi un outil prisé des attaquants : il est présent partout, puissant et légitime. Les mesures de base :
- la stratégie d'exécution (
Get-ExecutionPolicy) :RemoteSignedpar défaut sur Windows Server, elle refuse les scripts téléchargés non signés. Ce n'est pas une barrière de sécurité (un utilisateur peut la contourner pour sa session) mais un garde-fou contre l'exécution accidentelle ; - la journalisation : activer par GPO la journalisation des blocs de script (Modèles d'administration > Composants Windows > Windows PowerShell > Activer la journalisation de blocs de scripts PowerShell), qui enregistre le code réellement exécuté dans l'événement 4104 du journal
Microsoft-Windows-PowerShell/Operational, même s'il était masqué ; et la transcription, qui garde un fichier texte de chaque session (chapitre 14) ; - pas de mot de passe en clair dans un script : utilise
Get-Credential,Read-Host -AsSecureString, un compte de service géré, ou un coffre (moduleMicrosoft.PowerShell.SecretManagement) ; - la signature des scripts d'administration, avec un certificat de signature de code délivré par l'autorité de certification de l'entreprise (chapitre 16), et la stratégie
AllSignedsur les serveurs sensibles ; - le mode de langage restreint, imposé automatiquement quand une stratégie de contrôle des applications (App Control for Business, AppLocker) est active, limite ce qu'un script non approuvé peut faire.
Exercices
Exercice 1 sur 4 Écrire la commande Réussi À revoir
Écris la commande PowerShell qui :
- affiche le nom et le service de tous les comptes de l'OU
Commercial; - liste les comptes verrouillés ;
- ajoute
irouxetlfontaineau groupeGG_Commerciaux; - affiche les membres de
DL_Compta_M, groupes imbriqués dépliés ; - crée l'OU
DepartssousOU=Sorbier, protégée contre la suppression accidentelle ; - affiche la liste des GPO avec leur date de dernière modification.
Voir la réponseMasquer la réponse de l'exercice 1
# 1.
Get-ADUser -Filter * -SearchBase "OU=Commercial,OU=Utilisateurs,OU=Sorbier,DC=ad,DC=sorbier,DC=example" `
-Properties Department | Select-Object Name, Department
# 2.
Search-ADAccount -LockedOut -UsersOnly
# 3.
Add-ADGroupMember -Identity "GG_Commerciaux" -Members "iroux", "lfontaine"
# 4.
Get-ADGroupMember -Identity "DL_Compta_M" -Recursive
# 5.
New-ADOrganizationalUnit -Name "Departs" -Path "OU=Sorbier,DC=ad,DC=sorbier,DC=example" -ProtectedFromAccidentalDeletion $true
# 6.
Get-GPO -All | Select-Object DisplayName, ModificationTime
Pour le 1, sans -Properties Department, la colonne Department serait vide : la propriété n'est pas renvoyée par défaut. Pour le 5, la protection est d'ailleurs activée par défaut par New-ADOrganizationalUnit : la préciser documente l'intention.
Tu avais trouvé ?
Exercice 2 sur 4 Trouve les erreurs Réussi À revoir
Chacune de ces commandes ne donne pas le résultat attendu. Explique pourquoi et corrige.
Get-ADUser -Filter "Department -eq Comptabilité"Get-ADUser -Filter * | Where-Object Department -eq "Comptabilité"Get-ADUser -Filter * -SearchBase "OU=Sorbier,DC=sorbier,DC=example"
Voir la réponseMasquer la réponse de l'exercice 2
- La valeur texte n'est pas entre apostrophes : le filtre est invalide et la commande échoue. Correction :
-Filter "Department -eq 'Comptabilité'", avec-Properties Departmentsi l'on veut afficher le service. Departmentn'est pas renvoyé par défaut : il est vide pour tous les objets, etWhere-Objectne garde rien. Il faut-Properties Department. Mieux encore, filtrer côté serveur avec-Filter "Department -eq 'Comptabilité'", qui ne rapatrie que les comptes voulus au lieu de tout l'annuaire.- Le DN est faux : le domaine est
ad.sorbier.example, doncDC=ad,DC=sorbier,DC=example. La commande renvoie une erreur « répertoire introuvable ». Correction :-SearchBase "OU=Sorbier,DC=ad,DC=sorbier,DC=example".
Tu avais trouvé ?
Exercice 3 sur 4 Le tour des contrôleurs Réussi À revoir
Écris une commande qui affiche, pour srv-ad et srv-ad2, l'espace libre du disque C: en Go et l'état du service DNS, en une seule exécution depuis ton poste d'administration.
Voir la réponseMasquer la réponse de l'exercice 3
Invoke-Command -ComputerName srv-ad, srv-ad2 -ScriptBlock {
[pscustomobject]@{
LibreGo = [math]::Round((Get-PSDrive C).Free / 1GB, 1)
DNS = (Get-Service DNS).Status
}
} | Select-Object PSComputerName, LibreGo, DNS
La commande s'exécute en parallèle sur les deux serveurs par WinRM ; PSComputerName, ajouté automatiquement, dit quel serveur a répondu. Get-PSDrive C donne l'espace libre en octets, divisé par 1GB (1 073 741 824).
Tu avais trouvé ?
Exercice 4 sur 4 Sécuriser un script trouvé sur le serveur Réussi À revoir
Sur srv-gestion, tu trouves une tâche planifiée qui lance chaque nuit ce script, avec le compte Administrateur du domaine :
$mdp = ConvertTo-SecureString "Sorbier2026!" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("SORBIER\Administrateur", $mdp)
Copy-Item "D:\Export\*.csv" "\\srv-fichiers\Compta\Imports" -Credential $cred
Relève trois problèmes et propose une solution pour chacun.
Voir la réponseMasquer la réponse de l'exercice 4
- Mot de passe en clair dans le script : quiconque lit le fichier obtient le mot de passe de l'administrateur du domaine. Solution : ne jamais stocker de mot de passe dans un script ; utiliser un compte de service géré (gMSA) pour la tâche planifiée, dont le mot de passe n'est connu de personne.
- Compte Administrateur du domaine pour copier des fichiers : violation du moindre privilège ; si le serveur est compromis, tout le domaine l'est. Solution : un compte dédié qui n'a que le droit d'écrire dans
Compta\Imports(par un groupe de domaine localDL_Compta_Imports_M). - Mot de passe faible et prévisible (nom de l'entreprise, année, point d'exclamation), probablement réutilisé ailleurs. Solution : changer immédiatement ce mot de passe, puisqu'il a été exposé, et vérifier dans les journaux qu'il n'a pas été utilisé de façon anormale.
On peut ajouter : Copy-Item refuse le paramètre -Credential sur le système de fichiers (seul New-PSDrive l'accepte). Ce script échoue donc probablement chaque nuit sans que personne ne le remarque : une tâche planifiée doit être surveillée, et son résultat journalisé.
Tu avais trouvé ?