L'importance de la parallélisation dans l'automatisation
Dans le développement de scripts PowerShell, le pasage d'une exécution séquentielle à une approche parallèle est crucial pour la performance. Par défaut, PowerShell traite les commandes les unes après les autres. Si une tâche prend du temps (comme une requête réseau ou une analyse disque), l'intégralité du script est bloquée.
# Exemple de latence en mode séquentiel
$domaines = "google.com", "bing.com", "duckduckgo.com"
foreach ($domaine in $domaines) {
$debut = Get-Date
Test-Connection -ComputerName $domaine -Count 1 -ErrorAction SilentlyContinue | Out-Null
$duree = ((Get-Date) - $debut).TotalSeconds
Write-Host "Vérification de $domaine terminée en $duree secondes."
}
Ici, le temps total est la somme des délais de chaque test. Pour optimiser cela, nous disposons de trois approches principales.
1. Les Jobs en arrière-plan (Start-Job)
Il s'agit de la méthode la plus ancienne pour isoler des tâches. Chaque "Job" lance un nouveau processus PowerShell en arrière-plan.
- Avantage : Isolation complète (les variables n'interfèrent pas).
- Inconvénient : Consommation de ressources élevée (chaque job consomme de la mémoire comme une nouvelle instance de PowerShell).
$serveurs = "127.0.0.1", "localhost", "192.168.1.1"
# Lancement des processus esclaves
$mesJobs = foreach ($ip in $serveurs) {
Start-Job -ScriptBlock {
param($cible)
$status = Test-Connection $cible -Count 1 -Quiet
return [PSCustomObject]@{ Machine = $cible; EnLigne = $status }
} -ArgumentList $ip
}
# Récupération des données
$resultats = $mesJobs | Wait-Job | Receive-Job
$mesJobs | Remove-Job # Nettoyage impératif
$resultats | Format-Table -AutoSize
2. Parallélisation native avec ForEach-Object (PowerShell 7+)
Depuis PowerShell 7, l'instruction ForEach-Object dispose d'un paramètre -Parallel. Contrairement aux Jobs, cette méthode utilise des threads au sein du même processus, ce qui est beaucoup plus léger.
# Vérification de l'état de services en parallèle
$nomsServices = "Spooler", "WinRM", "LanmanWorkstation"
$nomsServices | ForEach-Object -Parallel {
$etat = Get-Service -Name $_ -ErrorAction SilentlyContinue
[PSCustomObject]@{
Nom = $_
Statut = if ($etat) { $etat.Status } else { "Inexistant" }
}
} -ThrottleLimit 5 # Limite à 5 threads simultanés
Note : Pour utiliser des variables définies à l'extérieur du bloc script, vous devez utiliser le préfixe $using: (ex: $using:maVariable).
3. Utilisation des Runspaces (Haute Performance)
Les RunspacePools représentent la méthode la plus performante et compatible (PS 5.1 et 7). Ils permettent de réutiliser des threads sans le surcoût de création de processus.
function Invoke-ActionParallele {
param(
[Parameter(Mandatory)] [array]$ListeEntree,
[Parameter(Mandatory)] [scriptblock]$BlocScript,
[int]$MaxThreads = 4
)
$pool = [RunspaceFactory]::CreateRunspacePool(1, $MaxThreads)
$pool.Open()
$taches = New-Object System.Collections.Generic.List[object]
foreach ($item in $ListeEntree) {
$ps = [PowerShell]::Create().AddScript($BlocScript).AddArgument($item)
$ps.RunspacePool = $pool
$taches.Add([PSCustomObject]@{
Instance = $ps
Handle = $ps.BeginInvoke()
})
}
# Extraction des données
$resultats = foreach ($t in $taches) {
$t.Instance.EndInvoke($t.Handle)
$t.Instance.Dispose()
}
$pool.Close()
$pool.Dispose()
return $resultats
}
# Exemple pratique : Calcul complexe simulé
$nombres = 1..10
$monCalcul = { param($n); Start-Sleep -Milliseconds 500; return $n * $n }
Invoke-ActionParallele -ListeEntree $nombres -BlocScript $monCalcul
Automatisation avec le Planificateur de tâches
L'exécution parallèle est souvent couplée à une planification. Voici comment enregistrer un script de maintenance pour qu'il s'exécute automatiquement.
# Définition du script de maintenance
$cheminScript = "C:\Scripts\PurgeLogs.ps1"
'Get-ChildItem "C:\Logs" -Filter *.log | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } | Remove-Item' | Set-Content $cheminScript
# Configuration de la tâche planifiée
$action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-File $cheminScript"
$declencheur = New-ScheduledTaskTrigger -Weekly -At "03:00" -DaysOfWeek Sunday
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount
Register-ScheduledTask -TaskName "MaintenanceHebdoLogs" -Action $action -Trigger $declencheur -Principal $principal
Tableau Comparatif des Méthodes
| Méthode | Modèle | Performance | Compatibilité |
|---|---|---|---|
| Start-Job | Multi-processus | Faible | PS 3.0+ |
| ForEach-Object -Parallel | Multi-thread | Haute | PS 7.0+ |
| RunspacePool | Multi-thread | Optimale | PS 5.1+ |
Synthèse des bonnes pratiques
- Utilisez
ForEach-Object -Parallelpour les scripts rapides sur des environnements modernes. - Privilégiez les Runspaces pour les outils d'adminitsration destinés à être partagés sur des serveurs avec différentes versions de PowerShell.
- N'oubliez jamais de libérer les ressources (
DisposeouRemove-Job) pour éviter les fuites de mémoire. - Pour la persistance (exécution après redémarrage), utilisez
Register-ScheduledTask.