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 :

ValeurComportement
Continue (par défaut)Affiche l’erreur en rouge, continue l’exécution
StopTransforme l’erreur en erreur terminante, capturable par catch
SilentlyContinueMasque l’erreur, continue l’exécution sans rien afficher
IgnoreMasque 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.