Points clés à retenir (TL;DR)
Synthèse exécutive- On deploie l'agent Wazuh (SIEM/EDR open-source) via un script de demarrage ordinateur pousse par GPO, execute en contexte SYSTEM, avec enregistrement automatique sous le nom d'hote.
- On installe Sysmon avec une configuration durcie, puis on redirige son canal d'evenements
Microsoft-Windows-Sysmon/Operationalvers l'agent Wazuh pour la collecte. - Deux voies complementaires : GPO pour les postes du domaine, WinRM/PSRemoting en parallele pour un lot de serveurs identifies.
- Chaque script est idempotent : detection de l'existant, journalisation, et comportement defini si deja installe.
- On traite l'operation comme un changement ITIL normal : fenetre de maintenance, pilote limite, rollback documente.

Le probleme qu’on resout
La journalisation Windows native est insuffisante pour la detection d’intrusion. Le journal Securite ne capture pas nativement les relations parent/enfant de processus, les creations de fichiers, les connexions reseau sortantes par processus, ni les chargements de DLL. Sans cette granularite, on ne peut pas detecter un living-off-the-land (MITRE ATT&CK T1059 — Command and Scripting Interpreter) ni un process injection (T1055).
Sysmon comble ce manque en produisant une telemetrie riche, mais il doit etre deploye et configure partout, de facon homogene. Le faire a la main sur chaque machine ne passe pas a l’echelle : derive de configuration, machines oubliees, versions heterogenes. Copier/coller un installeur via un partage reseau fonctionne un temps, puis se perd au reimaging.
Il manque aussi un collecteur : Sysmon ecrit dans un journal local, encore faut-il remonter ces evenements vers un point central. C’est le role de l’agent Wazuh, qui lit le canal Sysmon et l’envoie au manager pour correlation et alerte. La combinaison GPO + agent resout les deux problemes : deploiement reproductible et collecte centralisee.
Comment ca marche
L’approche repose sur trois briques independantes mais chainees :
- Distribution : un script de demarrage ordinateur (GPO) s’execute au boot, en SYSTEM, avec les droits necessaires pour installer un MSI. Pas de session utilisateur requise.
- Enrolement : l’agent Wazuh s’enregistre aupres du manager avec un nom egal au
$env:COMPUTERNAMEet un groupe d’agents, via un mot de passe d’enregistrement. Le manager pousse ensuite la configuration partagee du groupe. - Telemetrie : Sysmon est installe avec un fichier
sysmonconfig.xml. On ajoute dansossec.confun bloc<localfile>pointant vers le canal Sysmon pour que l’agent le collecte.
Le pilier operationnel est l’idempotence : chaque execution verifie l’etat courant (service present ? version a jour ? bloc deja dans la conf ?) et n’agit que si necessaire. Un script de demarrage tourne a chaque boot : il doit etre sur de ne pas reinstaller en boucle ni corrompre une configuration existante.
Prerequis
- Un domaine Active Directory et les droits pour creer/lier une GPO sur l’OU des machines cibles.
- Un manager Wazuh joignable depuis les postes (TCP 1514/1515 par defaut), avec un mot de passe d’enregistrement et un groupe d’agents cree.
- Un point de distribution HTTP(S) interne pour heberger
Sysmon64.exeetsysmonconfig.xml(ou un partage SMB en lecture). - Pour la voie WinRM : PSRemoting active (HTTPS/5986 recommande), un compte d’administration local delegue, et les pare-feux ouverts.
- PowerShell 5.1+ et TLS 1.2 force pour les telechargements.
- Un pilote (5 a 10 machines representatives) avant deploiement de masse.
Procedure
- Preparer le point de distribution : deposer
Sysmon64.exe,sysmonconfig.xmlet le MSI Wazuh sur un serveur web interne en lecture seule. - Creer le groupe d’agents cote manager Wazuh et generer/valider le mot de passe d’enregistrement.
- Deposer le script de demarrage dans le dossier
Scripts\Startupde la GPO et le declarer dans Configuration ordinateur > Strategies > Parametres Windows > Scripts (demarrage/arret). - Lier la GPO a une OU de test, forcer
gpupdate /forceet redemarrer un poste pilote. - Verifier l’enrolement de l’agent cote manager et la remontee d’evenements Sysmon.
- Pour les serveurs : executer le script WinRM parallelise depuis un poste d’administration, lot par lot.
- Elargir la liaison de la GPO a l’OU de production apres validation du pilote.
Le script
Script de deploiement de l’agent Wazuh, concu pour un demarrage GPO (contexte SYSTEM) ou un test local en administrateur. Toutes les valeurs sont generiques.
#requires -Version 5.1
# Deploiement agent Wazuh via GPO (script de demarrage ORDINATEUR, execute en SYSTEM)
# ou testable localement (PowerShell en administrateur).
# =================== REGLAGES (a adapter) ===================
$Version = '4.x.y-1' # version cible du MSI
$MsiUrl = "https://packages.wazuh.com/4.x/windows/wazuh-agent-$Version.msi"
$Manager = 'wazuh-manager.exemple.test' # FQDN du manager
$Group = 'serveurs-windows' # groupe d'agents
$RegPass = $env:WAZUH_REG_PASS # secret via variable d'env / MSI local
$AgentName = $env:COMPUTERNAME # nom = hostname automatique
$Force = $false # $true = reinstalle a chaque passage (tests)
# ===========================================================
$ErrorActionPreference = 'Stop'
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# ---- Journal ----
$LogDir = 'C:\ProgramData\SecAgent'
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
$Log = Join-Path $LogDir 'deploiement-wazuh.log'
function Note($m){ $l = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $m"; Add-Content $Log $l; Write-Host $l }
Note "=== Debut. Machine=$AgentName, version cible=$Version, force=$Force ==="
# ---- Detecter une installation existante (cle de desinstallation MSI) ----
function Get-WazuhInstall {
$keys = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty $keys -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like 'Wazuh Agent*' } |
Select-Object -First 1 DisplayName, DisplayVersion, PSChildName
}
function Uninstall-Wazuh($inst){
Note "Desinstallation de '$($inst.DisplayName)' ($($inst.DisplayVersion))..."
try { Stop-Service WazuhSvc -Force -ErrorAction SilentlyContinue } catch {}
$code = $inst.PSChildName # {GUID} du produit MSI
$p = Start-Process msiexec.exe -Wait -PassThru -ArgumentList '/x',$code,'/q','/norestart','/l*v',"$LogDir\wazuh-desinstall.log"
if ($p.ExitCode -ne 0 -and $p.ExitCode -ne 3010) { throw "Desinstallation en echec (code $($p.ExitCode))" }
Start-Sleep 3
Note "Desinstallation OK."
}
$inst = Get-WazuhInstall
if ($inst) {
Note "Deja installe : $($inst.DisplayName) $($inst.DisplayVersion)"
$cibleCourte = ($Version -split '-')[0]
if ($Force -or $inst.DisplayVersion -ne $cibleCourte) {
Uninstall-Wazuh $inst
} else {
Note "Version a jour et Force=`$false : rien a faire. Fin."; return
}
}
# ---- Telechargement du MSI ----
$Msi = Join-Path $env:TEMP 'wazuh-agent.msi'
Note "Telechargement : $MsiUrl"
Invoke-WebRequest -Uri $MsiUrl -OutFile $Msi -UseBasicParsing
Note "Telecharge : $([math]::Round((Get-Item $Msi).Length/1MB,1)) Mo"
# ---- Installation (nom d'agent = hostname) ----
Note "Installation (manager=$Manager, groupe=$Group, nom=$AgentName)..."
$args = @(
'/i',$Msi,'/q','/norestart',
"WAZUH_MANAGER=$Manager",
"WAZUH_AGENT_GROUP=$Group",
"WAZUH_AGENT_NAME=$AgentName",
"WAZUH_REGISTRATION_PASSWORD=$RegPass",
'/l*v',"$LogDir\wazuh-install.log"
)
$p = Start-Process msiexec.exe -Wait -PassThru -ArgumentList $args
if ($p.ExitCode -ne 0 -and $p.ExitCode -ne 3010) { throw "Installation en echec (code $($p.ExitCode))" }
Remove-Item $Msi -Force -ErrorAction SilentlyContinue
# ---- Demarrage du service ----
Set-Service WazuhSvc -StartupType Automatic -ErrorAction SilentlyContinue
Start-Service WazuhSvc
Note "Service WazuhSvc demarre."
Note "=== Fin ==="
Variante : deploiement de Sysmon sur un lot de serveurs via WinRM parallelise. Remplacez la liste de cibles et le point de distribution par vos valeurs.
param(
[string]$HttpBase = "https://distrib.exemple.test:8443", # point de distribution interne
[int] $MaxParallel = 8,
[string]$LogFile = "C:\Temp\sysmon-deploy-results.csv"
)
# Liste des serveurs cibles (noms courts, generiques)
$targets = @("srv-app01","srv-app02","srv-db01","srv-file01","srv-print01")
# Compte d'administration delegue (secret via Get-Credential, jamais en clair)
$cred = Get-Credential -Message "Compte admin pour le deploiement"
$sopts = New-PSSessionOption -SkipCACheck -SkipCNCheck # a eviter en prod : utiliser un cert valide
# Bloc a injecter dans ossec.conf pour collecter le canal Sysmon
$sysmonBlock = @"
Microsoft-Windows-Sysmon/Operational
eventchannel
"@
$deployScript = {
param($computer, $cred, $sopts, $base, $sysmonBlock)
$result = [PSCustomObject]@{ Computer=$computer; Status="PENDING"; Detail=""; Timestamp=(Get-Date -Format "yyyy-MM-dd HH:mm:ss") }
try {
$sess = New-PSSession -ComputerName "$computer.exemple.test" -Credential $cred `
-UseSSL -Port 5986 -SessionOption $sopts -ErrorAction Stop
$res = Invoke-Command -Session $sess -ArgumentList $base, $sysmonBlock -ScriptBlock {
param($base, $sysmonBlock)
# --- Sysmon ---
$sysmonStatus = "ALREADY_INSTALLED"
if (-not (Get-Service Sysmon64 -ErrorAction SilentlyContinue)) {
$dest = "C:\Windows\Temp\sysmon-deploy"
New-Item -ItemType Directory -Force -Path $dest | Out-Null
try {
Invoke-WebRequest -Uri "$base/Sysmon64.exe" -OutFile "$dest\Sysmon64.exe" -UseBasicParsing -TimeoutSec 120
Invoke-WebRequest -Uri "$base/sysmonconfig.xml" -OutFile "$dest\sysmonconfig.xml" -UseBasicParsing -TimeoutSec 30
} catch { return "SYSMON_DL_FAIL:$_" }
$p = Start-Process "$dest\Sysmon64.exe" -ArgumentList "-accepteula","-i","$dest\sysmonconfig.xml" -Wait -PassThru -NoNewWindow
if ($p.ExitCode -ne 0) { return "SYSMON_INSTALL_FAIL:exit=$($p.ExitCode)" }
Start-Sleep 3
$sysmonStatus = "NEWLY_INSTALLED"
}
# --- ossec.conf : ajout du collecteur Sysmon (idempotent) ---
$wazuhStatus = "SKIPPED"
$conf = "C:\Program Files (x86)\ossec-agent\ossec.conf"
if (-not (Test-Path $conf)) { $conf = "C:\Program Files\ossec-agent\ossec.conf" }
if (Test-Path $conf) {
$content = Get-Content $conf -Raw
if ($content -notmatch "Sysmon") {
Copy-Item $conf "$conf.bak" -Force # sauvegarde pour rollback
($content -replace "", "$sysmonBlock") |
Set-Content -Path $conf -Encoding UTF8
Restart-Service WazuhSvc -Force -ErrorAction SilentlyContinue
Start-Sleep 5
$wazuhStatus = "UPDATED"
} else { $wazuhStatus = "ALREADY_OK" }
} else { $wazuhStatus = "CONF_NOT_FOUND" }
$svcFinal = (Get-Service Sysmon64 -ErrorAction SilentlyContinue).Status
return "OK|sysmon=$sysmonStatus|wazuh=$wazuhStatus|svc=$svcFinal"
}
Remove-PSSession $sess
$result.Status = if ($res -like "OK|*") { "OK" } else { "ERROR" }
$result.Detail = $res
} catch {
$result.Status = "CONN_FAIL"
$result.Detail = $_.Exception.Message -replace "`n"," "
}
return $result
}
# Orchestration parallele par lots
$results = @(); $queue = [System.Collections.Queue]::new()
$targets | ForEach-Object { $queue.Enqueue($_) }
$running = @{}; $total = $targets.Count; $done = 0
while ($queue.Count -gt 0 -or $running.Count -gt 0) {
while ($queue.Count -gt 0 -and $running.Count -lt $MaxParallel) {
$srv = $queue.Dequeue()
$job = Start-Job -ScriptBlock $deployScript -ArgumentList $srv,$cred,$sopts,$HttpBase,$sysmonBlock
$running[$job.Id] = @{ Job=$job; Server=$srv }
}
foreach ($entry in @($running.GetEnumerator() | Where-Object { $_.Value.Job.State -in 'Completed','Failed' })) {
$res = Receive-Job -Job $entry.Value.Job; Remove-Job -Job $entry.Value.Job
if ($res) { $results += $res }
$running.Remove($entry.Key); $done++
Write-Host " [$done/$total] $($entry.Value.Server) -> $($res.Status)"
}
if ($running.Count -gt 0 -or $queue.Count -gt 0) { Start-Sleep 2 }
}
$results | Export-Csv -Path $LogFile -NoTypeInformation -Encoding UTF8
Write-Host "Rapport : $LogFile"
Validation
Verifiez chaque couche independamment :
- Service Wazuh sur la machine :
Get-Service WazuhSvcdoit retournerRunning. - Service Sysmon :
Get-Service Sysmon64enRunning, etGet-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 5doit renvoyer des evenements recents. - Enrolement cote manager : l’agent apparait avec le nom d’hote et le statut Active (via
agent_control -lou la console). - Collecte Sysmon : le bloc
<localfile>est present dansossec.confet des evenements Sysmon (ID 1 — process creation) remontent dans le SIEM. - Journaux locaux :
C:\ProgramData\SecAgent\deploiement-wazuh.logse termine par « === Fin === » sans exception.
Test fonctionnel : generer une activite controlee (ex. whoami depuis une invite) et confirmer l’alerte/l’evenement correspondant dans le SIEM.
Plan de retour (rollback)
- Desinstaller l’agent Wazuh : recuperer le GUID via la cle de desinstallation puis
msiexec /x {GUID} /q /norestart. Sur le manager, retirer l’agent avecmanage_agents. - Restaurer ossec.conf : le script cree une sauvegarde
ossec.conf.bak. Restaurer :Copy-Item "$conf.bak" $conf -ForcepuisRestart-Service WazuhSvc. - Desinstaller Sysmon :
Sysmon64.exe -u force(supprime le service et le pilote). - Annuler le deploiement GPO : delier la GPO de l’OU (la delier est plus sur que la supprimer), puis
gpupdate /forceau prochain boot.
Le rollback doit etre teste sur le pilote AVANT la production. Documentez le temps de retour attendu dans la fiche de changement.
Depannage
| Symptome | Cause probable | Solution |
|---|---|---|
| Agent installe mais statut Never connected | Mot de passe d’enregistrement incorrect ou ports 1514/1515 bloques | Verifier le secret, tester Test-NetConnection manager -Port 1514, ouvrir le pare-feu |
| Sysmon absent du SIEM | Bloc <localfile> manquant ou service non redemarre |
Verifier ossec.conf, relancer WazuhSvc, confirmer que le canal existe |
CONN_FAIL sur un serveur (WinRM) |
PSRemoting desactive ou HTTPS 5986 ferme | Enable-PSRemoting, ouvrir 5986, verifier le certificat |
| Installation MSI code 1603 | Installeur precedent corrompu ou conflit de version | Desinstaller proprement, nettoyer les restes, puis reinstaller le MSI (journal msiexec /i ... /l*v). |