Dominar el parámetro WhatIf de PowerShell

“No siempre pruebo mis scripts, pero cuando lo hago, lo hago en producción”

No mientas. Todos lo hemos hecho en algún momento. No tiene por qué ser arriesgado ejecutar scripts de PowerShell en producción. ¡Solo recuerda usar el parámetro incorporado de PowerShell, WhatIf!

¿No sería genial saber qué haría ese comando de PowerShell antes de realizar cambios en tu entorno? Imagina poder preguntarle a tu comando: “¿Qué harías si te ejecutara?” Puedes usar el parámetro WhatIf.

Todos los cmdlets compilados de PowerShell incluyen un parámetro llamado WhatIf. Este parámetro te ayuda a evaluar si un comando funcionará como esperas o si iniciará una fusión nuclear.

El útil parámetro WhatIf no solo está disponible con todos los cmdlets integrados, sino también para tus scripts y funciones avanzadas. Si tu código cambiará cosas en tu entorno, tienes el beneficio de un mecanismo de seguridad en caso de que decidas implementarlo.

Prerrequisitos

Este artículo será una guía paso a paso. Si deseas seguir durante este viaje, deberías tener algunas cosas en su lugar.

Ten en cuenta que en este artículo estaré utilizando Visual Studio Code 1.38 (agosto de 2019) en una máquina con Windows 10.

El Parámetro WhatIf de PowerShell: Definido

En pocas palabras, el parámetro WhatIf es un parámetro de conmutación integrado disponible con todas las funciones avanzadas y cmdlets (al agregar la palabra clave CmdletBinding de PowerShell a scripts y funciones). Cuando se utiliza, el comando informa el efecto esperado del comando en la consola. Pero no ejecuta realmente el comando.

Todos los cmdlets y funciones avanzadas tienen disponible el parámetro WhatIf. En este artículo, demostraremos este parámetro usando los cmdlets Get-Service, Stop-Service y New-Item.

Comprobación del Soporte de WhatIf en PowerShell

Si no estás seguro de si un comando en particular admite WhatIf, hay dos formas rápidas de verificarlo.

Usando Get-Command

Puedes usar el comando Get-Command para ver metadatos del comando usando el parámetro Syntax como se muestra a continuación. Si ves una referencia a -WhatIf, se admite WhatIf.

Get-Command <CommandName> -Syntax

Usando la Completación de Tabulación

También puedes verificar el soporte del parámetro WhatIf usando la completación de tabulación. Simplemente escribe el comando que deseas verificar en una consola de PowerShell seguido de un espacio, un guion, ‘Wh’ y la tecla de tabulación.

Si aparece WhatIf, sabes que ese comando tiene el parámetro WhatIf.

PS> <CommandName> -Wh[tab]

Uso del Parámetro WhatIf de PowerShell con Cmdlets

Hay muchas formas diferentes en las que puedes aprovechar el parámetro WhatIf. En esta sección, aprenderás cómo puedes empezar a usar el parámetro WhatIf de inmediato utilizando cmdlets integrados.

Creando un archivo

Como todos los cmdlets, el cmdlet New-Item tiene un parámetro WhatIf. En este ejemplo, usarás el cmdlet New-Item para crear un archivo llamado newfile1.txt en el mismo directorio de trabajo.

Si ejecutaras el siguiente comando, crearía el archivo llamado newfile.txt.

PS51> New-Item -ItemType File -Path .\newfile1.txt

Pero ¿qué pasa si este comando crea un archivo que podría causar un problema si se crea incorrectamente? No hay problema. Puedes añadir el parámetro WhatIf al final.

New-Item -ItemType File -Path .\newfile1.txt -WhatIf

Deteniendo un servicio

También puedes usar el parámetro WhatIf con el cmdlet Stop-Service. En este ejemplo, obtienes una lista de los cinco primeros servicios y los detienes con el comando Stop-Service. Pero no los estás deteniendo.

En cambio, solo estás viendo un mensaje de salida en la consola de PowerShell que te indica qué servicios habría detenido el cmdlet Stop-Service haber detenido.

PS51> (Get-Service)[0..4] | Stop-Service -WhatIf

Cambiando el comportamiento de WhatIf de Powershell globalmente

En este punto, deberías saber que usar el parámetro WhatIf con un cmdlet o una función avanzada solo simula la operación. Estabas afectando el comportamiento de WhatIf a nivel de comando.

El comportamiento de WhatIf también se puede configurar a un nivel superior que afecta a todos los comandos manipulando la variable automática $WhatIfPreference.

La variable $WhatIfPreference es booleana. Solo puede ser True o False. De forma predeterminada, está establecida en False, lo que significa que el soporte de WhatIf está deshabilitado en todos los comandos a menos que se anule a nivel de comando. Si se establece en True, todos los comandos que lo admitan, ya sea utilizando explícitamente el parámetro WhatIf o no, estarán en modo “WhatIf“.

Puedes probar esto cambiando el valor de $WhatIfPreference a True mediante $WhatIfPreference = $true. Puedes ver a continuación que usando New-Item sin el parámetro WhatIf ahora actúa como si el parámetro se hubiera pasado.

PS51> $WhatIfPreference = $true

Si has cambiado $WhatIfPreference a True, no olvides cambiarlo de nuevo a False mediante $WhatIfPreference = $false.

En esta sección, has aprendido cómo usar el soporte de WhatIf con los cmdlets existentes. En la próxima acción, aprenderás cómo construir el soporte de WhatIf en tus scripts y funciones personalizadas.

Implementación del Soporte de WhatIf en Powershell para Funciones

Antes de intentar implementar el soporte de WhatIf en tus scripts, es esencial saber que hay una forma incorrecta y una forma correcta de hacerlo. Verás en las siguientes secciones cuáles son esas formas.

Haciéndolo mal: No reinventes la rueda

No es raro que los desarrolladores de scripts reinventen la rueda e implementen su propio soporte de WhatIf con alguna lógica de if/then. A continuación puedes ver un ejemplo.

Observa que el desarrollador ha definido su propio parámetro de interruptor WhatIf. Luego, utilizando el valor de ese parámetro, han utilizado una estructura de if/then para manejar la lógica cuando el parámetro WhatIf se utiliza o no.

Function Remove-LogFile {
    param (
        [Parameter(Mandatory)]
        [string]$name,

        [switch]$WhatIf
    )
    
    if ($WhatIf.IsPresent) { # El interruptor WhatIf está activado.
        Write-Host "WhatIf: I will remove the log file ""$name""" -ForegroundColor Yellow
    } else {
        # El interruptor WhatIf está desactivado.
        Write-Host "Delete log file ""$name""" -ForegroundColor Green
    }
}

Cuando se ejecuta la función anterior utilizando el parámetro WhatIf como se muestra a continuación, parece que hizo su trabajo. La función en realidad no eliminó nada y devolvió un mensaje a la consola.

PS51> Remove-LogFile -name log.txt -WhatIf

Si funciona, ¿qué hay de malo en este método? Porque estás descuidando las capacidades incorporadas de una función avanzada. Necesitarás construir esta funcionalidad en cada función que crees en lugar de enfocarte solo en lo que hará el comando cuando esté desactivado.

En lugar de reinventar la rueda, utiliza la palabra clave SupportsShouldProcess combinada con el $PSCmdlet.ShouldProcess(). Eso está por venir.

Haciéndolo bien: Usando SupportsShouldProcess

Todas las funciones que utilizan la palabra clave [CmdletBinding()] se consideran “avanzadas”. Esta palabra clave agrega varias capacidades a la función, incluido el soporte de WhatIf.

Todas las funciones avanzadas admiten la funcionalidad de WhatIf, pero depende de ti aprovecharla. Para hacerlo, debes usar la palabra clave SupportsShouldProcess entre los paréntesis de [CmdletBinding()] primero, como se muestra a continuación.

[CmdletBinding(SupportsShouldProcess)]

Ahora la función te permite llamar al método ShouldProcess() en la variable de función $PSCmdlet para determinar si se pasó el parámetro WhatIf a la función o no. Cuando se usa el parámetro WhatIf, ShouldProcess() devuelve False. De lo contrario, siempre devolverá True.

Function Remove-LogFile {
     [cmdletbinding(SupportsShouldProcess)]
     param (
         [Parameter(Mandatory)]
         [string]$name
     )
     if ($PSCmdlet.ShouldProcess($name)) {
         ## El parámetro -WhatIf NO se usó. La función debería procesarse normalmente.
         Write-Host ("Delete log file " + $name) -ForegroundColor Green
     } else {
         ## El parámetro -WhatIf se usó. La función NO debería procesarse.
     }
 }
WHATIF PARAMETER USED SHOULDPROCESS() RESULT
True False
False True

Ahora puedes ver a continuación cuando se ejecuta Remove-LogFile con el parámetro WhatIf; muestra el mismo comportamiento que los cmdlets integrados.

PS51> Remove-LogFile -name log.txt -WhatIf

Resumen

En este artículo, aprendiste sobre el parámetro WhatIf de PowerShell. Ahora deberías entender cómo funciona y qué beneficios puede aportar.

También deberías saber que no es necesario reinventar la rueda la próxima vez que necesites un mecanismo de seguridad para tus funciones de PowerShell. ¡En su lugar, aprovecha el soporte existente de PowerShell WhatIf!

Lectura adicional

Source:
https://adamtheautomator.com/powershell-whatif/