PowerShell Remoting (WinRM) : administrer plusieurs Windows Server

PowerShell Remoting (WinRM) : administrer plusieurs Windows Server

Pilotez plusieurs serveurs Windows à distance avec PowerShell Remoting. Exécutez des scripts, installez des paquets, récupérez des informations sur un parc complet depuis un seul poste, sans RDP.

Introduction

Gérer 5 serveurs Windows en se connectant en RDP à chacun est inefficace. PowerShell Remoting (basé sur WinRM, Windows Remote Management) permet :

  • Exécuter une commande sur 10 serveurs en parallèle
  • Récupérer des infos depuis un script centralisé
  • Installer des mises à jour sur tout un parc
  • Auditer les configurations

C'est l'équivalent SSH du monde Windows : la base du sysadmin Windows moderne.

Prérequis

  • 2+ Windows Server 2019/2022/2025 (ou Windows 10/11 Pro)
  • Un user avec privilèges admin sur tous les serveurs
  • Connectivité réseau entre les machines (TCP 5985 HTTP ou 5986 HTTPS)

Étape 1 : Activer WinRM sur les serveurs cibles

Sur chaque serveur cible (PowerShell en admin) :

Enable-PSRemoting -Force

Cette commande :

  • Démarre le service WinRM
  • Configure le firewall pour TCP 5985 (HTTP)
  • Crée un listener WinRM
  • Active la délégation pour les admins

Vérifiez :

Get-Service WinRM
Get-PSSessionConfiguration
winrm enumerate winrm/config/listener

Étape 2 : Tester la connexion depuis le serveur de management

Depuis votre poste admin (PowerShell) :

Test-WSMan -ComputerName srv-app-01

Sortie attendue :

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/...
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0

Si erreur, voir la section dépannage.

Étape 3 : Configurer les TrustedHosts (pour hors domaine)

Si vos serveurs ne sont pas dans un domaine AD, vous devez ajouter les cibles aux TrustedHosts du client :

# Ajouter un serveur spécifique
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "srv-app-01" -Force

# Ajouter plusieurs
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "srv-app-01,srv-db-01,srv-web-01" -Force

# Tout autoriser (DEV uniquement)
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force

# Vérifier
Get-Item WSMan:\localhost\Client\TrustedHosts

Étape 4 : Exécuter une commande à distance

Une commande, un serveur

Invoke-Command -ComputerName srv-app-01 -ScriptBlock { 
    Get-ComputerInfo | Select-Object CsName, OsName, OsVersion 
} -Credential (Get-Credential)

Une fenêtre demande les credentials. Pour automatiser :

$cred = Get-Credential
Invoke-Command -ComputerName srv-app-01 -ScriptBlock { hostname } -Credential $cred

Une commande sur plusieurs serveurs en parallèle

$servers = @("srv-app-01", "srv-app-02", "srv-db-01")

Invoke-Command -ComputerName $servers -ScriptBlock {
    Get-Service | Where-Object Status -eq 'Running' | Measure-Object | Select-Object Count
} -Credential $cred

Sortie : Get-Service exécuté en parallèle sur les 3 serveurs.

Étape 5 : Sessions persistantes (PSSession)

Pour lancer plusieurs commandes sans réauthentifier à chaque fois :

$session = New-PSSession -ComputerName srv-app-01 -Credential $cred

Invoke-Command -Session $session -ScriptBlock { Get-Service WinRM }
Invoke-Command -Session $session -ScriptBlock { Get-Process | Select -First 5 }

# Quand fini
Remove-PSSession $session

Session interactive (équivalent SSH)

Enter-PSSession -ComputerName srv-app-01 -Credential $cred

Le prompt change : [srv-app-01]: PS C:\Users\admin\>. Tapez Windows comme si vous étiez en local. Pour sortir :

Exit-PSSession

Étape 6 : Copier des fichiers via WinRM

$session = New-PSSession -ComputerName srv-app-01 -Credential $cred

# Push (local → remote)
Copy-Item -Path "C:\scripts\install.ps1" -Destination "C:\temp\install.ps1" -ToSession $session

# Pull (remote → local)
Copy-Item -Path "C:\logs\app.log" -Destination "C:\local-logs\srv-app-01_app.log" -FromSession $session

Remove-PSSession $session

Étape 7 : Scripts d'administration courants

Inventaire complet du parc

$servers = @("srv-app-01", "srv-app-02", "srv-db-01")
$cred = Get-Credential

$inventory = Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
    [PSCustomObject]@{
        Hostname    = $env:COMPUTERNAME
        OS          = (Get-CimInstance Win32_OperatingSystem).Caption
        Version     = (Get-CimInstance Win32_OperatingSystem).Version
        CPU         = (Get-CimInstance Win32_Processor).Name
        Cores       = (Get-CimInstance Win32_Processor).NumberOfCores
        RAM_GB      = [math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
        Disk_C_Free = [math]::Round((Get-PSDrive C).Free / 1GB, 2)
        Uptime      = (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
        LastUpdate  = (Get-HotFix | Sort-Object InstalledOn -Descending | Select -First 1).InstalledOn
    }
}

$inventory | Format-Table -AutoSize
$inventory | Export-Csv "C:\inventaire-parc.csv" -NoTypeInformation

Installer une mise à jour Windows partout

Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
    Install-PackageProvider -Name NuGet -Force | Out-Null
    Install-Module PSWindowsUpdate -Force | Out-Null
    Import-Module PSWindowsUpdate
    Get-WindowsUpdate -AcceptAll -Install -AutoReboot
}

Service status sur tout le parc

Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
    Get-Service -Name "Spooler","WinRM","DNS" | 
        Select-Object @{N='Server';E={$env:COMPUTERNAME}}, Name, Status
} | Format-Table -AutoSize

Lister les utilisateurs actifs

Invoke-Command -ComputerName $servers -Credential $cred -ScriptBlock {
    query user
}

Redémarrer plusieurs serveurs

Restart-Computer -ComputerName $servers -Credential $cred -Force -Wait -For PowerShell -Timeout 600

Étape 8 : Sécuriser avec HTTPS (WinRM sur SSL)

Par défaut WinRM utilise HTTP non chiffré (port 5985). En production, chiffrez avec HTTPS (port 5986).

Générer un certificat self-signed (test) ou utiliser une CA

$cert = New-SelfSignedCertificate -DnsName "srv-app-01" -CertStoreLocation "Cert:\LocalMachine\My"

Créer le listener HTTPS

$thumbprint = $cert.Thumbprint
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -CertificateThumbprint $thumbprint -Force

Ouvrir le firewall

New-NetFirewallRule -DisplayName "WinRM HTTPS" -Protocol TCP -LocalPort 5986 -Action Allow

Désactiver HTTP

Get-ChildItem WSMan:\localhost\Listener | Where-Object { $_.Keys -match "HTTP$" } | Remove-Item -Recurse
Disable-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)"

Se connecter en HTTPS

Enter-PSSession -ComputerName srv-app-01 -UseSSL -Credential $cred

Pour un cert self-signed, ajoutez -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck).

Étape 9 : JEA (Just Enough Administration)

Pour donner à un user le droit d'exécuter certaines commandes sans le rendre admin :

# Créer une config session
New-PSSessionConfigurationFile -Path C:\Config\WebAdmin.pssc -SessionType RestrictedRemoteServer -RoleDefinitions @{'DOMAIN\WebOps' = @{ RoleCapabilities = 'WebOpsRole' }}

# Créer la role capability
New-PSRoleCapabilityFile -Path C:\Config\WebOpsRole.psrc -VisibleCmdlets @('Restart-Service', 'Get-Service', 'Get-IISSite')

# Enregistrer
Register-PSSessionConfiguration -Path C:\Config\WebAdmin.pssc -Name WebAdmin

Le user DOMAIN\WebOps peut maintenant se connecter à cette session restreinte et utiliser uniquement les cmdlets autorisés.

Étape 10 : Group Policy pour activer WinRM en masse

Pour activer WinRM sur tous les serveurs d'un domaine AD :

  1. GPMC.msc → créer/éditer une GPO
  2. Configuration ordinateur → Stratégies → Modèles d'administration → Composants Windows → Gestion de la communication à distance Windows (WinRM)
  3. Activer :
    • WinRM Service : Allow remote server management through WinRM = Enabled
    • Windows Firewall : ouvrir les ports 5985/5986
  4. Lier la GPO à l'OU des serveurs

gpupdate /force sur les serveurs pour appliquer immédiatement.

Dépannage

"WinRM cannot complete the operation"

Le service WinRM n'est pas démarré sur la cible :

Get-Service WinRM
Start-Service WinRM
Set-Service WinRM -StartupType Automatic

"Access is denied"

User pas dans le groupe Remote Management Users ou pas admin. Ajoutez :

Add-LocalGroupMember -Group "Remote Management Users" -Member "DOMAIN\username"

"The WinRM client cannot process the request"

Ajoutez la cible aux TrustedHosts (voir étape 3) ou utilisez HTTPS.

Firewall bloque

Get-NetFirewallRule | Where-Object DisplayName -like "*Windows Remote Management*" | Enable-NetFirewallRule

Timeout sur des commandes longues

$opt = New-PSSessionOption -OperationTimeout 600000  # 10 min
Invoke-Command -ComputerName srv -ScriptBlock {...} -SessionOption $opt

Commandes utiles

# Lister les sessions actives
Get-PSSession

# Killer toutes les sessions
Get-PSSession | Remove-PSSession

# Voir la config WinRM
winrm get winrm/config
winrm get winrm/config/listener

# Tester depuis CLI
winrm identify -r:http://srv-app-01:5985

# Reset complet WinRM
winrm quickconfig -force
winrm invoke restore winrm/config

# Lister les listeners
winrm enumerate winrm/config/listener

# Récupérer le journal WinRM
Get-WinEvent -LogName "Microsoft-Windows-WinRM/Operational" -MaxEvents 50

Conclusion

Avec PowerShell Remoting maîtrisé :

  • Vous administrez 1 ou 100 serveurs avec le même effort
  • Les scripts s'exécutent en parallèle automatiquement
  • Vous évitez les sessions RDP (plus rapide, audit-friendly)

Pour aller plus loin :

  • Combinez avec DSC (Desired State Configuration) pour de l'infra as code
  • Utilisez Azure Arc pour gérer des serveurs Windows on-prem depuis le cloud
  • Migrez vers Ansible ou Puppet pour de l'orchestration multi-OS

Ressources

Rejoignez notre serveur communautaire Discord

Pour toute question, suggestion ou simplement pour discuter avec la communauté, rejoignez-nous sur Discord !

900+Membres