Administrer plusieurs machines avec PowerShell Remoting
Objectifs de la séance
- Comprendre le rôle de WinRM dans l'administration à distance
- Ouvrir une session interactive sur une machine distante
- Exécuter une commande sur plusieurs machines à la fois avec
Invoke-Command - Transmettre une variable locale à un bloc de script distant avec
$using:
Notions théoriques
Pourquoi le remoting
Administrer vingt postes un par un, en se connectant en Bureau à distance sur chacun, ne passe pas à l'échelle. Le PowerShell Remoting permet d'exécuter des commandes sur une ou plusieurs machines distantes depuis un seul poste — l'équivalent Windows de ce que ssh permet de faire sous Linux, déjà pratiqué dans le cours Linux.
WinRM
Le remoting PowerShell s'appuie sur le service WinRM (Windows Remote Management), qui implémente le protocole WS-Management. Il écoute par défaut sur le port 5985 en HTTP et 5986 en HTTPS.
WinRM est activé par défaut sur Windows Server, mais pas sur Windows 10/11 en édition client : il faut l'activer explicitement.
Activer et tester WinRM
Enable-PSRemoting -Force
Get-Service WinRM
Test-WSMan -ComputerName SRV-FIC
Enable-PSRemoting démarre le service WinRM et crée automatiquement la règle de pare-feu nécessaire. Test-WSMan vérifie qu'une machine distante répond bien au protocole.
Hors domaine : TrustedHosts
Sur un réseau sans domaine Active Directory, l'authentification Kerberos n'est pas disponible entre les machines : il faut explicitement déclarer les machines de confiance.
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "SRV-FIC,PC01"
Ajouter une machine à TrustedHosts affaiblit la sécurité du poste d'administration : PowerShell ne vérifie alors plus l'identité de la machine distante par un mécanisme fort comme Kerberos, ce qui expose à une usurpation sur le réseau local. Cette configuration reste acceptable dans un labo isolé, mais doit être évitée en production au profit d'un vrai domaine Active Directory.
S'identifier
$cred = Get-Credential
Get-Credential ouvre une invite sécurisée demandant un compte et un mot de passe, stockés dans un objet réutilisable sur toutes les cmdlets acceptant un paramètre -Credential.
Session interactive : Enter-PSSession
Enter-PSSession -ComputerName SRV-FIC -Credential $cred
L'invite de commande devient [SRV-FIC]: PS> : toutes les commandes tapées s'exécutent désormais sur la machine distante, comme si vous étiez physiquement devant elle. Exit-PSSession referme la session. Cet usage convient à un dépannage ponctuel sur une seule machine.
Exécution scriptée : Invoke-Command
Invoke-Command -ComputerName SRV1, SRV2, PC01 -ScriptBlock {
Get-Service -Name Spooler
}
Invoke-Command exécute le bloc de script sur toutes les machines listées, en parallèle, et rapatrie les résultats. Chaque objet renvoyé porte une propriété supplémentaire, PSComputerName, qui indique de quelle machine il provient — indispensable pour ne pas perdre l'origine d'un résultat dans un rapport multi-machines.
La portée $using:
Une variable définie avant l'appel à Invoke-Command n'est pas automatiquement visible à l'intérieur du -ScriptBlock : celui-ci s'exécute dans un contexte totalement différent, sur une autre machine. Pour transmettre une variable locale, il faut la préfixer par $using: :
$nomService = "Spooler"
Invoke-Command -ComputerName PC01 -ScriptBlock {
Restart-Service -Name $using:nomService
}
Oublier $using: est une source d'erreur fréquente : la variable apparaît alors comme vide ou non définie côté distant.
Sessions persistantes
$session = New-PSSession -ComputerName SRV-FIC -Credential $cred
Invoke-Command -Session $session -ScriptBlock { Get-Process }
Remove-PSSession -Session $session
Une session persistante (New-PSSession) garde son état d'une commande à l'autre (variables, modules importés) et évite de rouvrir une connexion à chaque appel — utile quand plusieurs commandes doivent s'enchaîner sur la même machine.
Cibler une liste de machines
$machines = Get-Content -Path ".\machines.txt"
Invoke-Command -ComputerName $machines -ScriptBlock { Get-Service WinRM }
Une machine éteinte ou injoignable provoque une erreur qui, sans précaution, interromprait le traitement des autres — nous verrons à la séance suivante comment gérer proprement ce cas avec try/catch.
Exemple pratique
# Script : Get-InventairePoste.ps1
# Role : collecter un inventaire materiel sur plusieurs machines du parc
$machines = Get-Content -Path ".\machines.txt"
$cred = Get-Credential -Message "Compte administrateur du parc"
$inventaire = Invoke-Command -ComputerName $machines -Credential $cred -ScriptBlock {
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$disque = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='C:'"
[PSCustomObject]@{
Systeme = $os.Caption
MemoireGo = [math]::Round($os.TotalVisibleMemorySize / 1MB, 1)
DisqueLibreGo = [math]::Round($disque.FreeSpace / 1GB, 1)
Demarrage = $os.LastBootUpTime
}
}
$inventaire | Select-Object PSComputerName, Systeme, MemoireGo, DisqueLibreGo | Format-Table
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Si vous ne disposez pas d'une deuxième machine Windows sur le même réseau, le remoting fonctionne aussi sur la machine elle-même : après Enable-PSRemoting -Force, essayez Enter-PSSession -ComputerName localhost ou Invoke-Command -ComputerName localhost, $env:COMPUTERNAME -ScriptBlock { Get-Service WinRM }. Cela suffit pour observer le mécanisme.
Avec deux machines virtuelles sur le même réseau (voir le cours Virtualisation), reproduisez l'exemple pratique en ciblant réellement une seconde VM avec -ComputerName.
Les étapes ci-dessous sont conçues pour être réussies par complétion de code, sans dépendre d'une exécution réelle : une capture d'écran illustrant le résultat attendu est fournie par votre enseignant.
Étape 1 — Vérifier que WinRM répond
Vérifier qu'une machine répond avant de l'inclure dans un traitement en masse évite qu'une seule machine injoignable ne bloque tout le script.
Étape 2 — Exécuter une commande sur plusieurs machines
Invoke-Command avec une liste de machines exécute le traitement en parallèle sur toutes les cibles, bien plus rapide qu'une boucle qui se connecterait machine par machine.
Étape 3 — Transmettre une variable locale au bloc distant
Toute variable définie avant Invoke-Command et utilisée à l'intérieur du -ScriptBlock doit être préfixée par $using:, sans quoi elle est invisible côté distant.
Étape 4 — Identifier la machine d'origine de chaque résultat
Dans un rapport multi-machines, conservez toujours PSComputerName : sans elle, impossible de savoir quelle ligne concerne quelle machine.