Gestion des erreurs en PowerShell

2025-03-21Équipe pwsh.frIntermédiaire~14 min de lecture

Erreurs terminantes et non terminantes

PowerShell distingue deux catégories d'erreurs. Les erreurs terminantes (terminating errors) interrompent immédiatement l'exécution du bloc courant — ce sont celles que try/catch peut attraper nativement. Les erreurs non terminantes (non-terminating errors) affichent un message en rouge mais laissent le script continuer ; elles sont écrites dans le flux $Error et peuvent être promues en erreurs terminantes via -ErrorAction Stop.

TypeComportement par défautCapturable par try/catch ?
TerminanteArrête le bloc courant✅ Oui
Non terminanteContinue l'exécution❌ Non (sauf avec -ErrorAction Stop)

$ErrorActionPreference

Cette variable globale contrôle le comportement par défaut de toutes les erreurs non terminantes dans votre session ou script :

# Valeurs possibles :
$ErrorActionPreference = 'Continue'     # Défaut : affiche l'erreur et continue
$ErrorActionPreference = 'Stop'         # Transforme toutes les erreurs en terminantes
$ErrorActionPreference = 'SilentlyContinue'  # Masque l'erreur, continue silencieusement
$ErrorActionPreference = 'Inquire'      # Demande confirmation à l'utilisateur

# Bonne pratique en début de script robuste :
$ErrorActionPreference = 'Stop'

Vous pouvez aussi surcharger ce comportement pour une seule cmdlet via le paramètre commun -ErrorAction :

# N'affiche pas l'erreur si le fichier est absent
Get-Item 'C:\inexistant.txt' -ErrorAction SilentlyContinue

# Force la capture par try/catch pour cette cmdlet uniquement
Get-Item 'C:\inexistant.txt' -ErrorAction Stop

try / catch / finally

Le bloc try contient le code susceptible de lever une erreur terminante. Le bloc catch est exécuté si une erreur est interceptée. Le bloc finally s'exécute toujours, qu'il y ait eu erreur ou non.

try {
    $contenu = Get-Content 'C:\data\config.json' -ErrorAction Stop
    $json    = $contenu | ConvertFrom-Json
    Write-Host "Fichier chargé : $($json.nom)"
}
catch [System.IO.FileNotFoundException] {
    Write-Warning "Fichier introuvable : $_"
}
catch [System.Exception] {
    Write-Error "Erreur inattendue : $($_.Exception.Message)"
}
finally {
    Write-Host 'Nettoyage terminé.' -ForegroundColor DarkGray
}

Le paramètre $_ dans le bloc catch contient l'objet ErrorRecord courant. Ses propriétés les plus utiles :

catch {
    $_.Exception.Message      # Message d'erreur lisible
    $_.Exception.GetType().FullName  # Type .NET de l'exception
    $_.ScriptStackTrace       # Pile d'appels PowerShell
    $_.InvocationInfo.Line    # Numéro de ligne
    $_.FullyQualifiedErrorId  # Identifiant unique de l'erreur
}

Attraper plusieurs types d'exceptions

try {
    $result = Invoke-WebRequest 'https://api.exemple.com/data' -ErrorAction Stop
}
catch [System.Net.WebException] {
    Write-Warning "Problème réseau : $($_.Exception.Message)"
}
catch [System.UnauthorizedAccessException] {
    Write-Error 'Accès refusé. Vérifiez vos permissions.'
}
catch {
    # Filet de sécurité : attrape tout le reste
    Write-Error "Erreur non gérée : $_"
}

La variable $Error

PowerShell maintient automatiquement un historique des erreurs dans la variable automatique $Error, qui est une liste circulaire de 256 entrées par défaut.

$Error[0]                  # Dernière erreur survenue
$Error[0].Exception.Message
$Error.Count               # Nombre d'erreurs dans l'historique
$Error.Clear()             # Vider l'historique

# Changer la taille de l'historique
$MaximumErrorCount = 50

Write-Error vs Throw

Deux manières de générer manuellement une erreur, avec des comportements différents :

# Write-Error : génère une erreur NON terminante (le script continue)
Write-Error "Paramètre invalide : $Valeur"

# Throw : génère une erreur TERMINANTE (capturable par try/catch)
throw "Le fichier de configuration est manquant."

# Throw avec un objet exception typé
throw [System.IO.FileNotFoundException] "config.json introuvable"

-ErrorVariable

Le paramètre commun -ErrorVariable capture les erreurs d'une cmdlet dans une variable de votre choix, sans interrompre l'exécution :

Get-Item 'C:\inexistant.txt' -ErrorAction SilentlyContinue -ErrorVariable monErreur

if ($monErreur) {
    Write-Warning "Erreur capturée : $($monErreur[0].Exception.Message)"
}

Exemple pratique : script robuste de sauvegarde

Un script réel qui illustre toutes ces techniques ensemble :

#Requires -Version 5.1
[CmdletBinding()]
param(
    [Parameter(Mandatory)][string] $Source,
    [Parameter(Mandatory)][string] $Destination,
    [string] $LogFile = 'C:\Logs\backup.log'
)

$ErrorActionPreference = 'Stop'

function Write-Log {
    param([string]$Message, [string]$Niveau = 'INFO')
    $ligne = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Niveau] $Message"
    Add-Content -Path $LogFile -Value $ligne
    Write-Host $ligne -ForegroundColor $(if ($Niveau -eq 'ERROR') {'Red'} else {'Gray'})
}

try {
    Write-Log "Début de la sauvegarde : $Source → $Destination"

    if (-not (Test-Path $Source)) {
        throw "Dossier source introuvable : $Source"
    }

    if (-not (Test-Path $Destination)) {
        New-Item -Path $Destination -ItemType Directory | Out-Null
        Write-Log "Dossier de destination créé : $Destination"
    }

    $fichiers = Get-ChildItem -Path $Source -Recurse -File
    $compteur = 0

    foreach ($fichier in $fichiers) {
        try {
            $cible = $fichier.FullName.Replace($Source, $Destination)
            $dossierCible = Split-Path $cible

            if (-not (Test-Path $dossierCible)) {
                New-Item -Path $dossierCible -ItemType Directory | Out-Null
            }

            Copy-Item -Path $fichier.FullName -Destination $cible -Force
            $compteur++
        }
        catch {
            Write-Log "Impossible de copier $($fichier.Name) : $_" -Niveau 'ERROR'
            # On continue malgré l'erreur sur ce fichier
        }
    }

    Write-Log "Sauvegarde terminée : $compteur fichier(s) copié(s)."
}
catch {
    Write-Log "ERREUR CRITIQUE : $_" -Niveau 'ERROR'
    exit 1
}
finally {
    Write-Log "Script terminé."
}