Protéger la qualité des scripts PowerShell avec PSScriptAnalyzer ── choix des règles et intégration en CI

· · PowerShell, Analyse statique, CI/CD, GitHub Actions, Gestion de la qualité, Maintenabilité, Amélioration opérationnelle, Scripts

Dès que le nombre de scripts PowerShell internes augmente, le problème de « l’hétérogénéité de la qualité » finit toujours par apparaître. Un script illisible truffé d’alias, un script contenant un mot de passe en clair, un script où une faute de frappe dans un nom de variable est passée inaperçue de tous. Le problème se révèle une fois que la personne qui l’a écrit est partie, au moment où quelqu’un d’autre doit le lire.

Une bonne partie de ces problèmes peut être détectée mécaniquement par un outil d’analyse statique. PowerShell dispose d’un module officiel d’analyse statique, PSScriptAnalyzer, qui permet d’analyser un ensemble de scripts en une seule commande. Son principal atout est de produire un effet dès le premier jour, sans écrire la moindre ligne de test.

Cet article présente la démarche pratique pour déployer PSScriptAnalyzer sur le patrimoine de scripts internes de l’entreprise, dans l’ordre suivant : « les règles à activer en premier », « l’adoption progressive sur un patrimoine existant », puis « le contrôle automatique en CI ». Pour la garantie de qualité par les tests, reportez-vous également à « Mettre en place des tests PowerShell avec Pester ».

Public visé et environnement prérequis

Élément Contenu
Public visé Les personnes du service informatique ou du développement souhaitant désormais gérer mécaniquement la qualité des scripts PowerShell qui se sont accumulés en interne
Environnement d’exécution PSScriptAnalyzer s’exécute aussi bien sous Windows PowerShell 5.1 que sous PowerShell 7. Le fait que le script analysé cible la version 5.1 ou la version 7 se spécifie, indépendamment de la version utilisée pour l’analyse, via PSUseCompatibleSyntax (chapitre 4)
Prérequis pour la CI L’exemple de CI du chapitre 7 suppose un runner GitHub Actions windows-latest. La partie qui appelle Invoke-ScriptAnalyzer est identique sur d’autres CI, et l’analyse elle-même peut s’exécuter sur des runners autres que Windows
Droits nécessaires Le déploiement se fait via Install-Module -Scope CurrentUser, donc aucun droit administrateur n’est requis
Environnement de vérification des exemples Le code d’exemple distribué en fin d’article a été vérifié en l’exécutant sous PowerShell 7.6

1. La conclusion, d’abord

  • PSScriptAnalyzer est le module officiel d’analyse statique de PowerShell. Invoke-ScriptAnalyzer analyse des scripts ou des modules et signale les violations de règles.1
  • Chaque signalement possède une sévérité (Severity). Il y a trois niveaux — Error / Warning / Information —, et un point de départ réaliste consiste à d’abord ramener les Error seuls à zéro.1
  • La configuration se regroupe dans PSScriptAnalyzerSettings.psd1. En plaçant Severity, IncludeRules, ExcludeRules et Rules dans le dépôt, tout le monde analyse selon le même référentiel.2
  • Les suppressions ciblées passent par SuppressMessageAttribute accompagné d’une raison. Avant d’exclure une règle entière, examinez si une suppression à portée réduite ne suffit pas.
  • Certains signalements se corrigent automatiquement avec -Fix. La mise en forme, elle, est prise en charge par Invoke-Formatter.13
  • L’extension PowerShell de VS Code intègre PSScriptAnalyzer. Les avertissements apparaissent directement pendant l’édition, ce qui agit avant même la CI.4
  • La condition d’échec de la CI doit être « sévérité Error + règles critiques nommées explicitement ». La sévérité est fixée règle par règle, et la détection des mots de passe en clair (PSAvoidUsingPlainTextForPassword) est de sévérité Warning. Se limiter à la sévérité Error laisse passer ce cas.5
  • Adoption progressive pour le patrimoine existant. Dans l’ordre : « ramener les Error à zéro » → « analyser strictement seulement les fichiers modifiés » → « élargir le périmètre ».

2. Le déploiement ── d’abord, une seule commande

Install-Module -Name PSScriptAnalyzer -Scope CurrentUser

# Analyser en bloc tout le contenu d'un dossier
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
    Sort-Object Severity, RuleName |
    Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize

# Obtenir le nombre par sévérité (première étape de l'inventaire)
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
    Group-Object Severity | Select-Object Name, Count

Commencez par exécuter ces deux commandes pour prendre la mesure chiffrée de l’état de votre patrimoine. Ne soyez pas surpris si plusieurs centaines de signalements apparaissent : c’est le cas dans la plupart des environnements au démarrage.

Ce qui est retourné. Invoke-ScriptAnalyzer retourne chaque signalement sous la forme d’un objet, avec des propriétés telles que Severity, RuleName, ScriptName, Line et Message. La première commande les liste, triés par sévérité, ligne par ligne, sous la forme « quel fichier, quelle ligne, quelle règle, pourquoi ». La seconde ne retourne que deux colonnes, Name (Error / Warning / Information) et Count : notez d’abord ces quelques chiffres. La progression de l’adoption progressive (chapitre 6) se mesure par l’évolution de ces chiffres. S’il n’y a aucun signalement, ni l’une ni l’autre commande n’affiche rien (une sortie vide = c’est bon).

La liste des règles disponibles et leur description s’obtiennent avec Get-ScriptAnalyzerRule.1

Get-ScriptAnalyzerRule | Select-Object Severity, RuleName, CommonName | Sort-Object Severity
Get-ScriptAnalyzerRule -Name PSAvoidUsingWriteHost | Format-List *   # description d'une règle précise

3. Les signalements les plus utiles ── priorités en pratique

Parmi la quarantaine de règles disponibles, voici, classées par priorité, celles qui touchent directement à la qualité des scripts internes.

Règle Sévérité Ce qu’elle détecte Pourquoi c’est important
PSAvoidUsingPlainTextForPassword Warning Un mot de passe en clair reçu en paramètre La conservation en clair d’identifiants est aussi relevée en audit5
PSAvoidUsingConvertToSecureStringWithPlainText Error Un SecureString construit à partir de texte en clair Même racine que ci-dessus. Rend le chiffrement sans objet
PSUseDeclaredVarsMoreThanAssignments Warning Une variable affectée mais jamais utilisée Permet de détecter une faute de frappe dans un nom de variable. Une détection de bug bien réelle
PSAvoidUsingInvokeExpression Warning L’utilisation d’Invoke-Expression Exécute une chaîne comme du code, ce qui ouvre la porte aux injections
PSUseShouldProcessForStateChangingFunctions Warning Une fonction modifiant un état sans -WhatIf Détecte une conception qui ne permet pas de confirmer une opération risquée à l’avance
PSAvoidUsingCmdletAliases Warning Des alias comme ls, %, ? Pratique en interactif, mais nuit à la lisibilité dans un script
PSUseApprovedVerbs Warning Un nom de fonction avec un verbe non approuvé Sans la convention Get-/Set- etc., la fonction est plus difficile à découvrir
PSAvoidGlobalVars Warning L’utilisation de variables globales Rend les effets de bord illisibles. Empêche aussi d’écrire des tests
PSUseSingularNouns Warning Un nom au pluriel (Get-Users, etc.) Convention de nommage de PowerShell. Un nom que les autres peuvent deviner

Regardez la colonne de sévérité. Sur ces 9 règles, une seule est de sévérité Error ; toutes les autres sont Warning.6 Même la détection des mots de passe en clair (PSAvoidUsingPlainTextForPassword) est en Warning, si bien que limiter la condition d’échec de la CI à « sévérité Error uniquement » laisse passer la quasi-totalité de ce tableau. La sévérité étant fixée règle par règle, toute règle que vous voulez faire échouer indépendamment de sa sévérité doit être désignée par son nom (chapitres 6 et 7). Pour vérifier chez vous : Get-ScriptAnalyzerRule | Select-Object Severity, RuleName.

PSUseDeclaredVarsMoreThanAssignments mérite une mention particulière pour son rapport bénéfice/coût. Elle capture, sous la forme d’« une variable affectée mais jamais utilisée », des fautes de frappe du type « on croit avoir affecté $fileName, mais on référence ensuite $fileNmae » — c’est un cas réel de bug qu’on ne trouve que par analyse statique (notez que les noms de variables PowerShell ne sont pas sensibles à la casse, donc $fileName et $filename désignent la même variable ; ce que cette détection capture, ce sont les cas où l’orthographe elle-même diffère).

4. Fixer le référentiel de l’équipe avec un fichier de configuration

Si chacun analyse selon un référentiel différent, cela n’a pas de sens. Placez PSScriptAnalyzerSettings.psd1 dans le dépôt, afin que tout le monde et la CI utilisent la même configuration.2

# PSScriptAnalyzerSettings.psd1
@{
    # Utiliser le jeu de règles par défaut
    IncludeDefaultRules = $true

    # Pour la première étape de l'adoption progressive, se limiter à Error et Warning
    Severity = @('Error', 'Warning')

    # Règles reportées comme choix interne pour l'instant (raison en commentaire)
    ExcludeRules = @(
        'PSAvoidUsingWriteHost'          # Beaucoup d'outils interactifs, toléré pour l'instant
        'PSUseSingularNouns'             # Impossible de renommer d'un coup toutes les fonctions existantes
    )

    # Réglages détaillés par règle
    Rules = @{
        PSUseCompatibleSyntax = @{
            # Vérifier les scripts devant fonctionner à la fois en 5.1 et en 7.
            # Seules les versions pour lesquelles la règle possède une définition de syntaxe
            # peuvent être indiquées dans TargetVersions (vérifiable avec Get-ScriptAnalyzerRule).
            # Une valeur non prise en charge provoque une erreur au chargement de la configuration
            Enable         = $true
            TargetVersions = @('5.1', '7.0')
        }
        PSPlaceOpenBrace = @{
            Enable             = $true
            OnSameLine         = $true
            NewLineAfter       = $true
            IgnoreOneLineBlock = $true
        }
        PSUseConsistentIndentation = @{
            Enable          = $true
            IndentationSize = 4
            Kind            = 'space'
        }
    }
}
Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1

Ce fichier de configuration est également lu par l’extension VS Code. La valeur par défaut du paramètre powershell.scriptAnalysis.settingsPath de l’extension PowerShell est PSScriptAnalyzerSettings.psd1 : en plaçant le fichier sous ce nom à la racine du dépôt, les avertissements affichés pendant l’édition et le critère de jugement de la CI s’alignent automatiquement.7 Si vous utilisez un autre nom ou le placez dans un sous-dossier, indiquez explicitement le chemin dans ce paramètre. Un écart à ce niveau produit le phénomène « rien ne s’affiche en local, mais ça échoue en CI », qui discrédite le bénéfice du retour immédiat.

PSUseCompatibleSyntax est particulièrement utile dans un environnement où 5.1 et 7 coexistent. Elle permet de détecter avant exécution l’accident consistant à écrire, dans un script destiné à 5.1, une syntaxe propre à la version 7 (opérateur ternaire, opérateur de chaînage de pipeline, etc.). Pour la politique de migration elle-même, reportez-vous à « Les différences entre Windows PowerShell 5.1 et PowerShell 7 ».

5. Conserver les exceptions « avec leur raison »

Lorsqu’il est vraiment impossible de se conformer à un signalement, ne désactivez pas la règle entière : supprimez-la uniquement à cet endroit précis.8

function Show-KsBanner {
    # Fonction d'affichage décoratif pour un outil interactif ; usage intentionnel de Write-Host
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidUsingWriteHost', '',
        Justification = 'Fonction d''affichage réservée à l''exécution interactive. Ne retourne pas de valeur par conception')]
    [CmdletBinding()]
    param([string] $Title)

    Write-Host ('=' * 60) -ForegroundColor Cyan
    Write-Host $Title -ForegroundColor Cyan
}

Le point essentiel est d’écrire systématiquement Justification. Une suppression sans raison est, pour la prochaine personne qui la lit, indiscernable d’un simple « avertissement fait taire ». C’est l’équivalent, dans le code, d’un ADR (enregistrement de décision d’architecture) ; la logique rejoint ce qui est décrit dans « Utiliser les ADR (enregistrements de décision d’architecture) dans une petite équipe ».

6. Adoption progressive sur un patrimoine existant

Face à plusieurs centaines d’avertissements, se dire « on corrige tout avant de déployer » mène droit à l’échec. Découpez la démarche en étapes.

Étape 1 : arrêter l’hémorragie (1 jour) Mettez en CI uniquement les signalements Severity = 'Error', et ramenez-les à zéro. Attention, la sévérité est fixée règle par règle et ne correspond pas toujours à l’intuition. Par exemple, la sévérité de PSAvoidUsingPlainTextForPassword est Warning : en ne conditionnant l’échec qu’à Error, elle ne se déclenche pas.5 Pour les règles qu’on veut faire échouer indépendamment de leur sévérité — typiquement tout ce qui touche aux identifiants —, ajoutez-les à la condition d’échec en les nommant explicitement, comme ci-dessous.

# Récupérer d'abord le résultat de l'analyse
$issues = Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1

# La condition d'échec de la CI = sévérité Error + règles critiques désignées individuellement
$mustFix = @(
    'PSAvoidUsingPlainTextForPassword'
    'PSAvoidUsingConvertToSecureStringWithPlainText'
    'PSAvoidUsingUsernameAndPasswordParams'
)
$blocking = $issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix }

Étape 2 : protéger le nouveau et le modifié (1 semaine) N’analysez que les fichiers modifiés. La dette existante reste en l’état, mais cela empêche l’apparition de nouveaux problèmes.

Pour l’exécuter en CI, le commit de comparaison doit avoir été récupéré au préalable. actions/checkout ne récupère par défaut qu’un seul commit, donc indiquez fetch-depth: 0, ou récupérez explicitement la branche de base (sans cela, l’opération échoue avec unknown revision).

Autre point : ne fixez pas la cible de comparaison sur main. git diff A...HEAD signifie « la différence depuis l’ancêtre commun de A et HEAD » ; utiliser origin/main...HEAD pour une PR ciblant develop ou une branche de release fait donc entrer dans l’analyse des modifications que cette PR n’a pas touchées, et la CI échoue à cause de signalements existants sur des fichiers sans rapport. Dans GitHub Actions, la branche cible de la PR est disponible dans GITHUB_BASE_REF : utilisez-la.9

De plus, détectez impérativement les échecs de git. Par défaut, PowerShell ne considère pas comme une erreur terminale le fait qu’une commande externe retourne un code de sortie non nul.10 Ainsi, si la ref de base n’a pas été récupérée, git diff échoue et la sortie est simplement vide ; la suite interprète cela comme « aucun fichier modifié », et la CI passe au vert sans avoir analysé le moindre fichier. C’est la panne la plus dangereuse pour un contrôle par différence. Vérifiez $LASTEXITCODE immédiatement après l’exécution et faites échouer explicitement (à partir de PowerShell 7.3, il est aussi possible de définir $PSNativeCommandUseErrorActionPreference = $true).10

      - uses: actions/checkout@v4
        with:
          fetch-depth: 0        # L'historique est nécessaire pour obtenir la différence
# N'analyser que les ps1/psm1 modifiés (contrôle par différence en CI)
# Ne pas fixer la cible de comparaison sur main. Pour une PR ciblant develop
# ou une branche de release, la différence avec main inclurait des modifications
# non liées, et la CI échouerait sur des fichiers non touchés.
# La branche cible de la PR se récupère via GITHUB_BASE_REF (vide lors d'un push)
$base = if ($env:GITHUB_BASE_REF) { "origin/$($env:GITHUB_BASE_REF)" } else { 'origin/main' }

# Sans -c core.quotePath=false, les chemins contenant des caractères japonais
# reviennent sous la forme "scripts/\346..." (guillemets + échappement octal),
# ce qui les fait échapper à la détection par extension
$diff = git -c core.quotePath=false diff --name-only "$base...HEAD"

# Par défaut, l'échec d'une commande native n'est pas une erreur terminale. Si la
# ref de base n'est pas récupérée, git échoue et la sortie est vide : cela se lit
# comme « aucune modification = aucune cible d'analyse = succès » et passe inaperçu.
# Vérifier $LASTEXITCODE juste après et échouer explicitement
if ($LASTEXITCODE -ne 0) {
    throw "Échec de git diff (exit $LASTEXITCODE). La branche de base $base n'a peut-être pas été récupérée"
}

$changed = $diff |
    Where-Object { $_ -match '\.ps(m|d)?1$' } |   # Cibler .ps1 / .psm1 / .psd1
    Where-Object { Test-Path $_ }

# -Path est un paramètre qui n'accepte qu'un seul chemin : lui passer un tableau
# tel quel échoue au moment du binding des paramètres. Analyser fichier par fichier
# et agréger les résultats
$issues = foreach ($file in $changed) {
    Invoke-ScriptAnalyzer -Path $file -Settings .\PSScriptAnalyzerSettings.psd1
}

Étape 3 : élargir le périmètre (en continu) Retirez ExcludeRules une règle à la fois, et devenez plus strict au fur et à mesure des corrections. Profitez des occasions de refactorisation pour corriger les fichiers existants et réduire la dette.

Les signalements corrigibles automatiquement peuvent être traités en masse avec -Fix (vérifiez toujours la différence avant d’appliquer).1 Pour la seule mise en forme, Invoke-Formatter suffit.3

Invoke-ScriptAnalyzer -Path .\Scripts -Recurse -Fix -Settings .\PSScriptAnalyzerSettings.psd1
git diff        # Toujours vérifier visuellement ce qui a changé

7. Automatiser en CI

Avec GitHub Actions, il suffit de quelques lignes sur un runner Windows. Le point clé est de faire échouer sur Error, et de se contenter d’afficher les Warning.

name: powershell-lint

on:
  pull_request:
    paths: ['**/*.ps1', '**/*.psm1', '**/*.psd1']

jobs:
  analyze:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0        # Nécessaire si vous passez à l'analyse par différence (chapitre 6)

      - name: Install PSScriptAnalyzer
        shell: pwsh
        run: |
          Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
          Install-Module PSScriptAnalyzer -Scope CurrentUser -Force

      - name: Analyze
        shell: pwsh
        run: |
          $issues = Invoke-ScriptAnalyzer -Path . -Recurse `
                    -Settings ./PSScriptAnalyzerSettings.psd1

          # Journaliser l'ensemble des résultats (rendre aussi les Warning visibles)
          $issues | Sort-Object Severity, ScriptName, Line |
              Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize |
              Out-String -Width 200 | Write-Host

          # Condition d'échec = sévérité Error + règles à ne tolérer quelle que soit leur sévérité
          $mustFix = @(
              'PSAvoidUsingPlainTextForPassword'
              'PSAvoidUsingConvertToSecureStringWithPlainText'
              'PSAvoidUsingUsernameAndPasswordParams'
          )
          $blocking = @($issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix })
          $warns    = @($issues | Where-Object Severity -eq 'Warning')
          Write-Host "Blocking: $($blocking.Count) / Warning: $($warns.Count)"

          # Une fois l'adoption progressive avancée, ajouter aussi les Warning à la condition
          if ($blocking.Count -gt 0) {
              throw "$($blocking.Count) signalement(s) à corriger"
          }

En regroupant cela dans le même flux de travail que les tests Pester, vous obtenez l’enchaînement « le lint passe → les tests passent → on peut fusionner ». Pour la structure générale de la CI/CD des applications Windows, reportez-vous à « Mettre en pratique la CI/CD des applications WinForms / WPF ».

Même sans environnement de CI, faire tourner Invoke-ScriptAnalyzer chaque mois et conserver les résultats en CSV suffit largement à rendre visible l’état du patrimoine.

Invoke-ScriptAnalyzer -Path '\\fileserver\scripts' -Recurse |
    Select-Object Severity, RuleName, ScriptName, Line, Message |
    Export-Csv "D:\Inventaire\lint_$(Get-Date -f yyyyMM).csv" -Encoding utf8BOM -NoTypeInformation

8. Les règles de l’art en pratique (tableau de décision)

Point de décision Options Critère de jugement
Ordre d’adoption Commencer par Pester / Commencer par PSScriptAnalyzer L’analyse statique agit dès le premier jour, sans écrire de tests
Première cible Toutes les règles / Severity=Error + règles liées aux identifiants nommées explicitement Conditionner tout fait que personne ne passe. Attention, la sévérité est fixée règle par règle5
Avertissements existants en grand nombre Tout corriger / Rester strict uniquement sur les fichiers modifiés Arrêter la croissance d’abord, réduire ensuite selon les occasions
Exception ponctuelle ExcludeRules / SuppressMessageAttribute + Justification Minimiser la portée. Toujours conserver la raison8
Partage de la configuration Configuration individuelle / .psd1 dans le dépôt Aligner le référentiel entre la CI et les développeurs2
Coexistence de 5.1 et 7 Vérifier en exécutant / PSUseCompatibleSyntax Détecter les incompatibilités de syntaxe avant exécution
Correction automatique Manuellement / -Fix + vérification de la différence Toujours regarder git diff après application1
Retour pendant l’édition CI uniquement / Extension VS Code Pouvoir corriger sur le champ est ce qui coûte le moins cher4

9. Conclusion

  • PSScriptAnalyzer est le module officiel d’analyse statique ; il se déploie sans écrire de tests et produit un effet dès le premier jour.
  • Il est réaliste d’adopter une démarche progressive : d’abord ramener les Error à zéro, puis analyser strictement uniquement les fichiers modifiés. La sévérité étant fixée règle par règle, les règles qu’on souhaite faire échouer, comme celle des mots de passe en clair (Warning), doivent être ajoutées à la condition d’échec par leur nom.
  • Certaines règles, comme PSUseDeclaredVarsMoreThanAssignments, capturent un bug réel — une faute de frappe dans un nom de variable.
  • La configuration se centralise dans PSScriptAnalyzerSettings.psd1, placé dans le dépôt, pour aligner le référentiel entre les développeurs et la CI.
  • Les exceptions se conservent via SuppressMessageAttribute accompagné d’une raison. Exclure une règle entière est le dernier recours.
  • En CI, faire échouer sur Error et se contenter d’afficher les Warning. Intégré au même flux de travail que Pester, le contrôle qualité se regroupe en un seul point.

Téléchargement du code d’exemple

Le code traité dans cet article est distribué sous une forme directement exécutable. Il comprend le fichier de configuration, le script de décision de succès/échec pour la CI et l’exemple GitHub Actions.

Télécharger le code d’exemple (zip)

Les exemples de cet article ont été vérifiés en les exécutant réellement sous PowerShell 7.6 (14 tests Pester). En exécutant Invoke-SampleTests.ps1, inclus dans le zip, vous pouvez reproduire la même vérification chez vous.

# Analyse syntaxique + analyse statique + tests Pester
./Invoke-SampleTests.ps1

Les valeurs (chemins, noms de serveurs, ID de tenant, etc.) sont des exemples. Ne les exécutez pas telles quelles en production : adaptez-les à votre propre environnement.

Articles connexes

Domaines de conseil associés

合同会社小村ソフト (Komura Software LLC) prend en charge l’inventaire du patrimoine de scripts internes et la définition de standards de qualité, le déploiement de l’analyse statique et des tests en CI, ainsi que l’amélioration de la maintenabilité des scripts opérationnels devenus dépendants d’une seule personne.

Références

  1. Microsoft Learn, Présentation du module PSScriptAnalyzer. Sur le fait que PSScriptAnalyzer est un outil d’analyse statique pour les scripts et modules PowerShell, l’analyse via Invoke-ScriptAnalyzer et ses paramètres -Path / -Recurse / -Settings / -Fix / -ExcludeRule, l’obtention de la liste des règles via Get-ScriptAnalyzerRule, et le fait que les résultats de diagnostic possèdent une sévérité (Error / Warning / Information).  2 3 4 5 6

  2. Microsoft Learn, Fichier de configuration de PSScriptAnalyzer. Sur le fait qu’un fichier de configuration (.psd1) peut spécifier Severity, IncludeRules, ExcludeRules, IncludeDefaultRules et Rules, que le paramètre -Settings permet de transmettre ce fichier, et sur les réglages détaillés par règle (TargetVersions de PSUseCompatibleSyntax, options des règles de mise en forme).  2 3

  3. Microsoft Learn, Invoke-Formatter. Sur la mise en forme du texte d’un script selon la configuration, et sur le fait que les règles de mise en forme (indentation, position de l’accolade ouvrante, gestion des espaces, etc.) peuvent être spécifiées dans le fichier de configuration.  2

  4. Microsoft Learn, Utiliser PowerShell dans Visual Studio Code. Sur le fait que l’extension PowerShell utilise PSScriptAnalyzer pour afficher des avertissements pendant l’édition, et qu’elle fournit une fonction de mise en forme.  2

  5. Microsoft Learn, AvoidUsingPlainTextForPassword. Sur le fait qu’un mot de passe ou une information secrète ne devrait pas être reçu comme paramètre de type chaîne en clair, mais via SecureString ou PSCredential, et que la sévérité (Severity Level) de cette règle est Warning, toujours active. Voir aussi la règle associée AvoidUsingConvertToSecureStringWithPlainText (le fait que générer un SecureString à partir de texte en clair ne protège pas le secret).  2 3 4

  6. Microsoft Learn, Liste des règles de PSScriptAnalyzer. Sur le fait que la liste des règles intégrées est présentée sous forme de tableau, avec pour chacune sa sévérité (Severity), son activation par défaut et sa configurabilité. La sévérité des règles citées dans le tableau de l’article (AvoidUsingConvertToSecureStringWithPlainText en Error ; AvoidUsingPlainTextForPassword, UseDeclaredVarsMoreThanAssignments, AvoidUsingInvokeExpression, UseShouldProcessForStateChangingFunctions, AvoidUsingCmdletAliases, UseApprovedVerbs, AvoidGlobalVars et UseSingularNouns en Warning), ainsi que le fait qu’AvoidUsingUsernameAndPasswordParams, nommée aux chapitres 6 et 7, soit en Error, reposent sur cette liste et les pages individuelles de chaque règle. 

  7. PowerShell/vscode-powershell, package.json (définition des paramètres de l’extension). Sur le fait que powershell.scriptAnalysis.settingsPath désigne le chemin du fichier de configuration de PSScriptAnalyzer, avec pour valeur par défaut PSScriptAnalyzerSettings.psd1, et que powershell.scriptAnalysis.enable permet d’activer ou de désactiver l’analyse en temps réel pendant l’édition. 

  8. Microsoft Learn, Suppression des règles PSScriptAnalyzer. Sur le fait que System.Diagnostics.CodeAnalysis.SuppressMessageAttribute permet de supprimer un diagnostic par règle ou par cible, et sur les arguments RuleName, Target et Justification.  2

  9. GitHub Docs, Référence des variables ─ Variables d’environnement par défaut. Sur le fait que, lors d’un événement pull_request, GITHUB_BASE_REF contient le nom de la branche cible de la PR (vide pour les autres événements). Pour la signification de la notation à trois points (la différence depuis la base de fusion des deux refs indiquées), voir git diff dans la documentation officielle de Git. 

  10. Microsoft Learn, about_Preference_Variables ─ $PSNativeCommandUseErrorActionPreference. Sur le fait qu’un code de sortie non nul d’une commande native n’est par défaut pas une erreur terminale, que ce réglage introduit dans PowerShell 7.3, une fois activé à $true, applique $ErrorActionPreference pour en faire une erreur terminale, et que le code de sortie de la dernière commande externe s’obtient via $LASTEXITCODE.  2

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

En exécutant PSScriptAnalyzer sur nos scripts existants, nous avons obtenu plusieurs centaines d'avertissements. Par où commencer ?
N'essayez pas de tout corriger d'un coup. La démarche pratique consiste d'abord à ne cibler que la sévérité Error, et à la ramener à zéro. La sévérité est fixée règle par règle : par exemple, PSAvoidUsingPlainTextForPassword, qui détecte les mots de passe en clair, est de sévérité Warning. Si certaines règles doivent bloquer la CI quelle que soit leur sévérité — typiquement tout ce qui touche aux identifiants — nommez-les explicitement dans la condition d'échec de la CI. Ensuite, ajoutez à la CI une règle qui n'analyse que les fichiers en cours de modification, pour empêcher l'apparition de nouveaux problèmes. Les avertissements existants sont, pour l'instant, tolérés et exclus dans le fichier de configuration ; il est réaliste de les réduire un par un, au fil des refactorisations.
Je voudrais supprimer un avertissement uniquement à un endroit précis. Comment faire ?
Ajoutez SuppressMessageAttribute à la fonction ou au script concerné. Indiquez le nom de la règle dans System.Diagnostics.CodeAnalysis.SuppressMessageAttribute, et écrivez la raison dans Justification. Il est important d'écrire cette raison : elle permet à quiconque relira le code plus tard de comprendre pourquoi il s'agit d'une exception. Pour désactiver une règle entière, on la place dans ExcludeRules du fichier de configuration, mais cela a une portée beaucoup plus large : envisagez d'abord si une suppression ciblée ne suffit pas.
Utiliser Write-Host déclenche un avertissement. Est-ce interdit ?
PSAvoidUsingWriteHost signale, sur le plan de la conception, qu'utiliser Write-Host là où une valeur devrait être retournée empêche de récupérer cette sortie. Si l'objectif est un affichage décoratif dans un outil interactif, il est légitime de le supprimer via SuppressMessageAttribute en y indiquant la raison. En revanche, si un script destiné à une exécution non surveillée n'utilise que Write-Host, il vaut la peine de le revoir, comme le suggère l'avertissement. Ne suivez pas les règles mécaniquement : comprenez l'intention du signalement et jugez au cas par cas.
Entre Pester et PSScriptAnalyzer, lequel adopter en premier ?
Il est préférable d'introduire PSScriptAnalyzer en premier, car son rapport bénéfice/coût de mise en place est plus élevé. Sans écrire une seule ligne de test, une seule commande permet d'analyser tous les scripts, avec un effet visible dès le premier jour. Pester demande d'écrire des tests, ce qui prend plus de temps à démarrer, mais seuls les tests garantissent la correction de la logique. Comme ordre, nous recommandons de commencer par mettre l'analyse statique en CI pour arrêter les problèmes évidents, puis d'ajouter des tests Pester en priorité sur les traitements dont la casse serait problématique.
Est-ce utile même pour une petite équipe sans serveur de CI ?
Oui. Même sans Git ni CI, exécuter simplement Invoke-ScriptAnalyzer -Path . -Recurse sur l'ensemble des scripts d'un dossier partagé constitue déjà un inventaire. Exporter les résultats en CSV et suivre chaque mois « combien y a-t-il d'occurrences de sévérité Error » suffit à rendre visible l'état du patrimoine. De plus, l'extension PowerShell de VS Code intègre PSScriptAnalyzer, donc les avertissements apparaissent directement pendant l'édition. Cela seul améliore déjà sensiblement les habitudes d'écriture.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog