5.5 : Gestion des erreurs (try/catch, ErrorAction)

Sophie repense au script de l’ancien stagiaire, vu en 5.2 : “Un jour, ce script a planté à 3h du matin parce qu’un serveur de la liste était injoignable. Résultat : les 12 serveurs suivants n’ont jamais été vérifiés, et personne ne l’a su avant le lendemain matin.” Après plusieurs années à écrire des scripts destinés à tourner sans surveillance, la gestion des erreurs est probablement le sujet le plus sous-estimé par les débutants — et celui qui cause le plus d’incidents silencieux en production.

Deux familles d’erreurs à distinguer

PowerShell distingue deux types d’erreurs, dont la différence est essentielle pour comprendre try/catch :

  • Erreur terminante : arrête immédiatement l’exécution du script (une exception .NET classique, un accès refusé bloquant)
  • Erreur non-terminante : affichée en rouge, mais le script continue à s’exécuter normalement pour la suite

C’est cette seconde catégorie qui piège le plus de débutants : par défaut, la plupart des erreurs de cmdlets (un service introuvable, un serveur injoignable) sont non-terminantes. try/catch ne les intercepte donc pas tant qu’on ne change pas ce comportement, comme on va le voir juste après.

try / catch / finally : intercepter une erreur

try {
    Get-Service -Name "ServiceInexistant" -ErrorAction Stop
} catch {
    Write-Warning "Erreur rencontrée : $($_.Exception.Message)"
} finally {
    Write-Output "Vérification terminée, quel que soit le résultat"
}
  • Le bloc try contient le code à risque
  • Le bloc catch ne s’exécute que si une erreur terminante survient dans le try
  • Le bloc finally s’exécute toujours, erreur ou non — utile pour une action de nettoyage (fermer une connexion, par exemple)

$_ (déjà croisé dans le pipeline en 2.3) représente ici l’objet d’erreur capturé, avec sa propriété Exception.Message qui contient le texte lisible de l’erreur.

Point essentiel, source d’un piège très fréquent : sans -ErrorAction Stop ajouté explicitement à la cmdlet, la plupart des erreurs PowerShell sont non-terminantes par défaut, et le bloc catch ne se déclenchera jamais, même en cas d’erreur réelle. C’est probablement la confusion numéro un observée chez les administrateurs qui débutent avec try/catch : ils supposent, à tort, que try intercepte automatiquement toute erreur affichée en rouge dans la console.

ErrorAction : contrôler le comportement d’une erreur

Le paramètre -ErrorAction, disponible sur la quasi-totalité des cmdlets, permet de forcer leur comportement :

Valeur Comportement
Continue (par défaut) Affiche l’erreur en rouge, continue l’exécution
Stop Transforme l’erreur en erreur terminante, capturable par catch
SilentlyContinue Masque l’erreur, continue l’exécution sans rien afficher
Ignore Masque l’erreur, sans même l’enregistrer dans $Error
Get-Service -Name "ServiceInexistant" -ErrorAction SilentlyContinue

Retour d’expérience terrain : SilentlyContinue est extrêmement pratique, mais aussi extrêmement dangereux mal utilisé. Masquer une erreur sans la journaliser quelque part (avec Write-Warning, un fichier de log, ou la variable $Error) revient à transformer un problème visible en incident invisible — exactement le scénario du script de Sophie qui a laissé passer 12 serveurs sans que personne ne s’en aperçoive.

ErrorVariable : capturer l’erreur sans bloquer le script

Une alternative à try/catch, utile quand on veut simplement savoir qu’une erreur a eu lieu sans interrompre le flux du script :

Get-Service -Name "ServiceInexistant" -ErrorAction SilentlyContinue -ErrorVariable erreurService

if ($erreurService) {
    Write-Warning "Le service n'a pas pu être trouvé : $($erreurService.Exception.Message)"
}

$Error : l’historique de toutes les erreurs de la session

PowerShell conserve automatiquement un historique de toutes les erreurs survenues dans la session en cours, accessible via la variable automatique $Error :

$Error[0]          # La dernière erreur survenue
$Error.Clear()     # Vide l'historique (utile en début de script pour repartir propre)

Un exemple concret Nordika : le script qui n’échoue plus en silence

En reprenant l’exemple du script de supervision multi-serveurs évoqué en introduction, voici une version qui ne perd plus aucun serveur en cours de route :

$serveurs = "SRV-01", "SRV-02", "SRV-03-HORS-SERVICE"
$resultats = @()

foreach ($serveur in $serveurs) {
    try {
        $service = Get-Service -Name "Spooler" -ComputerName $serveur -ErrorAction Stop
        $resultats += [PSCustomObject]@{
            Serveur = $serveur
            Statut  = $service.Status
            Erreur  = $null
        }
    } catch {
        Write-Warning "$serveur injoignable : $($_.Exception.Message)"
        $resultats += [PSCustomObject]@{
            Serveur = $serveur
            Statut  = "Inconnu"
            Erreur  = $_.Exception.Message
        }
    }
}

$resultats | Format-Table -AutoSize

Ici, même si SRV-03-HORS-SERVICE échoue, la boucle continue pour les serveurs suivants, et le rapport final (Format-Table, vu en 2.5) garde une trace explicite de l’échec plutôt que de l’ignorer silencieusement.

Source officielle : la documentation Microsoft about_Try_Catch_Finally et about_CommonParameters (pour le détail complet des valeurs d’-ErrorAction) sont accessibles via Get-Help about_Try_Catch_Finally, comme pour les autres rubriques about_ déjà citées dans cette formation.

Retenez la règle la plus importante de cette leçon : un script qui échoue visiblement est un script qu’on peut corriger. Un script qui échoue en silence — par un SilentlyContinue mal placé ou un try/catch qui ne capture jamais rien faute de -ErrorAction Stop — est un script qui vous trahira au pire moment, généralement en dehors des heures de bureau.

Dans la prochaine leçon, un TP pour mettre en pratique toute la section 5 : un script de création d’utilisateurs en masse à partir d’un fichier CSV.