Ensayo seguro (-WhatIf / -Confirm)
Abstract
Esta lección introduce el patrón de ensayo seguro en PowerShell: simular o confirmar acciones antes de aplicarlas. Verás cómo aprovechar el soporte integrado del lenguaje para ejecutar comandos con más seguridad y control.
Exploramos cómo estructurar operaciones para ensayar cambios sin riesgos y decidir cuándo pedir confirmación. Con ejemplos prácticos, aplicarás el patrón para prevenir errores y construir scripts más confiables sin alterar su funcionamiento.
Patrón de ensayo seguro (dry run)
Antes de crear carpetas o archivos, conviene ensayar el cambio: ver qué se haría sin ejecutar nada.
En PowerShell esto se habilita con el atributo , que agrega
los parámetros automáticos SupportsShouldProcess SupportsShouldProcess y -WhatIf -WhatIf . Con ellos, el script puede simular o solicitar confirmación de acciones potencialmente
destructivas, reduciendo riesgos y aumentando la confianza en su comportamiento.
-Confirm -Confirm
-WhatIf)
#Requires -Version 7.5
[CmdletBinding(SupportsShouldProcess,
ConfirmImpact = 'Medium')]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrWhiteSpace()]
[string] $Name,
[Parameter(Mandatory)]
[ValidateNotNullOrWhiteSpace()]
[ValidateScript({ Test-Path -Path $_ -PathType Container })]
[string] $Path
)
Set-StrictMode -Version 3.0
$base = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
$target = Join-Path $base $Name
$readmePath = Join-Path $target 'README.md'
$helperPath = Join-Path $PSScriptRoot 'New-Readme.ps1' -Resolve
$existsBefore = Test-Path -LiteralPath $readmePath -PathType Leaf
$created = $false
$skipped = $false
if (!(Test-Path -LiteralPath $target -PathType Container)) {
New-Item -Path $target -ItemType Directory -Force | Out-Null
}
if (!$existsBefore) {
if ($PSCmdlet.ShouldProcess($readmePath, 'Create README.md')) {
$content = & $helperPath -Name $Name -Verbose:$PSBoundParameters['Verbose']
Set-Content -Path $readmePath -Encoding UTF8 -Value $content
$created = $true
}
}
else {
$skipped = $true
}
[PSCustomObject]@{
BasePath = $base
TargetPath = $target
ReadmePath = $readmePath
HelperPath = $helperPath
ExistsBefore = $existsBefore
Created = $created
Skipped = $skipped
} ¿Qué acabamos de hacer?
-
indica que el comando admite los parámetros automáticosSupportsShouldProcessSupportsShouldProcessy-WhatIf-WhatIf. PowerShell los agrega y gestiona por ti:-Confirm-Confirmnunca ejecuta la acción, sólo la describe;-WhatIf-WhatIfsolicita confirmación antes de proceder (según reglas de impacto y preferencia del usuario). 1-Confirm-Confirm -
declara el impacto de las operaciones del comando (ConfirmImpactConfirmImpactLow,Medium,High). PowerShell lo compara condel usuario (por defecto,$ConfirmPreference$ConfirmPreferenceHigh). PowerShell pide confirmación cuandoConfirmImpact >= $ConfirmPreference.
Si no se supera el umbral, puedes forzar la confirmación con.-Confirm-Confirm
En este ejemploMediumdescribe creación/modificación de archivos (relevante pero no destructiva). -
es el punto de decisión: aquí se evalúa si una operación debe simularse o ejecutarse. Coloca cada operación con efectos dentro de este if. Con$PSCmdlet.ShouldProcess([string] target, [string] action)$PSCmdlet.ShouldProcess([string] target, [string] action)se simula y con-WhatIf-WhatIfse solicita permiso -como se explicó arriba-.-Confirm-Confirm -
pasa un switch sólo si la variable booleana es-Switch:$variable-Switch:$variable. Para propagar switches comunes a helpers, usa$true$true: por ejemplo$PSBoundParameters$PSBoundParameters. Si quieres propagar-Verbose:$PSBoundParameters['Verbose']-Verbose:$PSBoundParameters['Verbose']/-WhatIf-WhatIfel helper también debe declarar-Confirm-Confirmpara que PowerShell gestione su simulación/confirmación correctamente.SupportsShouldProcessSupportsShouldProcess
Precaución
SupportsShouldProcess SupportsShouldProcess ,
-WhatIf -WhatIf / -Confirm -Confirm no surten efecto. Compruébalo
con
$cmd = Get-Command Start-Job
[System.Management.Automation.CommandMetadata]::new($cmd).SupportsShouldProcess # (1)
$cmd = Get-Command Copy-ItemProperty
[System.Management.Automation.CommandMetadata]::new($cmd).SupportsShouldProcess # (2) $false $false ya que Start-Job Start-Job no acepta -WhatIf -WhatIf / -Confirm -Confirm .
(2) Retorna
$true $true
ya que Copy-ItemProperty Copy-ItemProperty acepta -WhatIf -WhatIf / -Confirm -Confirm .
Regla mental
- Declarar soporte →
SupportsShouldProcessSupportsShouldProcess - Asignar impacto →
ConfirmImpactConfirmImpact - Envolver efectos →
ShouldProcess(target, action)ShouldProcess(target, action)
Confirmación y simulación
habilita SupportsShouldProcess SupportsShouldProcess /-WhatIf -WhatIf ; el punto de decisión es -Confirm -Confirm . $PSCmdlet.ShouldProcess(target, action) $PSCmdlet.ShouldProcess(target, action) simula; -WhatIf -WhatIf pide permiso
previo (según -Confirm -Confirm y las preferencias del usuario).
ConfirmImpact ConfirmImpact
-
— operaciones triviales (por ejemplo, lectura o inspección de datos).LowLow -
— operaciones que crean o modifican recursos.MediumMedium -
— operaciones críticas o destructivas (por ejemplo, eliminación de archivos o estructuras completas).HighHigh
Ensayo seguro con -WhatIf y confirmación
Antes de crear carpetas o archivos, ejecuta el comando con para
comprobar qué haría el script sin realizar cambios reales. Si además deseas que el script pida
confirmación antes de ejecutar cada acción, cambia -WhatIf -WhatIf -WhatIf -WhatIf .
-Confirm -Confirm
dibs/scripts ./scaffolding/Initialize-Project.ps1 -Name "Test" -Path "." -Verbose -WhatIf -WhatIf -WhatIf What if: Performing the operation "Create Directory" on target "Destination: /path/to/dibs/scripts/Test".
What if: Performing the operation "Create README.md" on target "/path/to/dibs/scripts/Test/README.md".
BasePath : /path/to/dibs/scripts
TargetPath : /path/to/dibs/scripts/Test
ReadmePath : /path/to/dibs/scripts/Test/README.md
HelperPath : /path/to/dibs/scripts/scaffolding/New-Readme.ps1
ExistsBefore : False
Created : False
Skipped : False Piensa rápido
-Name -Name y
-Path -Path junto con
-WhatIf -WhatIf . ¿Qué ocurre si el proyecto ya existe? ¿Y si la ruta no es válida?
Piensa rápido
-Confirm -Confirm para observar cómo solicita autorización
antes de crear cada archivo.
Ejercicio:
Limpieza segura con ConfirmImpact = 'High'
Requisitos
Remove-WorkFolder.ps1 que elimine una carpeta de trabajo específica de
forma segura, aplicando el patrón de ensayo/confirmación:
- Declara
.[CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')][CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')] - Parámetro obligatorio
$Path, válido y de tipo carpeta existente. - Usa
para proteger la llamada a$PSCmdlet.ShouldProcess(target, action)$PSCmdlet.ShouldProcess(target, action).Remove-Item -Recurse -ForceRemove-Item -Recurse -Force - Propaga verbosidad y switches comunes con
y, opcionalmente,-Verbose:$PSBoundParameters['Verbose']-Verbose:$PSBoundParameters['Verbose']/-WhatIf:$PSBoundParameters['WhatIf']-WhatIf:$PSBoundParameters['WhatIf'].-Confirm:$PSBoundParameters['Confirm']-Confirm:$PSBoundParameters['Confirm'] - Devuelve un
con[PSCustomObject][PSCustomObject]TargetPath,ExistsBeforeyDeleted.
Uso esperado
./Remove-WorkFolder.ps1 -Path './Test' -Verbose -WhatIf
# Luego, con confirmación interactiva
./Remove-WorkFolder.ps1 -Path './Test' -Verbose -Confirm Hints
$PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path) $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
y usar -LiteralPath -LiteralPath en las operaciones de archivo para evitar problemas con
caracteres especiales.
- Con
no debe borrarse nada;-WhatIf-WhatIf'Deleted'debe serFalse. - Con
, respeta-Confirm-Confirm(aplica confirmación cuando corresponda).$ConfirmPreference$ConfirmPreference
Solución
#Requires -Version 7.5
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrWhiteSpace()]
[ValidateScript({ Test-Path -Path $_ -PathType Container })]
[string] $Path
)
Set-StrictMode -Version 3.0
$target = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
$existsBefore = Test-Path -LiteralPath $target -PathType Container
$deleted = $false
if ($existsBefore -and $PSCmdlet.ShouldProcess($target, 'Remove work folder recursively')) {
$removeParams = @{
LiteralPath = $target
Recurse = $true
Force = $true
Verbose = $PSBoundParameters['Verbose']
WhatIf = $PSBoundParameters['WhatIf']
Confirm = $PSBoundParameters['Confirm']
}
Remove-Item @removeParams
$deleted = $true
}
[PSCustomObject]@{
TargetPath = $target
ExistsBefore = $existsBefore
Deleted = $deleted
}Conclusiones
El patrón ShouldProcess ofrece una base sólida para escribir scripts más seguros, predecibles y
confiables. Gracias a los modificadores automáticos y
-WhatIf -WhatIf , podemos agregar ensayos y confirmaciones sin alterar la estructura
ni el comportamiento principal del script, integrando buenas prácticas de seguridad operacional
directamente en la herramienta.
-Confirm -Confirm
Incorporarlo no solo previene errores costosos, sino que además impulsa una forma de pensar centrada en
la
reversibilidad y en el control del efecto que cada comando tiene sobre el sistema. Con una
estructura mínima -declarar , asignar
SupportsShouldProcess SupportsShouldProcess y envolver la operación en
ConfirmImpact ConfirmImpact - obtenemos de forma automática simulación y confirmación
interactiva.
ShouldProcess() ShouldProcess()
Puntos clave
-
habilitaSupportsShouldProcessSupportsShouldProcessy-WhatIf-WhatIf, añadiendo seguridad sin modificar la estructura principal del script.-Confirm-Confirm -
define el nivel de riesgo (ConfirmImpactConfirmImpactLow,Medium,High) y cuándo PowerShell pedirá confirmación. -
es el punto donde se decide ejecutar o simular; todo efecto del script debería pasar por él.ShouldProcess()ShouldProcess() - Los parámetros
y-WhatIf-WhatIfse aplican automáticamente en cascada a funciones que también declaran soporte para este patrón.-Confirm-Confirm - Al combinarlo con validaciones (
,ValidateScriptValidateScript) yLiteralPathLiteralPath, se obtienen scripts robustos y reutilizables.$PSBoundParameters$PSBoundParameters
¿Qué nos llevamos?
El ensayo seguro en PowerShell no es solo una característica técnica: es una filosofía de diseño que fomenta escribir comandos conscientes del contexto y del impacto que generan.
Adoptar este patrón tempranamente te permite crear utilidades más confiables, colaborativas y mantenibles, facilitando pruebas, revisiones y automatización en entornos reales. A partir de aquí, cada comando que modifique el sistema debería preguntarse: «¿debo hacerlo, o solo mostrar lo que haría?»
¿Con ganas de más?
Referencias recomendadas
- “ Everything you wanted to know about ShouldProcess ” en Microsoft Learn por Kevin MarquetteGuía exhaustiva sobre la implementación de
en funciones de PowerShell. Explica en detalle cómo habilitar los parámetros automáticosSupportsShouldProcessSupportsShouldProcessy-WhatIf-WhatIf, el uso de las sobrecargas de-Confirm-Confirmpara personalizar mensajes de simulación y confirmación, la configuración de$PSCmdlet.ShouldProcess()$PSCmdlet.ShouldProcess()para controlar cuándo se solicita confirmación segúnConfirmImpactConfirmImpact, y patrones avanzados como$ConfirmPreference$ConfirmPreferencepara escenarios de alto riesgo. Incluye ejemplos prácticos de propagación de switches mediante splatting, localización automática de prompts, y mejores prácticas para integrar ensayos seguros en scripts de producción. Recurso esencial para escribir comandos robustos, confiables y conscientes del impacto.ShouldContinue()ShouldContinue()
Notas
-
Nota que
hace que el script solicite confirmación antes de ejecutar una acción; no significa que tú ya la estés confirmando. Volver-Confirm-Confirm