MAIN MENU
Le blogue Devolutions

Annonces, mises à jour et analyses de Devolutions

PowerShell Universal custom PSScriptAnalyzer rules illustration for the Devolutions blog.

Créer des règles PSScriptAnalyzer personnalisées pour PowerShell Universal

Parcourez les règles par défaut de PSScriptAnalyzer, les fichiers de paramètres, et une règle personnalisée PowerShell Universal qui signale les affectations aux variables intégrées à l’aide de ScriptBlockAst et DiagnosticRecord.

Dans le cadre de la version 5 de PowerShell Universal, nous ajoutons des règles PSScriptAnalyzer personnalisées à la plateforme pour faciliter la détection des problèmes lors de la création de scripts. Dans cet article, nous allons voir comment créer des règles PSScriptAnalyzer personnalisées.

PSScriptAnalyzer

PSScriptAnalyzer est un outil d’analyse statique du code pour les scripts PowerShell. Il vérifie la qualité du code selon un ensemble de règles. Les règles intégrées couvrent toutes sortes de points, comme l’usage des alias, les espaces en fin de ligne, les vérifications $null, et plus encore. Dans des éditeurs comme VS Code, PSScriptAnalyzer s’exécute en arrière-plan pour fournir une rétroaction en temps réel sur vos scripts.

En interne, l’extension PowerShell exécute Invoke-ScriptAnalyzer pour fournir cette rétroaction. Vous pouvez lancer cette commande vous-même pour voir les résultats de l’analyse. L’exécution avec seulement le chemin applique toutes les règles par défaut.

Invoke-ScriptAnalyzer -Path .\MyScript.ps1

Vous pouvez aussi exclure des règles à l’aide des paramètres de l’applet de commande.

Invoke-ScriptAnalyzer -Path .\MyScript.ps1 -ExcludeRule PSAvoidUsingCmdletAliases

Pour personnaliser le comportement de PSScriptAnalyzer sans modifier la ligne de commande, vous pouvez aussi utiliser un fichier de configuration. Ce fichier couvre les mêmes options que les différents paramètres de Invoke-ScriptAnalyzer.

# PSScriptAnalyzerSettings.psd1
@{
    Severity=@('Error','Warning')
    ExcludeRules=@('PSAvoidUsingCmdletAliases', 'PSAvoidUsingWriteHost')
}

Vous pouvez utiliser des fichiers de paramètres de l’analyseur de scripts en passant le paramètre -Settings à Invoke-ScriptAnalyzer.

Invoke-ScriptAnalyzer -Path MyScript.ps1 -Settings PSScriptAnalyzerSettings.psd1

Vous pouvez aussi utiliser ce fichier de paramètres avec VS Code en définissant PowerShell > Script Analysis: Settings Path sur PSScriptAnalyzerSettings.psd1.

VS Code PowerShell Script Analysis Settings Path pointing at PSScriptAnalyzerSettings.psd1.
Chemin des paramètres PSScriptAnalyzer dans VS Code

Règles personnalisées

Les règles personnalisées sont définies à l’aide de modules PowerShell. Elles peuvent accepter une liste de jetons ou un arbre de syntaxe abstraite (AST) et renvoyer des enregistrements de diagnostic. Par exemple, dans PowerShell Universal, nous avons une règle qui vérifie l’utilisation de variables intégrées comme $PSUEnvironment ou $UniversalClient. Affecter ces variables peut provoquer un comportement indéfini, alors nous voulons que les utilisateurs le sachent dès qu’ils tentent de les remplacer.

La première étape consiste à créer un nouveau fichier de module .psm1 et à définir une fonction qui analysera le script. Dans cet exemple, nous avons choisi Universal.Rules.psm1 comme nom de fichier et défini la fonction Measure-PSUBuiltInVariables. L’aide basée sur les commentaires est obligatoire, et certains détails précis de cette fonction doivent être notés.

D’abord, vous devez inclure l’attribut OutputType pour indiquer le type d’objet que la fonction renverra. Dans ce cas, elle renverra un tableau d’objets Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord. Vous devez aussi accepter un objet ScriptBlockAst comme paramètre. Cet objet représente le bloc de script du script en cours d’analyse.

<#
.SYNOPSIS
    Locates built-in PowerShell Universal variables
.DESCRIPTION
    Checks to make sure that built-in PowerShell Universal variables are not being used in the script.
.EXAMPLE
    Measure-PSUBuiltInVariables -ScriptBlockAst $ScriptBlockAst
.INPUTS
    [System.Management.Automation.Language.ScriptBlockAst]
.OUTPUTS
    [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]
.NOTES
    None
#>
function Measure-PSUBuiltInVariables {
    [CmdletBinding()]
    [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])]
    Param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.Language.ScriptBlockAst]
        $ScriptBlockAst
    )

Le corps de cette fonction fait le gros du travail d’analyse du bloc de script. Dans notre exemple, nous parcourons l’AST pour trouver les instructions d’affectation. Si nous en trouvons, nous vérifions ensuite si le côté gauche de l’affectation est une variable. Le cas échéant, nous utilisons ensuite une méthode statique pour vérifier s’il s’agit d’une des variables intégrées de PowerShell Universal.

Si les conditions sont remplies, nous créons de nouveaux objets DiagnosticRecord et y plaçons les informations nécessaires. $_.Extent est important, car il indique aux éditeurs, comme VS Code ou Monaco, quelle partie du script mettre en évidence avec l’avertissement ou l’erreur.

    Process {
        $results = @()

        try {
            #region Define predicates to find ASTs.
            [ScriptBlock]$predicate1 = {
                param ([System.Management.Automation.Language.Ast]$Ast)

                if ($Ast -is [System.Management.Automation.Language.AssignmentStatementAst]) {
                    if ($Ast.Left -is [System.Management.Automation.Language.VariableExpressionAst]) {
                        return [PowerShellUniversal.BuiltInVariables]::IsBuiltInVariable($Ast.Left.VariablePath.UserPath)
                    }
                }

                return $false
            }

            #endregion

            #region Finds ASTs that match the predicate.

            [System.Management.Automation.Language.Ast[]]$methodAst = $ScriptBlockAst.FindAll($predicate1, $true)

            $methodAst | ForEach-Object {
                $result = New-Object `
                    -Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
                    -ArgumentList "Overriding built in PowerShell Universal variables can cause undefined behavior.", $_.Extent, $PSCmdlet.MyInvocation.InvocationName, Warning, $null
                $results += $result
            }

            return $results

            #endregion
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($PSItem)
        }
    }

Enfin, nous n’oublions pas d’exporter la fonction depuis notre module.

Export-ModuleMember -Function Measure-PSUBuiltInVariables

Dans PowerShell Universal, nous utilisons Invoke-ScriptAnalyzer pour exécuter l’analyse de code. Pendant que vous tapez dans l’éditeur, cette fonction est appelée pour fournir une rétroaction en temps réel. Pour utiliser notre règle personnalisée, nous passons le paramètre -CustomRulePath à Invoke-ScriptAnalyzer.

Invoke-ScriptAnalyzer -ScriptDefinition $code -CustomRulePath $InstallDir\Modules\Universal.Rules\Universal.Rules.psm1 -IncludeDefaultRules

Nous traitons ensuite la sortie de PSScriptAnalyzer pour fournir une rétroaction à l’utilisateur.

PowerShell Universal editor warning that overriding built-in variables causes undefined behavior.
Règle PSScriptAnalyzer personnalisée dans l’éditeur PowerShell Universal

Vous avez des idées de règles personnalisées pour PowerShell Universal? Dites-le-nous.

Prêt à construire? Téléchargez PowerShell Universal.