PowerShell Remoting et le registre a distance¶
Ce que vous allez apprendre
- Configurer WinRM et les prerequis pour le remoting PowerShell
- Utiliser Invoke-Command pour lire, ecrire et supprimer des cles de registre a distance
- Exploiter les sessions CIM pour acceder au registre via StdRegProv
- Orchestrer des operations massives sur des centaines de machines en parallele
- Maitriser les mecanismes de securite : Kerberos, CredSSP, JEA
Configuration de WinRM et prerequis¶
sequenceDiagram
participant Admin as Poste Admin
participant WinRM as Service WinRM
participant Target as Machine Cible
Admin->>WinRM: Invoke-Command (port 5985)
WinRM->>Target: Authentification Kerberos / NTLM
Target-->>WinRM: Ticket de session
WinRM->>Target: Envoi du ScriptBlock chiffre
Target-->>WinRM: Execution locale
WinRM-->>Admin: Resultat deserialise
style Admin fill:#bd93f9,color:#fff
style WinRM fill:#8be9fd,color:#000
style Target fill:#50fa7b,color:#000 Vous devez deployer un correctif de registre sur 200 postes de travail, mais aucun d'entre eux n'accepte de connexion distante. Avant toute operation de registre a distance, WinRM doit etre configure sur chaque machine cible.
Activer PSRemoting sur la machine cible¶
WinRM has been updated to receive requests.
WinRM service type changed successfully.
WinRM service started.
WinRM has been updated for remote management.
WinRM firewall exception configured.
La commande Enable-PSRemoting realise quatre operations en une seule passe : elle demarre le service WinRM, le configure en demarrage automatique, cree les endpoints de session PowerShell et ouvre les regles de pare-feu necessaires.
Verifier l'etat de WinRM¶
# Check WinRM service status and listener configuration
Get-Service WinRM
winrm enumerate winrm/config/listener
Status Name DisplayName
------ ---- -----------
Running WinRM Windows Remote Management (WS-Manag...
Listener
Address = *
Transport = HTTP
Port = 5985
Hostname
Enabled = true
Configurer les TrustedHosts (environnement workgroup)¶
En domaine Active Directory, Kerberos gere l'authentification mutuelle. En workgroup, vous devez explicitement autoriser les machines distantes.
# Add specific machines to TrustedHosts
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "SRV01,SRV02,192.168.1.0/24" -Force
# Verify the configuration
Get-Item WSMan:\localhost\Client\TrustedHosts
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Client
Type Name SourceOfValue Value
---- ---- ------------- -----
System.String TrustedHosts SRV01,SRV02,192.168.1.0/24
TrustedHosts ne remplace pas le chiffrement
Ajouter une machine aux TrustedHosts signifie que vous acceptez de vous authentifier sans verification Kerberos mutuelle. Le trafic reste chiffre via WinRM, mais vous n'avez aucune garantie que le serveur est bien celui qu'il pretend etre.
Regles de pare-feu associees¶
WinRM utilise le port TCP 5985 (HTTP) ou 5986 (HTTPS). Voici les regles de pare-feu correspondantes dans le registre :
# Verify firewall rules for WinRM
Get-NetFirewallRule -DisplayName "*Windows Remote Management*" |
Select-Object DisplayName, Enabled, Direction, Action
DisplayName Enabled Direction Action
----------- ------- --------- ------
Windows Remote Management (HTTP-In) True Inbound Allow
Windows Remote Management (HTTP-In) True Inbound Allow
Activation a grande echelle via GPO¶
Plutot que d'activer WinRM machine par machine, utilisez une GPO :
| Parametre GPO | Chemin | Valeur |
|---|---|---|
| Autoriser la gestion a distance | Computer Configuration\Policies\Administrative Templates\Windows Components\Windows Remote Management (WinRM)\WinRM Service | Active, filtre IPv4 : * |
| Service WinRM demarrage auto | Computer Configuration\Policies\Windows Settings\Security Settings\System Services | WinRM : Automatique |
| Regle pare-feu | Computer Configuration\Policies\Windows Settings\Security Settings\Windows Firewall | Entrant TCP 5985 |
En resume
Enable-PSRemoting -Forceconfigure tout en une commande (service, listeners, pare-feu)- En workgroup, les TrustedHosts sont necessaires ; en domaine, Kerberos suffit
- Pour un parc entier, privilegiez l'activation via GPO plutot que machine par machine
- WinRM utilise le port TCP 5985 (HTTP) ou 5986 (HTTPS)
Invoke-Command pour les operations de registre a distance¶
flowchart TD
A["Operation de registre a distance"] --> B{"Combien de machines ?"}
B -->|"1 seule"| C{"Plusieurs commandes ?"}
B -->|"Plusieurs"| D["Invoke-Command<br/>-ComputerName"]
C -->|"Non"| E["Invoke-Command<br/>-ComputerName"]
C -->|"Oui"| F["New-PSSession +<br/>Invoke-Command -Session"]
D --> G{"Besoin de WMI/CIM ?"}
G -->|"Oui"| H["New-CimSession +<br/>Invoke-CimMethod"]
G -->|"Non"| I["Invoke-Command<br/>-ThrottleLimit"]
style A fill:#bd93f9,color:#fff
style B fill:#ffb86c,color:#000
style C fill:#ffb86c,color:#000
style G fill:#ffb86c,color:#000
style E fill:#50fa7b,color:#000
style F fill:#8be9fd,color:#000
style H fill:#ff79c6,color:#fff
style I fill:#50fa7b,color:#000
style D fill:#50fa7b,color:#000 Vous recevez un ticket : "La valeur DisableNotifications dans le Centre de securite est incorrecte sur le poste PC-COMPTA-03." Plutot que de vous deplacer, une seule commande PowerShell suffit.
Lire une valeur de registre a distance¶
# Read a registry value on a remote machine
Invoke-Command -ComputerName PC-COMPTA-03 -ScriptBlock {
Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender Security Center\Notifications" `
-Name "DisableNotifications"
}
DisableNotifications : 1
PSComputerName : PC-COMPTA-03
RunspaceId : a1b2c3d4-e5f6-7890-abcd-ef1234567890
Ecrire une valeur de registre a distance¶
# Set a registry value on a remote machine
Invoke-Command -ComputerName PC-COMPTA-03 -ScriptBlock {
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender Security Center\Notifications" `
-Name "DisableNotifications" `
-Value 0
}
Creer une cle et une valeur inexistantes¶
# Create a new registry key and value on a remote machine
Invoke-Command -ComputerName SRV-APP01 -ScriptBlock {
$path = "HKLM:\SOFTWARE\MonEntreprise\AppConfig"
if (-not (Test-Path $path)) {
New-Item -Path $path -Force | Out-Null
}
New-ItemProperty -Path $path -Name "MaintenanceMode" -Value 1 -PropertyType DWord -Force
}
MaintenanceMode : 1
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\MonEntreprise\AppConfig
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\MonEntreprise
PSChildName : AppConfig
PSProvider : Microsoft.PowerShell.Core\Registry
Supprimer une valeur ou une cle¶
# Remove a specific registry value on a remote machine
Invoke-Command -ComputerName SRV-APP01 -ScriptBlock {
Remove-ItemProperty -Path "HKLM:\SOFTWARE\MonEntreprise\AppConfig" `
-Name "MaintenanceMode" -ErrorAction Stop
}
# Remove an entire registry key and its children
Invoke-Command -ComputerName SRV-APP01 -ScriptBlock {
Remove-Item -Path "HKLM:\SOFTWARE\MonEntreprise\AppConfig" -Recurse -Force
}
Utiliser des sessions persistantes pour plusieurs operations¶
Quand vous devez executer plusieurs commandes sur la meme machine, une session persistante evite de se re-authentifier a chaque fois. C'est comme garder une porte ouverte au lieu de sonner a chaque passage.
# Create a persistent session
$session = New-PSSession -ComputerName SRV-APP01
# Execute multiple operations via the same session
Invoke-Command -Session $session -ScriptBlock {
$path = "HKLM:\SOFTWARE\MonEntreprise\Deploy"
New-Item -Path $path -Force | Out-Null
New-ItemProperty -Path $path -Name "Version" -Value "2.5.1" -PropertyType String -Force
New-ItemProperty -Path $path -Name "DeployDate" -Value (Get-Date -Format "yyyy-MM-dd") -PropertyType String -Force
}
# Verify
Invoke-Command -Session $session -ScriptBlock {
Get-ItemProperty "HKLM:\SOFTWARE\MonEntreprise\Deploy"
}
# Clean up
Remove-PSSession $session
Preferer les sessions persistantes pour le scripting
Une session persistante (New-PSSession) conserve les variables, les modules importes et le contexte entre les appels Invoke-Command. Elle est aussi plus rapide car la connexion WinRM n'est etablie qu'une seule fois.
En resume
Invoke-Commandpermet de lire, ecrire, creer et supprimer des cles de registre a distance- Utilisez
Test-Pathpour verifier l'existence d'une cle avant de la creer - Les sessions persistantes (
New-PSSession) sont plus efficaces pour les operations multiples - L'absence de sortie sur une ecriture ou suppression indique un succes
Sessions CIM pour l'acces au registre¶
Les sessions CIM offrent une alternative a Invoke-Command. Elles utilisent le fournisseur WMI StdRegProv (Standard Registry Provider), qui expose des methodes dediees pour manipuler le registre. C'est comme utiliser un outil specialise plutot qu'un couteau suisse.
Creer une session CIM¶
# Create a CIM session to a remote machine
$cimSession = New-CimSession -ComputerName SRV-DC01
# Verify the session
$cimSession
Id : 1
Name : CimSession1
InstanceId : b2c3d4e5-f6a7-8901-bcde-f23456789012
ComputerName : SRV-DC01
Protocol : WSMAN
Constantes de ruches pour StdRegProv¶
StdRegProv utilise des constantes numeriques pour identifier les ruches :
| Constante | Valeur | Ruche |
|---|---|---|
HKEY_CLASSES_ROOT | 2147483648 | HKCR |
HKEY_CURRENT_USER | 2147483649 | HKCU |
HKEY_LOCAL_MACHINE | 2147483650 | HKLM |
HKEY_USERS | 2147483651 | HKU |
HKEY_CURRENT_CONFIG | 2147483653 | HKCC |
Lire une valeur String via CIM¶
# Read a REG_SZ value using CIM StdRegProv
$HKLM = [uint32]2147483650
$result = Invoke-CimMethod -CimSession $cimSession `
-Namespace "root\default" `
-ClassName "StdRegProv" `
-MethodName "GetStringValue" `
-Arguments @{
hDefKey = $HKLM
sSubKeyName = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
sValueName = "ProductName"
}
$result.sValue
Lire une valeur DWORD via CIM¶
# Read a REG_DWORD value using CIM StdRegProv
$result = Invoke-CimMethod -CimSession $cimSession `
-Namespace "root\default" `
-ClassName "StdRegProv" `
-MethodName "GetDWORDValue" `
-Arguments @{
hDefKey = $HKLM
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
sValueName = "EnableLUA"
}
$result.uValue
Ecrire une valeur via CIM¶
# Write a REG_DWORD value using CIM StdRegProv
Invoke-CimMethod -CimSession $cimSession `
-Namespace "root\default" `
-ClassName "StdRegProv" `
-MethodName "SetDWORDValue" `
-Arguments @{
hDefKey = $HKLM
sSubKeyName = "SOFTWARE\MonEntreprise\Config"
sValueName = "AuditEnabled"
uValue = [uint32]1
}
Un ReturnValue de 0 signifie succes. Toute autre valeur indique une erreur (acces refuse, cle inexistante, etc.).
Enumerer les sous-cles et valeurs¶
# Enumerate subkeys under a registry path
$enum = Invoke-CimMethod -CimSession $cimSession `
-Namespace "root\default" `
-ClassName "StdRegProv" `
-MethodName "EnumKey" `
-Arguments @{
hDefKey = $HKLM
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
}
$enum.sNames | Select-Object -First 10
AddressBook
Connection Manager
DirectDrawEx
{0A7C82A5-1754-4E06-AB5A-B6AE46346CE8}
{1D8E6291-B0D5-35EC-8441-6616F567A0F7}
{26A24AE4-039D-4CA4-87B4-2F64180101F0}
{4A03706F-666A-4037-7777-5F2748764D10}
...
CIM vs Invoke-Command : quand choisir CIM ?
Utilisez CIM quand vous travaillez avec des outils qui consomment deja des sessions CIM (SCCM, DSC, inventaire WMI). Utilisez Invoke-Command pour des scripts PowerShell generiques. CIM a l'avantage de fonctionner aussi via DCOM pour les machines anciennes qui ne supportent pas WinRM.
En resume
- Les sessions CIM utilisent le fournisseur
StdRegProvpour acceder au registre a distance - Chaque ruche est identifiee par une constante numerique (HKLM =
2147483650) - Les methodes CIM sont typees :
GetStringValue,GetDWORDValue,SetDWORDValue, etc. - Un
ReturnValuede 0 indique un succes, toute autre valeur est une erreur
Operations massives sur plusieurs machines¶
Votre responsable vous demande de verifier la valeur NtpServer sur les 200 postes du parc, puis de corriger les machines non conformes. Faire cela une par une prendrait des heures. PowerShell Remoting permet de paralleliser ces operations.
Lire une valeur sur plusieurs machines simultanement¶
# Read NTP configuration from multiple machines at once
$computers = Get-ADComputer -Filter "OperatingSystem -like '*Windows 10*'" |
Select-Object -ExpandProperty Name
$results = Invoke-Command -ComputerName $computers -ScriptBlock {
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" `
-Name "NtpServer" -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty NtpServer
} -ThrottleLimit 50 -ErrorAction SilentlyContinue
$results | Select-Object PSComputerName, @{N="NtpServer";E={$_}} |
Sort-Object PSComputerName
PSComputerName NtpServer
-------------- ---------
PC-COMPTA-01 ntp.entreprise.local,0x9
PC-COMPTA-02 time.windows.com,0x9
PC-COMPTA-03 ntp.entreprise.local,0x9
PC-RH-01 time.windows.com,0x9
PC-RH-02 ntp.entreprise.local,0x9
...
Le parametre -ThrottleLimit 50 controle le nombre de connexions simultanees. La valeur par defaut est 32. Ajustez-la en fonction de la capacite de votre reseau et de vos serveurs.
Corriger les machines non conformes¶
# Define the expected NTP server
$expectedNtp = "ntp.entreprise.local,0x9"
# Find and fix non-compliant machines
$nonCompliant = $results | Where-Object { $_ -ne $expectedNtp }
$nonCompliantNames = $nonCompliant | Select-Object -ExpandProperty PSComputerName
if ($nonCompliantNames) {
Invoke-Command -ComputerName $nonCompliantNames -ScriptBlock {
param($ntpServer)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" `
-Name "NtpServer" -Value $ntpServer
Restart-Service W32Time -Force
} -ArgumentList $expectedNtp -ThrottleLimit 50
Write-Host "$($nonCompliantNames.Count) machines corrected."
} else {
Write-Host "All machines are compliant."
}
Gestion des erreurs avec collecte de resultats¶
En environnement reel, certaines machines seront eteintes, inaccessibles ou en erreur. Un script robuste doit capturer ces cas.
# Robust mass registry operation with error handling
$computers = @("SRV-APP01", "SRV-APP02", "SRV-DB01", "PC-OFFLINE-01")
$job = Invoke-Command -ComputerName $computers -ScriptBlock {
try {
$path = "HKLM:\SOFTWARE\MonEntreprise\SecurityBaseline"
if (-not (Test-Path $path)) {
New-Item -Path $path -Force | Out-Null
}
Set-ItemProperty -Path $path -Name "BaselineVersion" -Value "2026.04" -Force
Set-ItemProperty -Path $path -Name "AppliedDate" -Value (Get-Date -Format "yyyy-MM-dd") -Force
[PSCustomObject]@{
Status = "Success"
Machine = $env:COMPUTERNAME
}
} catch {
[PSCustomObject]@{
Status = "Failed"
Machine = $env:COMPUTERNAME
Error = $_.Exception.Message
}
}
} -ErrorAction SilentlyContinue -ErrorVariable remoteErrors -ThrottleLimit 50
# Display successes
$job | Where-Object Status -eq "Success" |
Format-Table Machine, Status -AutoSize
# Display failures (machines that responded with errors)
$job | Where-Object Status -eq "Failed" |
Format-Table Machine, Status, Error -AutoSize
# Display unreachable machines
$remoteErrors | ForEach-Object {
[PSCustomObject]@{
Machine = $_.TargetObject
Error = $_.Exception.Message
}
} | Format-Table -AutoSize
Machine Status
------- ------
SRV-APP01 Success
SRV-APP02 Success
SRV-DB01 Success
Machine Error
------- -----
PC-OFFLINE-01 Connecting to remote server PC-OFFLINE-01 failed...
Execution parallele avec ForEach-Object -Parallel (PowerShell 7+)¶
# PowerShell 7+ parallel execution for registry operations
$computers = Get-Content "C:\Admin\workstations.txt"
$computers | ForEach-Object -Parallel {
$result = Invoke-Command -ComputerName $_ -ScriptBlock {
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" |
Select-Object EnableLUA, ConsentPromptBehaviorAdmin
} -ErrorAction SilentlyContinue
if ($result) {
[PSCustomObject]@{
Computer = $_
UAC = if ($result.EnableLUA -eq 1) { "Enabled" } else { "Disabled" }
Consent = $result.ConsentPromptBehaviorAdmin
}
}
} -ThrottleLimit 100 | Export-Csv "C:\Admin\uac-audit.csv" -NoTypeInformation
En resume
-ThrottleLimitcontrole le nombre de connexions simultanees (defaut : 32)- Capturez toujours les erreurs avec
-ErrorAction SilentlyContinueet-ErrorVariable - Structurez les resultats en objets pour faciliter le tri, le filtrage et l'export
- PowerShell 7+ offre
ForEach-Object -Parallelpour une parallelisation native
Considerations de securite¶
Un script qui modifie le registre de 200 machines a distance est un outil puissant, mais aussi un vecteur d'attaque potentiel. La securite du remoting PowerShell repose sur trois piliers : l'authentification, la delegation et la restriction des droits.
Kerberos et le probleme du double-hop¶
Scenario classique : depuis votre poste d'administration, vous vous connectez en PowerShell a SRV-APP01, puis depuis SRV-APP01 vous tentez d'acceder a un partage sur SRV-FILE01. Le second saut echoue : c'est le probleme du double-hop.
Kerberos ne delegue pas automatiquement vos credentials au-dela du premier saut. C'est comme donner votre badge d'acces a un collegue qui ne peut pas l'utiliser pour ouvrir une autre porte a votre place.
Poste Admin ──[Kerberos]──> SRV-APP01 ──[echec]──> SRV-FILE01
(1er hop: OK) (2eme hop: pas de credentials)
Solutions au double-hop¶
| Solution | Securite | Complexite | Cas d'usage |
|---|---|---|---|
| CredSSP | Faible | Facile | Tests uniquement, jamais en production |
| Delegation contrainte Kerberos | Elevee | Moyenne | Production, serveurs specifiques |
| Resource-based constrained delegation | Elevee | Moyenne | Production, inter-forets |
| JEA (Just Enough Administration) | Tres elevee | Elevee | Production, zero-trust |
| PSSessionConfiguration avec RunAs | Moyenne | Moyenne | Scenarios specifiques |
Delegation contrainte Kerberos¶
# Configure constrained delegation on SRV-APP01 in Active Directory
# SRV-APP01 will be allowed to delegate credentials to SRV-FILE01
Set-ADComputer -Identity "SRV-APP01" -Add @{
"msDS-AllowedToDelegateTo" = @(
"cifs/SRV-FILE01.entreprise.local"
"cifs/SRV-FILE01"
)
}
# Verify the delegation
Get-ADComputer "SRV-APP01" -Properties "msDS-AllowedToDelegateTo" |
Select-Object -ExpandProperty "msDS-AllowedToDelegateTo"
CredSSP (a eviter en production)¶
CredSSP expose vos credentials
CredSSP ne transmet pas vos identifiants en clair sur le reseau, mais il les delegue au serveur intermediaire, qui les stocke ensuite dans LSASS pour realiser le second saut. Si ce serveur est compromis, l'attaquant peut reutiliser ces credentials de haut niveau. Le trafic reste chiffre, mais le risque vient bien du poste intermediaire.
# Enable CredSSP (TEST ENVIRONMENT ONLY)
Enable-WSManCredSSP -Role Client -DelegateComputer "SRV-APP01.entreprise.local" -Force
Enable-WSManCredSSP -Role Server -Force # On SRV-APP01
# Connect with CredSSP
$cred = Get-Credential
Invoke-Command -ComputerName SRV-APP01 -Credential $cred -Authentication CredSSP -ScriptBlock {
# This second hop now works (but at what cost?)
Invoke-Command -ComputerName SRV-FILE01 -ScriptBlock {
Get-ItemProperty "HKLM:\SOFTWARE\MonApp"
}
}
Just Enough Administration (JEA)¶
JEA est la meilleure approche pour limiter ce qu'un administrateur peut faire sur une machine distante. Vous definissez exactement quelles commandes et quels parametres sont autorises.
# Create a JEA role capability file
$roleCapability = @{
Path = "C:\Program Files\WindowsPowerShell\Modules\RegistryJEA\RoleCapabilities\RegistryOperator.psrc"
VisibleCmdlets = @(
"Get-ItemProperty"
"Set-ItemProperty"
"Test-Path"
@{
Name = "New-Item"
Parameters = @{ Name = "Path"; ValidatePattern = "HKLM:\\SOFTWARE\\MonEntreprise\\*" }
}
)
VisibleFunctions = @()
VisibleProviders = @("Registry")
}
New-PSRoleCapabilityFile @roleCapability
# Create and register the JEA session configuration
$sessionConfig = @{
Path = "C:\Admin\RegistryJEA.pssc"
SessionType = "RestrictedRemoteServer"
RunAsVirtualAccount = $true
RoleDefinitions = @{
"ENTREPRISE\Registry-Operators" = @{ RoleCapabilities = "RegistryOperator" }
}
}
New-PSSessionConfigurationFile @sessionConfig
Register-PSSessionConfiguration -Name "RegistryJEA" -Path "C:\Admin\RegistryJEA.pssc" -Force
WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin
Type Keys Name
---- ---- ----
Container {Name=RegistryJEA} RegistryJEA
L'administrateur se connecte ensuite a l'endpoint JEA et ne peut executer que les commandes autorisees :
# Connect to the JEA endpoint
Enter-PSSession -ComputerName SRV-APP01 -ConfigurationName RegistryJEA
# This works (authorized)
Get-ItemProperty "HKLM:\SOFTWARE\MonEntreprise\Config"
# This fails (not authorized)
Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows"
PS SRV-APP01> Get-ItemProperty "HKLM:\SOFTWARE\MonEntreprise\Config"
AppVersion : 3.2.1
PS SRV-APP01> Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows"
The term 'Remove-Item' is not recognized as the name of a cmdlet...
En resume
- Le double-hop Kerberos est le probleme de securite n°1 en remoting PowerShell
- CredSSP est la solution facile mais dangereuse : a proscrire en production
- La delegation contrainte Kerberos est le bon compromis securite/complexite
- JEA offre le plus haut niveau de controle en limitant les commandes disponibles
OpenSSH : l'alternative moderne a WinRM¶
OpenSSH est inclus comme fonctionnalite optionnelle depuis Windows 10 1809 et Windows Server 2019. Sur les generations recentes, dont Server 2025, il devient une alternative credible a WinRM pour les environnements hybrides, cross-OS ou segmentes par pare-feu. PowerShell 7+ supporte le remoting via SSH avec Enter-PSSession -HostName et Invoke-Command -HostName.
Cles de registre OpenSSH¶
| Valeur | Type | Exemple | Effet |
|---|---|---|---|
DefaultShell | REG_SZ | C:\Program Files\PowerShell\7\pwsh.exe | Shell lance par defaut a la connexion SSH |
DefaultShellCommandOption | REG_SZ | -c pour pwsh, /c pour cmd.exe | Option transmise au shell pour executer une commande |
DefaultShellEscapeArguments | REG_DWORD | 0 | Controle l'echappement automatique des arguments |
Version OpenSSH
DefaultShell est la cle la plus stable. Les options DefaultShellCommandOption et DefaultShellEscapeArguments doivent etre verifiees sur la version OpenSSH de l'image cible avant standardisation.
Installation et activation¶
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Set-Service -Name sshd -StartupType Automatic
Start-Service sshd
New-Item -Path "HKLM:\SOFTWARE\OpenSSH" -Force | Out-Null
Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
-Value "C:\Program Files\PowerShell\7\pwsh.exe" -Type String
Configuration pour PowerShell Remoting over SSH¶
Editez C:\ProgramData\ssh\sshd_config et ajoutez ou validez :
Subsystem powershell C:/progra~1/powershell/7/pwsh.exe -sshs -NoLogo
PasswordAuthentication yes
Puis redemarrez le service :
Pour un environnement durci, remplacez ensuite PasswordAuthentication yes par une authentification par cle publique et une restriction de groupes.
Chemin PowerShell
OpenSSH peut mal gerer les chemins avec espaces dans Subsystem. Utilisez le nom court C:/progra~1/... ou un lien symbolique vers pwsh.exe dans C:\ProgramData\ssh.
Enter-PSSession -HostName server01 -UserName admin
Invoke-Command -HostName server01,server02 -UserName admin -ScriptBlock {
Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
}
Decision matrix WinRM vs SSH¶
| Critere | WinRM | SSH |
|---|---|---|
| Protocole | HTTP/HTTPS, TCP 5985/5986 | SSH, TCP 22 |
| Authentification | Kerberos, NTLM, certificat | Cle publique, mot de passe |
| Cross-platform | Windows principalement | Windows, Linux, macOS |
| PowerShell version | Windows PowerShell 5.1 + PowerShell 7 | PowerShell 7 recommande |
| Configuration | GPO + WinRM listeners | sshd_config + service sshd |
| Registre a distance | Invoke-Command, provider Registry:: | Invoke-Command -HostName |
| Firewall | Ports WinRM a ouvrir | Port 22 a ouvrir |
| Recommandation | Environnements AD homogenes | Environnements hybrides ou cross-OS |
Hardening SSH¶
Commencez par ces quatre controles :
PasswordAuthentication nodanssshd_configapres deploiement des cles publiques ;- restriction des utilisateurs avec
AllowGroups SSH-Admins; - pare-feu limite aux sous-reseaux d'administration ;
- journalisation et supervision du service
sshd.
Lien avec JEA
SSH ne remplace pas JEA. Si vous avez besoin d'endpoints JEA et de configurations de session avancees, WinRM reste le transport le plus adapte ; SSH sert surtout les scenarios hybrides et cross-OS.
En resume
- OpenSSH est l'alternative moderne a WinRM pour les environnements hybrides et cross-OS.
HKLM\SOFTWARE\OpenSSH\DefaultShellpermet de lancer PowerShell 7 par defaut.- Le remoting PowerShell via SSH utilise
Enter-PSSession -HostNameetInvoke-Command -HostName. - En production, privilegiez les cles publiques,
AllowGroupset un filtrage pare-feu strict.
Scenario reel : deployer un correctif de registre sur 200 postes¶
Votre equipe securite a identifie que la valeur legacy DisableAntiSpyware est presente sur certains postes. Sur Windows moderne, cette valeur ne prouve pas a elle seule que Defender est desactive, mais elle reste un signal de sabotage ou de politique obsolete. Vous devez auditer les 200 postes du parc, nettoyer les machines suspectes et generer un rapport.
Etape 1 : Preparer la liste des machines¶
# Get all workstations from Active Directory
$computers = Get-ADComputer -Filter "OperatingSystem -like '*Windows 10*' -or OperatingSystem -like '*Windows 11*'" `
-SearchBase "OU=Workstations,DC=entreprise,DC=local" |
Select-Object -ExpandProperty Name
Write-Host "Machines found: $($computers.Count)"
Etape 2 : Auditer l'etat actuel¶
# Audit the legacy DisableAntiSpyware policy and collect the effective Defender state
$auditResults = Invoke-Command -ComputerName $computers -ScriptBlock {
$path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender"
$policy = Get-ItemProperty -Path $path -Name "DisableAntiSpyware" -ErrorAction SilentlyContinue
$mpStatus = Get-MpComputerStatus -ErrorAction SilentlyContinue
$reviewReason = if ($null -ne $policy -and $policy.DisableAntiSpyware -eq 1) {
"LegacyPolicy"
} elseif ($null -eq $mpStatus) {
"NoMpStatus"
} else {
"OK"
}
[PSCustomObject]@{
Computer = $env:COMPUTERNAME
DisableAntiSpyware = if ($null -ne $policy) { $policy.DisableAntiSpyware } else { "N/A" }
AntivirusEnabled = if ($null -ne $mpStatus) { $mpStatus.AntivirusEnabled } else { "Unknown" }
RealTimeProtection = if ($null -ne $mpStatus) { $mpStatus.RealTimeProtectionEnabled } else { "Unknown" }
IsTamperProtected = if ($null -ne $mpStatus) { $mpStatus.IsTamperProtected } else { "Unknown" }
ReviewReason = $reviewReason
}
} -ThrottleLimit 50 -ErrorAction SilentlyContinue -ErrorVariable auditErrors
# Summary
$compliant = $auditResults | Where-Object { $_.ReviewReason -eq "OK" }
$needsReview = $auditResults | Where-Object { $_.ReviewReason -ne "OK" }
$unreachable = $auditErrors.Count
Write-Host "Compliant : $($compliant.Count)"
Write-Host "Needs review: $($needsReview.Count)"
Write-Host "Unreachable : $unreachable"
Etape 3 : Appliquer le correctif¶
# Remove the rogue legacy value on suspicious machines
$fixTargets = $auditResults |
Where-Object { $_.ReviewReason -eq "LegacyPolicy" } |
Select-Object -ExpandProperty Computer
$fixResults = Invoke-Command -ComputerName $fixTargets -ScriptBlock {
try {
$path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender"
# Remove the legacy DisableAntiSpyware value if present
if ($null -ne (Get-ItemProperty -Path $path -Name "DisableAntiSpyware" -ErrorAction SilentlyContinue)) {
Remove-ItemProperty -Path $path -Name "DisableAntiSpyware" -Force -ErrorAction Stop
}
$mpStatus = Get-MpComputerStatus -ErrorAction SilentlyContinue
[PSCustomObject]@{
Computer = $env:COMPUTERNAME
Status = "PolicyRemoved"
AntivirusEnabled = if ($null -ne $mpStatus) { $mpStatus.AntivirusEnabled } else { "Unknown" }
IsTamperProtected = if ($null -ne $mpStatus) { $mpStatus.IsTamperProtected } else { "Unknown" }
}
} catch {
[PSCustomObject]@{
Computer = $env:COMPUTERNAME
Status = "Error"
AntivirusEnabled = "Unknown"
IsTamperProtected = $_.Exception.Message
}
}
} -ThrottleLimit 50 -ErrorAction SilentlyContinue
Computer Status AntivirusEnabled IsTamperProtected
-------- ------ ---------------- -----------------
PC-COMPTA-02 PolicyRemoved True True
PC-COMPTA-05 PolicyRemoved True True
PC-RH-01 PolicyRemoved True True
PC-RH-03 PolicyRemoved True True
...
Etape 4 : Generer le rapport¶
# Generate a comprehensive report
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$reportPath = "C:\Admin\Reports\defender-audit-$timestamp.csv"
$fullReport = @()
# Add successful audits
$fullReport += $auditResults | Select-Object Computer,
@{N="Status";E={
switch ($_.ReviewReason) {
"LegacyPolicy" { "LegacyPolicy" }
"NoMpStatus" { "NoMpStatus" }
default { "Compliant" }
}
}},
DisableAntiSpyware, AntivirusEnabled, RealTimeProtection, IsTamperProtected
# Add unreachable machines
$fullReport += $auditErrors | ForEach-Object {
[PSCustomObject]@{
Computer = $_.TargetObject
Status = "Unreachable"
DisableAntiSpyware = "Unknown"
AntivirusEnabled = "Unknown"
RealTimeProtection = "Unknown"
IsTamperProtected = "Unknown"
}
}
$fullReport | Export-Csv -Path $reportPath -NoTypeInformation -Encoding UTF8
Write-Host "Report saved: $reportPath"
# Display summary
$fullReport | Group-Object Status |
Select-Object Name, Count |
Format-Table -AutoSize
Report saved: C:\Admin\Reports\defender-audit-20260404-143022.csv
Name Count
---- -----
Compliant 156
LegacyPolicy 38
Unreachable 9
Verifiez avant de supprimer a grande echelle
Avant d'executer le correctif sur 200 machines, testez toujours sur un echantillon de 5 a 10 postes. Validez les resultats, verifiez l'absence d'effets secondaires, puis elargissez au parc complet.
Automatisez avec une tache planifiee
Ce type d'audit peut etre programme quotidiennement via une tache planifiee PowerShell. Couple a une alerte email, vous detectez les regressions en temps reel.
En resume
- Un deploiement massif suit quatre etapes : inventaire, audit, correction, rapport
DisableAntiSpywaredoit etre traitee comme une politique legacy suspecte, puis recoupee avecGet-MpComputerStatus-ThrottleLimitet-ErrorVariablesont essentiels pour les operations a grande echelle- Testez toujours sur un echantillon avant de deployer sur le parc complet
- Generez systematiquement un rapport CSV pour la tracabilite
Voir aussi
- Registre et reseau — Bible Registre
- Scripts et automatisation — Bible Registre