Como parte de la versión 5 de PowerShell Universal, estamos añadiendo algunas reglas PSScriptAnalyzer personalizadas a la plataforma para detectar más fácilmente problemas al crear scripts. En esta entrada veremos cómo crear reglas PSScriptAnalyzer personalizadas.
PSScriptAnalyzer
PSScriptAnalyzer es una herramienta de análisis estático de código para scripts de PowerShell. Comprueba la calidad del código según un conjunto de reglas. Las reglas integradas cubren todo tipo de aspectos, como el uso de alias, los espacios al final de las líneas, las comprobaciones $null y más. En editores como VS Code, PSScriptAnalyzer se ejecuta en segundo plano para ofrecer comentarios en tiempo real sobre sus scripts.
Internamente, la extensión de PowerShell ejecuta Invoke-ScriptAnalyzer para ofrecer esos comentarios. Puede ejecutar este comando usted mismo para ver los resultados del análisis. Ejecutarlo solo con la ruta aplica todas las reglas predeterminadas.
Invoke-ScriptAnalyzer -Path .\MyScript.ps1
También puede excluir reglas con parámetros del cmdlet.
Invoke-ScriptAnalyzer -Path .\MyScript.ps1 -ExcludeRule PSAvoidUsingCmdletAliases
Para personalizar el comportamiento de PSScriptAnalyzer sin modificar la línea de comandos, también puede usar un archivo de configuración. Este archivo cubre las mismas opciones que los distintos parámetros de Invoke-ScriptAnalyzer.
# PSScriptAnalyzerSettings.psd1
@{
Severity=@('Error','Warning')
ExcludeRules=@('PSAvoidUsingCmdletAliases', 'PSAvoidUsingWriteHost')
}
Puede usar archivos de configuración del analizador de scripts pasando el parámetro -Settings a Invoke-ScriptAnalyzer.
Invoke-ScriptAnalyzer -Path MyScript.ps1 -Settings PSScriptAnalyzerSettings.psd1
También puede usar este archivo de configuración con VS Code estableciendo PowerShell > Script Analysis: Settings Path en PSScriptAnalyzerSettings.psd1.
Reglas personalizadas
Las reglas personalizadas se definen con módulos de PowerShell. Pueden aceptar una lista de tokens o un árbol de sintaxis abstracta (AST) y devolver registros de diagnóstico. Por ejemplo, en PowerShell Universal, tenemos una regla que comprueba el uso de variables integradas como $PSUEnvironment o $UniversalClient. Asignar esas variables puede provocar un comportamiento indefinido, así que queremos que los usuarios lo sepan de inmediato cuando intenten sobrescribirlas.
El primer paso es crear un nuevo archivo de módulo .psm1 y definir una función que analice el script. En este ejemplo, hemos elegido Universal.Rules.psm1 como nombre de archivo y hemos definido la función Measure-PSUBuiltInVariables. La ayuda basada en comentarios es obligatoria, y hay algunos detalles específicos de esta función que conviene señalar.
Primero, debe incluir el atributo OutputType para indicar el tipo de objeto que devolverá la función. En este caso, devolverá una matriz de objetos Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord. También debe aceptar un objeto ScriptBlockAst como parámetro. Este objeto representa el bloque de script del script que se está analizando.
<#
.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
)
El cuerpo de esta función realiza el trabajo principal de analizar el bloque de script. En nuestro ejemplo, recorremos el AST para encontrar instrucciones de asignación. Si encontramos instrucciones de asignación, comprobamos a continuación si el lado izquierdo de la asignación es una variable. Si es así, usamos entonces un método estático para verificar si es una de las variables integradas de PowerShell Universal.
Si se cumplen las condiciones, creamos nuevos objetos DiagnosticRecord y rellenamos la información necesaria. $_.Extent es importante porque indica a los editores, como VS Code o Monaco, qué parte del script resaltar con la advertencia o el error.
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)
}
}
Por último, nos aseguramos de exportar la función desde nuestro módulo.
Export-ModuleMember -Function Measure-PSUBuiltInVariables
Dentro de PowerShell Universal, usamos Invoke-ScriptAnalyzer para ejecutar el análisis de código. Mientras escribe en el editor, se llama a esta función para ofrecer comentarios en tiempo real. Para usar nuestra regla personalizada, pasamos el parámetro -CustomRulePath a Invoke-ScriptAnalyzer.
Invoke-ScriptAnalyzer -ScriptDefinition $code -CustomRulePath $InstallDir\Modules\Universal.Rules\Universal.Rules.psm1 -IncludeDefaultRules
A continuación procesamos la salida de PSScriptAnalyzer para ofrecer comentarios al usuario.
¿Tiene ideas de reglas personalizadas para PowerShell Universal? Cuéntenoslo.
¿Listo para crear? Descargue PowerShell Universal.

Adam Driscoll