Cómo proteger la calidad de los scripts de PowerShell con PSScriptAnalyzer — Selección de reglas e integración en CI

· Actualizado el: · · PowerShell, Análisis estático, CI/CD, GitHub Actions, Gestión de calidad, Mantenibilidad, Mejora operativa, Scripts

Cuando los scripts de PowerShell internos empiezan a multiplicarse, siempre aparece el problema de la «disparidad de calidad»: scripts ilegibles llenos de alias, scripts con contraseñas escritas en texto plano, o scripts con nombres de variable con errores tipográficos que nadie ha detectado. El problema sale a la luz cuando la persona que los escribió ya no está, y quien tiene que leerlos después se encuentra con dificultades.

Una parte considerable de estos problemas se puede detectar de forma mecánica con una herramienta de análisis estático. PowerShell cuenta con el módulo oficial de análisis estático PSScriptAnalyzer, que permite analizar un conjunto de scripts con un solo comando. Su mayor ventaja es que da resultados desde el primer día sin escribir ni una sola línea de código de pruebas.

En este artículo se resume el procedimiento práctico para implementar PSScriptAnalyzer en los activos de scripts internos, siguiendo el orden «reglas que conviene habilitar primero», «adopción gradual en los activos existentes» y «comprobación automática en CI». Para la garantía de calidad mediante pruebas, consulte también «Integración de pruebas de PowerShell con Pester».

Público objetivo y entorno previsto

Elemento Contenido
Público objetivo Personal de sistemas o desarrollo que quiere empezar a gestionar de forma mecánica la calidad de los scripts de PowerShell que se han multiplicado dentro de la empresa
Entorno de ejecución PSScriptAnalyzer se puede ejecutar tanto en Windows PowerShell 5.1 como en PowerShell 7. Si el script analizado está destinado a 5.1 o a 7 se indica de forma independiente de la versión que realiza el análisis, mediante PSUseCompatibleSyntax (capítulo 4)
Prerrequisitos de CI El ejemplo de CI del capítulo 7 asume el ejecutor windows-latest de GitHub Actions. La parte que invoca Invoke-ScriptAnalyzer es igual en otros sistemas de CI, y el análisis en sí puede ejecutarse también en ejecutores que no sean Windows
Permisos necesarios Como la instalación se hace con Install-Module -Scope CurrentUser, no se necesitan permisos de administrador
Entorno de verificación de la muestra El código de ejemplo distribuido al final del artículo se ha verificado ejecutándolo en PowerShell 7.6

1. Conclusión principal

  • PSScriptAnalyzer es el módulo oficial de análisis estático de PowerShell. Invoke-ScriptAnalyzer analiza scripts y módulos, y notifica las infracciones de las reglas.1
  • Cada observación tiene un nivel de severidad (Severity). Se divide en tres niveles: Error, Warning e Information, y el punto de partida realista es reducir primero a cero solo los Error.1
  • La configuración se centraliza en PSScriptAnalyzerSettings.psd1. Coloque Severity, IncludeRules, ExcludeRules y Rules en el repositorio para que todos analicen con el mismo criterio.2
  • La supresión individual se hace con SuppressMessageAttribute más una justificación por escrito. Antes de excluir una regla por completo, considere si basta con una supresión de alcance acotado.3
  • Algunas observaciones se pueden corregir automáticamente con -Fix. El formateo corre a cargo de Invoke-Formatter.14
  • La extensión de PowerShell para VS Code incorpora PSScriptAnalyzer. Muestra las advertencias en el momento mientras se edita, por lo que actúa antes que el CI.5
  • Defina la condición de fallo del CI como «severidad Error + reglas críticas señaladas por nombre». La severidad está determinada por cada regla, y la detección de contraseñas en texto plano (PSAvoidUsingPlainTextForPassword) es Warning. Si la condición se limita solo a Error, esta pasa desapercibida.6
  • Adopción gradual en los activos existentes. El orden es: «reducir Error a cero» → «aplicar el criterio estricto solo a los archivos modificados» → «ampliar el alcance».

2. Instalación — primero, un solo comando

Install-Module -Name PSScriptAnalyzer -Scope CurrentUser

# Analizar todo el contenido de la carpeta de una vez
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
    Sort-Object Severity, RuleName |
    Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize

# Ver el número de elementos por severidad (primer paso del inventario)
Invoke-ScriptAnalyzer -Path 'D:\Scripts' -Recurse |
    Group-Object Severity | Select-Object Name, Count

Ejecute primero estos dos comandos para conocer con cifras en qué estado se encuentran los activos de su empresa. No hace falta sorprenderse si aparecen cientos de resultados: en la mayoría de los entornos es así al principio.

Qué devuelve. Invoke-ScriptAnalyzer devuelve cada observación como un objeto, con propiedades como Severity, RuleName, ScriptName, Line y Message. El primer comando las ordena por severidad y las presenta línea por línea, indicando «en qué archivo, en qué línea, con qué regla y por qué motivo» se produjo cada una. El segundo devuelve solo dos columnas, Name (Error / Warning / Information) y Count, así que anote primero estas pocas cifras. El avance de la adopción gradual (capítulo 6) se mide observando la evolución de estos números. Si no hay ninguna observación, ninguno de los dos comandos muestra nada (una salida vacía significa que se aprueba).

La lista de reglas disponibles y su descripción se pueden consultar con Get-ScriptAnalyzerRule.1

Get-ScriptAnalyzerRule | Select-Object Severity, RuleName, CommonName | Sort-Object Severity
Get-ScriptAnalyzerRule -Name PSAvoidUsingWriteHost | Format-List *   # Descripción de una regla concreta

3. Las observaciones más eficaces — prioridades en la práctica

Entre las decenas de reglas existentes, se enumeran a continuación, por orden de prioridad, las que inciden directamente en la calidad de los scripts internos.

Regla Severidad Qué detecta Por qué es importante
PSAvoidUsingPlainTextForPassword Warning Recibe una contraseña en texto plano como parámetro La conservación de credenciales en texto plano también se señala en auditorías6
PSAvoidUsingConvertToSecureStringWithPlainText Error Crea un SecureString a partir de texto plano Comparte la raíz del punto anterior: el cifrado pierde su sentido
PSUseDeclaredVarsMoreThanAssignments Warning Variables que se asignaron pero nunca se usaron Puede detectar errores tipográficos en nombres de variables. Una detección de bugs real
PSAvoidUsingInvokeExpression Warning Uso de Invoke-Expression Ejecuta cadenas de texto como código, lo que la convierte en un vector de inyección
PSUseShouldProcessForStateChangingFunctions Warning Funciones que cambian el estado sin -WhatIf Detecta un diseño que no permite confirmar operaciones peligrosas de antemano
PSAvoidUsingCmdletAliases Warning Alias como ls, %, ? Cómodos en modo interactivo, pero perjudican la legibilidad en scripts
PSUseApprovedVerbs Warning Nombres de funciones con verbos no aprobados Si no siguen convenciones como Get-/Set-, cuesta más encontrarlas
PSAvoidGlobalVars Warning Uso de variables globales Los efectos secundarios dejan de ser legibles, y tampoco se pueden escribir pruebas
PSUseSingularNouns Warning Sustantivos en plural (como Get-Users) Convención de nomenclatura de PowerShell; el nombre debe ser adivinable por terceros

Fíjese en la columna de severidad. De las 9 reglas, solo 1 es Error; el resto son todas Warning.7 Como incluso la detección de contraseñas en texto plano (PSAvoidUsingPlainTextForPassword) es Warning, si la condición de fallo del CI se limita a «solo severidad Error», la mayor parte de esta tabla pasa desapercibida. Puesto que la severidad está determinada por cada regla, las que se quieran hacer fallar sin importar su severidad deben especificarse por nombre (capítulos 6 y 7). Para comprobarlo localmente, use Get-ScriptAnalyzerRule | Select-Object Severity, RuleName.

En particular, PSUseDeclaredVarsMoreThanAssignments es una observación con una relación coste-beneficio muy alta. Detecta errores tipográficos como haber asignado a $fileName y, más adelante, referenciar $fileNmae por error, identificándolos como «variable asignada pero nunca usada»; de este modo captura bugs reales que solo el análisis estático puede encontrar (cabe señalar que, como los nombres de variable de PowerShell no distinguen mayúsculas de minúsculas, $fileName y $filename son la misma variable; lo que este tipo de detección puede atrapar son los casos en los que la ortografía en sí es diferente).

4. Fijar el estándar del equipo con un archivo de configuración

No tiene sentido que cada persona analice con un criterio distinto. Coloque PSScriptAnalyzerSettings.psd1 en el repositorio para que todos, y también el CI, utilicen la misma configuración.2

# PSScriptAnalyzerSettings.psd1
@{
    # Usar el conjunto de reglas predeterminado
    IncludeDefaultRules = $true

    # En la primera fase de la adopción gradual, limitar a Error y Warning
    Severity = @('Error', 'Warning')

    # Reglas que, como política interna, se posponen por ahora (deje el motivo en un comentario)
    ExcludeRules = @(
        'PSAvoidUsingWriteHost'          # Hay muchas herramientas interactivas; se tolera por ahora
        'PSUseSingularNouns'             # No se pueden cambiar de golpe los nombres de función existentes
    )

    # Configuración detallada por regla
    Rules = @{
        PSUseCompatibleSyntax = @{
            # Verifica los scripts que deben funcionar tanto en 5.1 como en 7.
            # En TargetVersions solo se pueden indicar las versiones para las que la regla
            # tiene definiciones de sintaxis (se puede comprobar con Get-ScriptAnalyzerRule).
            # Escribir un valor no soportado provoca un error al cargar la configuración
            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

Este archivo de configuración también lo lee la extensión de VS Code. Como el valor predeterminado de la configuración powershell.scriptAnalysis.settingsPath de la extensión de PowerShell es PSScriptAnalyzerSettings.psd1, si lo coloca con este nombre en la raíz del repositorio, las advertencias que aparecen mientras edita y el criterio de evaluación del CI quedan alineados automáticamente.8 Si usa otro nombre o lo coloca en una subcarpeta, indique la ruta explícitamente en esta configuración. Si esto se desalinea, ocurre que «localmente no aparece nada, pero el CI falla», y se pierde la confianza en esa valiosa retroalimentación inmediata.

PSUseCompatibleSyntax es especialmente útil en entornos donde coexisten 5.1 y 7. Permite detectar antes de la ejecución el error de escribir en un script destinado a 5.1 una sintaxis exclusiva de 7 (como el operador ternario o el operador de encadenamiento de canalización). Para la política de migración en sí, consulte «Diferencias entre Windows PowerShell 5.1 y PowerShell 7».

5. Deje las excepciones documentadas con su motivo

En los puntos donde de ninguna manera se puede seguir la observación, en lugar de desactivar la regla por completo, suprímala solo en ese lugar.3

function Show-KsBanner {
    # Se usa Write-Host de forma intencionada porque el objetivo es una presentación decorativa en una herramienta interactiva
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidUsingWriteHost', '',
        Justification = 'Función de presentación exclusiva para ejecución interactiva. Por diseño, no devuelve ningún valor')]
    [CmdletBinding()]
    param([string] $Title)

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

El punto clave es escribir siempre Justification. Una supresión sin motivo resulta indistinguible, para quien la lea después, de «simplemente se quitó la advertencia». Esto funciona como un ADR (registro de decisiones) dentro del propio código, una idea que enlaza con «Usar ADR (registro de decisiones de arquitectura) en equipos pequeños».

6. Adopción gradual en los activos existentes

Si ante cientos de advertencias se piensa en «corregirlo todo antes de implementarlo», el proyecto se estanca casi con seguridad. Divídalo en etapas.

Primera etapa: detener la hemorragia (1 día). Incorpore al CI solo Severity = 'Error' y redúzcalo a cero. Aquí hay que tener cuidado: la severidad está determinada por cada regla y no siempre coincide con la intuición. Por ejemplo, la severidad de PSAvoidUsingPlainTextForPassword es Warning, y si la condición de fallo se limita a Error, no se detecta.6 Para reglas como las relacionadas con credenciales, que se quieren hacer fallar sin importar su severidad, añádalas a la condición de fallo especificándolas explícitamente por nombre, como se muestra a continuación.

# Primero, obtener el resultado del análisis
$issues = Invoke-ScriptAnalyzer -Path . -Recurse -Settings .\PSScriptAnalyzerSettings.psd1

# Hacer que la severidad Error + las reglas críticas indicadas individualmente sean la condición de fallo del CI
$mustFix = @(
    'PSAvoidUsingPlainTextForPassword'
    'PSAvoidUsingConvertToSecureStringWithPlainText'
    'PSAvoidUsingUsernameAndPasswordParams'
)
$blocking = $issues | Where-Object { $_.Severity -eq 'Error' -or $_.RuleName -in $mustFix }

Segunda etapa: proteger lo nuevo y lo modificado (1 semana). Limite el análisis solo a los archivos modificados. Aunque la deuda existente se quede tal cual, se puede detener la aparición de nuevos problemas.

Al ejecutarlo en CI, el commit que sirve de referencia para la comparación debe estar ya descargado. Como actions/checkout descarga solo un commit de forma predeterminada, indique fetch-depth: 0 o haga un fetch explícito de la rama base (si no lo hace, fallará con unknown revision).

Otro punto: no fije el destino de la comparación en main. git diff A...HEAD significa «la diferencia desde el ancestro común entre A y HEAD», por lo que si usa origin/main...HEAD en un PR dirigido a develop o a una rama de lanzamiento, entrarán en el análisis incluso cambios que ese PR no ha tocado, y el CI fallará por observaciones ya existentes en archivos que no tienen relación. En GitHub Actions, la rama de destino del PR se encuentra en GITHUB_BASE_REF, así que utilícela.9

Además, detecte siempre los fallos de git. De forma predeterminada, PowerShell no convierte en error de terminación el hecho de que un comando externo devuelva un código de salida distinto de cero.10 Por eso, si la ref base no está descargada, git diff simplemente falla y la salida queda vacía; lo que viene después lo interpreta como «sin archivos modificados» y el CI se pone en verde sin haber analizado ni un solo archivo. Esta es la forma de rotura más peligrosa en una comprobación de diferencias. Verifique $LASTEXITCODE justo después de la ejecución y deténgase de forma explícita (a partir de PowerShell 7.3 también existe la opción de establecer $PSNativeCommandUseErrorActionPreference = $true).10

      - uses: actions/checkout@v4
        with:
          fetch-depth: 0        # Se necesita el historial para obtener las diferencias
# Analizar solo los ps1/psm1 modificados (comprobación de diferencias en CI)
# No fijar el destino de la comparación en main: en los PR dirigidos a develop
# o a una rama de lanzamiento arrastraría cambios ajenos y el CI fallaría por archivos no tocados
# La rama de destino del PR se obtiene de GITHUB_BASE_REF (vacía en un push)
$base = if ($env:GITHUB_BASE_REF) { "origin/$($env:GITHUB_BASE_REF)" } else { 'origin/main' }

# Sin -c core.quotePath=false, las rutas con japonés se devuelven entrecomilladas
# con escapes octales, como "scripts/\346...", y se escapan de la comprobación de extensión
$diff = git -c core.quotePath=false diff --name-only "$base...HEAD"

# De forma predeterminada, el fallo de un comando nativo no se convierte en error de terminación.
# Si la ref base no está descargada, git falla y la salida queda vacía, y esto pasa como
# "sin cambios = cero elementos que analizar = aprobado". Comprobar $LASTEXITCODE y detenerse
if ($LASTEXITCODE -ne 0) {
    throw "git diff ha fallado (exit $LASTEXITCODE). Es posible que la rama base $base no se haya descargado"
}

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

# -Path es un parámetro que recibe una única ruta; pasarle un array directamente
# falla el enlace de parámetros. Se analiza archivo por archivo y se agregan los resultados
$issues = foreach ($file in $changed) {
    Invoke-ScriptAnalyzer -Path $file -Settings .\PSScriptAnalyzerSettings.psd1
}

Tercera etapa: ampliar el alcance (continuo). Vaya quitando ExcludeRules una por una, endureciendo el criterio solo en la medida en que se va corrigiendo. Aproveche las oportunidades de refactorización para arreglar archivos existentes y reducir la deuda.

Las observaciones que admiten corrección automática se pueden procesar en bloque con -Fix (compruebe siempre el diff antes de aplicarlas).1 Si solo se necesita formatear, se puede usar Invoke-Formatter.4

Invoke-ScriptAnalyzer -Path .\Scripts -Recurse -Fix -Settings .\PSScriptAnalyzerSettings.psd1
git diff        # Revisar siempre visualmente qué ha cambiado

7. Automatizarlo en CI

Con GitHub Actions, en un ejecutor de Windows son solo unas pocas líneas. La clave es hacer que falle con Error y limitarse a mostrar los 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        # Necesario si se cambia al análisis de diferencias (capítulo 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

          # Volcar todos los resultados al log (para que también se vean los warnings)
          $issues | Sort-Object Severity, ScriptName, Line |
              Format-Table Severity, RuleName, ScriptName, Line, Message -AutoSize |
              Out-String -Width 200 | Write-Host

          # Condición de fallo = severidad Error + reglas que no se toleran sin importar la severidad
          $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)"

          # Cuando avance la adopción gradual, añadir también Warning a la condición
          if ($blocking.Count -gt 0) {
              throw "Hay $($blocking.Count) observaciones que requieren corrección"
          }

Si lo integra en el mismo flujo de trabajo que las pruebas de Pester, puede crear el flujo «pasa el lint → pasan las pruebas → se puede fusionar». Para el planteamiento general de CI/CD en aplicaciones de Windows, consulte «Práctica de CI/CD para aplicaciones WinForms / WPF».

Incluso en entornos sin CI, basta con ejecutar Invoke-ScriptAnalyzer mensualmente y guardar el resultado en CSV para visualizar suficientemente el estado de los activos.

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

8. Reglas prácticas habituales (tabla de decisión)

Cuestión Opciones Criterio de decisión
Orden de implementación Empezar por Pester / Empezar por PSScriptAnalyzer El análisis estático funciona desde el primer día sin escribir pruebas
Primer objetivo Todas las reglas / Severity=Error + reglas de credenciales nombradas explícitamente Si se exige todo, nadie lo pasa. Tenga en cuenta que la severidad está determinada por cada regla6
Gran volumen de advertencias existentes Corregirlo todo / Estricto solo en los archivos modificados Detener el aumento primero, y reducir aprovechando las oportunidades
Excepciones individuales ExcludeRules / SuppressMessageAttribute + Justification Minimizar el alcance del impacto. Dejar siempre el motivo por escrito3
Compartir la configuración Configuración individual / .psd1 en el repositorio Alinear el criterio entre el CI y los desarrolladores2
Coexistencia de 5.1 y 7 Ejecutar y comprobar / PSUseCompatibleSyntax Detecta antes de la ejecución las incompatibilidades a nivel de sintaxis
Corrección automática Manual / -Fix + revisión del diff Revisar siempre git diff después de aplicarla1
Retroalimentación al editar Solo CI / Extensión de VS Code Poder corregir en el momento es lo más económico5

9. Resumen

  • PSScriptAnalyzer es el módulo oficial de análisis estático; se puede implementar sin escribir pruebas y da resultados desde el primer día.
  • La adopción gradual realista consiste en reducir primero Error a cero y, después, comprobar de forma estricta solo los archivos modificados. Como la severidad está determinada por cada regla, las que se quieran hacer fallar —como la de contraseñas en texto plano (Warning)— deben añadirse a la condición de fallo por nombre.
  • Hay reglas, como PSUseDeclaredVarsMoreThanAssignments, que pueden detectar bugs reales derivados de errores tipográficos en nombres de variables.
  • Centralice la configuración en PSScriptAnalyzerSettings.psd1, colóquela en el repositorio y alinee el criterio entre los desarrolladores y el CI.
  • Deje las excepciones documentadas escribiendo el motivo en SuppressMessageAttribute. Excluir una regla por completo es el último recurso.
  • En el CI, falle con Error y visualice los Warning. Si lo integra en el mismo flujo de trabajo que Pester, el control de calidad queda concentrado en un solo lugar.

Descarga del código de ejemplo

El código tratado en este artículo se distribuye listo para ejecutarse tal cual. Incluye el archivo de configuración, el script de evaluación de aprobación para CI y un ejemplo de GitHub Actions.

Descargar el código de ejemplo (zip)

El ejemplo de este artículo se ha verificado ejecutándolo realmente en PowerShell 7.6 (14 pruebas de Pester). Si ejecuta Invoke-SampleTests.ps1, incluido en el zip, podrá reproducir la misma verificación en su propio entorno.

# Análisis sintáctico + análisis estático + pruebas de Pester
./Invoke-SampleTests.ps1

Los valores de configuración (rutas, nombres de servidor, ID de inquilino, etc.) son solo ejemplos. No los ejecute tal cual en un entorno de producción; adáptelos al entorno de su empresa.

Artículos relacionados

Áreas de consultoría relacionadas

KomuraSoft LLC se encarga del inventario de los activos de scripts internos y la definición de estándares de calidad, la implementación de análisis estático y pruebas en CI, y la mejora de la mantenibilidad de scripts operativos dependientes de una sola persona.

Referencias

  1. Microsoft Learn, Introducción al módulo PSScriptAnalyzer. Sobre PSScriptAnalyzer como herramienta de análisis estático para scripts y módulos de PowerShell, el análisis mediante Invoke-ScriptAnalyzer y parámetros como -Path / -Recurse / -Settings / -Fix / -ExcludeRule, la obtención de la lista de reglas con Get-ScriptAnalyzerRule, y el hecho de que los resultados del diagnóstico tienen severidad (Error / Warning / Information).  2 3 4 5 6

  2. Microsoft Learn, Archivo de configuración de PSScriptAnalyzer. Sobre la posibilidad de especificar Severity, IncludeRules, ExcludeRules, IncludeDefaultRules y Rules, entre otros, en el archivo de configuración (.psd1); sobre pasar el archivo de configuración mediante el parámetro -Settings; y sobre la configuración detallada por regla (como TargetVersions de PSUseCompatibleSyntax u opciones de las reglas de formateo).  2 3

  3. Microsoft Learn, Supresión de reglas de PSScriptAnalyzer. Sobre la posibilidad de suprimir diagnósticos por regla o por destino mediante System.Diagnostics.CodeAnalysis.SuppressMessageAttribute, y sobre los argumentos RuleName, Target y Justification 2 3

  4. Microsoft Learn, Invoke-Formatter. Sobre el formateo del texto del script según la configuración, y sobre la posibilidad de especificar en el archivo de configuración las reglas de formateo (indentación, posición de la llave de apertura, tratamiento de los espacios, etc.).  2

  5. Microsoft Learn, Uso de PowerShell en Visual Studio Code. Sobre el hecho de que la extensión de PowerShell utiliza PSScriptAnalyzer para mostrar advertencias durante la edición, y sobre que ofrece una función de formateo.  2

  6. Microsoft Learn, AvoidUsingPlainTextForPassword. Sobre el hecho de que las contraseñas o la información secreta no deberían recibirse mediante un parámetro de tipo cadena en texto plano, sino usando SecureString o PSCredential, y sobre que la severidad (Severity Level) de esta regla es Warning y está siempre activa. Consulte también, como regla relacionada, AvoidUsingConvertToSecureStringWithPlainText (generar un SecureString a partir de texto plano no protege el secreto).  2 3 4

  7. Microsoft Learn, Lista de reglas de PSScriptAnalyzer. Sobre la lista de reglas integradas y la tabla que resume, para cada una, su severidad (Severity), si está habilitada de forma predeterminada y si es configurable. La severidad de las reglas mencionadas en la tabla del cuerpo del artículo (AvoidUsingConvertToSecureStringWithPlainText es Error; AvoidUsingPlainTextForPassword, UseDeclaredVarsMoreThanAssignments, AvoidUsingInvokeExpression, UseShouldProcessForStateChangingFunctions, AvoidUsingCmdletAliases, UseApprovedVerbs, AvoidGlobalVars y UseSingularNouns son Warning), así como el hecho de que AvoidUsingUsernameAndPasswordParams, señalada por nombre en los capítulos 6 y 7, es Error, se basan en esta lista y en las páginas individuales de cada regla. 

  8. PowerShell/vscode-powershell, package.json (definición de la configuración de la extensión). Sobre powershell.scriptAnalysis.settingsPath como la configuración que indica la ruta al archivo de configuración de PSScriptAnalyzer, cuyo valor predeterminado es PSScriptAnalyzerSettings.psd1, y sobre powershell.scriptAnalysis.enable, que permite activar o desactivar el análisis en tiempo real durante la edición. 

  9. GitHub Docs, Referencia de variables ─ Variables de entorno predeterminadas. Sobre el hecho de que, en el evento pull_request, GITHUB_BASE_REF contiene el nombre de la rama de destino del PR (y queda vacía en los demás eventos). Para el significado de la notación de tres puntos (la diferencia desde la base de fusión de las dos refs indicadas), consulte git diff en la documentación oficial de Git. 

  10. Microsoft Learn, about_Preference_Variables ─ $PSNativeCommandUseErrorActionPreference. Sobre el hecho de que, de forma predeterminada, un código de salida distinto de cero de un comando nativo no se convierte en error de terminación; sobre que, al establecer en $true esta configuración introducida en PowerShell 7.3, el error de terminación pasa a seguir $ErrorActionPreference; y sobre que el código de salida del comando externo más reciente se obtiene con $LASTEXITCODE 2

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

Al ejecutar PSScriptAnalyzer sobre scripts existentes aparecieron cientos de advertencias. ¿Por dónde debería empezar?
No intente corregirlo todo de una vez. El enfoque práctico es centrarse primero únicamente en los elementos con severidad Error y reducirlos a cero. Tenga en cuenta que la severidad está determinada por cada regla; por ejemplo, PSAvoidUsingPlainTextForPassword, que detecta contraseñas en texto plano, tiene severidad Warning. Si existen reglas que quiere hacer fallar sin importar su severidad —como las relacionadas con credenciales—, añádalas explícitamente por nombre a las condiciones de fallo de CI. A continuación, incorpore en el CI una regla que analice únicamente los archivos que va a modificar a partir de ahora, para detener la aparición de nuevos problemas. Las advertencias existentes se pueden dejar como «tolerables por ahora» excluyéndolas en el archivo de configuración, e ir reduciéndolas una a una aprovechando las tareas de refactorización; este es el enfoque más realista.
Quiero suprimir la advertencia solo en un punto concreto. ¿Cómo lo hago?
Añada SuppressMessageAttribute a esa función o script. Especifique el nombre de la regla en System.Diagnostics.CodeAnalysis.SuppressMessageAttribute y escriba el motivo en Justification. Es importante escribir el motivo, porque permite que quien lo lea después entienda por qué se trata de una excepción. Si quiere desactivar la regla por completo, escríbala en ExcludeRules del archivo de configuración, pero como esto tiene un alcance mucho más amplio, primero considere si la supresión individual no es suficiente.
Al usar Write-Host aparece una advertencia. ¿No debería usarlo?
PSAvoidUsingWriteHost es una observación de diseño: si usa Write-Host en un lugar donde debería devolver un valor, ese valor deja de poder recuperarse. Si el objetivo es una presentación decorativa en una herramienta interactiva, es razonable suprimirla escribiendo el motivo en SuppressMessageAttribute. En cambio, si un script de ejecución desatendida solo usa Write-Host, vale la pena revisarlo tal como indica la advertencia. No siga la regla de forma mecánica: entienda la intención de la observación y decida en consecuencia.
¿Cuál conviene implementar primero, Pester o PSScriptAnalyzer?
Conviene implementar primero PSScriptAnalyzer, porque el efecto es mayor en relación con el coste de adopción. Sin escribir ni una línea de código de pruebas, puede analizar todos los scripts con un solo comando y obtener resultados desde el primer día. Pester requiere tiempo para arrancar porque implica escribir pruebas, pero solo las pruebas pueden garantizar la corrección de la lógica. Como orden recomendado: primero incorpore el análisis estático al CI para detener los problemas evidentes, y después vaya añadiendo pruebas de Pester empezando por los procesos cuya rotura sería más problemática.
¿Tiene sentido implementarlo incluso en un equipo pequeño sin servidor de CI?
Sí. Aunque no haya Git ni CI, basta con ejecutar Invoke-ScriptAnalyzer -Path . -Recurse sobre el conjunto de scripts de una carpeta compartida para hacer un inventario. Con solo exportar el resultado a CSV y revisar mensualmente cuántos elementos de severidad Error hay, ya se visualiza el estado de los activos. Además, la extensión de PowerShell para VS Code incorpora PSScriptAnalyzer, por lo que muestra advertencias en el momento mientras se edita. Solo con esto ya mejoran de forma constante los hábitos de escritura.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog