Tudo o que você queria saber sobre ShouldProcess
As funções do PowerShell têm vários recursos que melhoram muito a maneira como os usuários interagem com elas.
Uma característica importante que muitas vezes é negligenciada é -WhatIf
-Confirm
o suporte e é fácil de adicionar às suas funções. Neste artigo, nos aprofundamos em como implementar esse recurso.
Nota
A versão original deste artigo apareceu no blog escrito por @KevinMarquette. A equipe do PowerShell agradece Kevin por compartilhar esse conteúdo conosco. Por favor, confira seu blog em PowerShellExplained.com.
Este é um recurso simples que você pode habilitar em suas funções para fornecer uma rede de segurança para os usuários que precisam dela. Não há nada mais assustador do que executar um comando que você sabe que pode ser perigoso pela primeira vez. A opção de executá-lo pode -WhatIf
fazer uma grande diferença.
CommonParameters
Antes de analisarmos a implementação desses parâmetros comuns, quero dar uma olhada rápida em como eles são usados.
Usando -WhatIf
Quando um comando suporta o -WhatIf
parâmetro, ele permite que você veja o que o comando teria feito em vez de fazer alterações. É uma boa maneira de testar o impacto de um comando, especialmente antes de fazer algo destrutivo.
PS C:\temp> Get-ChildItem
Directory: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/19/2021 8:59 AM 0 importantfile.txt
-a---- 4/19/2021 8:58 AM 0 myfile1.txt
-a---- 4/19/2021 8:59 AM 0 myfile2.txt
PS C:\temp> Remove-Item -Path .\myfile1.txt -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
Se o comando for implementado ShouldProcess
corretamente, ele deverá mostrar todas as alterações que ele teria feito. Aqui está um exemplo usando um curinga para excluir vários arquivos.
PS C:\temp> Remove-Item -Path * -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
What if: Performing the operation "Remove File" on target "C:\Temp\myfile2.txt".
What if: Performing the operation "Remove File" on target "C:\Temp\importantfile.txt".
Usando -Confirm
Os comandos que suportam -WhatIf
também o .-Confirm
Isso lhe dá a chance de confirmar uma ação antes de executá-la.
PS C:\temp> Remove-Item .\myfile1.txt -Confirm
Confirm
Are you sure you want to perform this action?
Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
Nesse caso, você tem várias opções que permitem continuar, ignorar uma alteração ou parar o script. O prompt de ajuda descreve cada uma dessas opções como esta.
Y - Continue with only the next step of the operation.
A - Continue with all the steps of the operation.
N - Skip this operation and proceed with the next operation.
L - Skip this operation and all subsequent operations.
S - Pause the current pipeline and return to the command prompt. Type "exit" to resume the pipeline.
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
Localização
Esse prompt é localizado no PowerShell para que o idioma seja alterado com base no idioma do seu sistema operacional. Essa é mais uma coisa que o PowerShell gerencia para você.
Parâmetros do interruptor
Vamos dar uma olhada rápida em maneiras de passar um valor para um parâmetro switch. A principal razão pela qual eu chamo isso é que muitas vezes você quer passar valores de parâmetro para funções que você chama.
A primeira abordagem é uma sintaxe de parâmetro específica que pode ser usada para todos os parâmetros, mas você a vê principalmente usada para parâmetros de switch. Você especifica dois pontos para anexar um valor ao parâmetro.
Remove-Item -Path:* -WhatIf:$true
Você pode fazer o mesmo com uma variável.
$DoWhatIf = $true
Remove-Item -Path * -WhatIf:$DoWhatIf
A segunda abordagem é usar uma hashtable para splat o valor.
$RemoveSplat = @{
Path = '*'
WhatIf = $true
}
Remove-Item @RemoveSplat
Se você é novo em hashtables ou splatting, tenho outro artigo sobre isso que cobre tudo o que você queria saber sobre hashtables.
ApoiosDevemProcesso
O primeiro passo para habilitar -WhatIf
e -Confirm
dar suporte é especificar SupportsShouldProcess
na CmdletBinding
sua função.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
Remove-Item .\myfile1.txt
}
Especificando SupportsShouldProcess
desta forma, podemos agora chamar a nossa função com -WhatIf
(ou -Confirm
).
PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
Observe que eu não criei um parâmetro chamado -WhatIf
. Especificar SupportsShouldProcess
automaticamente cria-o para nós. Quando especificamos o -WhatIf
parâmetro em Test-ShouldProcess
, algumas coisas que chamamos também executam -WhatIf
processamento.
Confiar, mas verificar
Há algum perigo aqui confiando que tudo o que você chama herda -WhatIf
valores. Para o resto dos exemplos, vou assumir que não funciona e ser muito explícito ao fazer chamadas para outros comandos. Eu recomendo que você faça o mesmo.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
Remove-Item .\myfile1.txt -WhatIf:$WhatIfPreference
}
Vou revisitar as nuances muito mais tarde, uma vez que você tenha uma melhor compreensão de todas as peças em jogo.
$PSCmdlet.DeveProcessar
O método que permite implementar SupportsShouldProcess
é $PSCmdlet.ShouldProcess
. Você liga $PSCmdlet.ShouldProcess(...)
para ver se deve processar alguma lógica e o PowerShell cuida do resto. Vamos começar com um exemplo:
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
$file = Get-ChildItem './myfile1.txt'
if($PSCmdlet.ShouldProcess($file.Name)){
$file.Delete()
}
}
A chamada para $PSCmdlet.ShouldProcess($file.name)
verificar o -WhatIf
(e parâmetro) então o manipula -Confirm
de acordo. As -WhatIf
causas ShouldProcess
para produzir uma descrição da alteração e retorno $false
:
PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
Uma chamada usando -Confirm
pausa o script e solicita ao usuário a opção de continuar. Ele retorna $true
se o usuário selecionou Y
.
PS> Test-ShouldProcess -Confirm
Confirm
Are you sure you want to perform this action?
Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
Uma característica impressionante é $PSCmdlet.ShouldProcess
que ele funciona como saída detalhada. Eu dependo disso muitas vezes ao implementar ShouldProcess
.
PS> Test-ShouldProcess -Verbose
VERBOSE: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
Sobrecargas
Existem algumas sobrecargas diferentes para $PSCmdlet.ShouldProcess
com diferentes parâmetros para personalizar as mensagens. Já vimos o primeiro no exemplo acima. Vamos dar uma olhada mais de perto.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
if($PSCmdlet.ShouldProcess('TARGET')){
# ...
}
}
Isso produz uma saída que inclui o nome da função e o destino (valor do parâmetro).
What if: Performing the operation "Test-ShouldProcess" on target "TARGET".
Especificando um segundo parâmetro como a operação usa o valor da operação em vez do nome da função na mensagem.
## $PSCmdlet.ShouldProcess('TARGET','OPERATION')
What if: Performing the operation "OPERATION" on target "TARGET".
A próxima opção é especificar três parâmetros para personalizar totalmente a mensagem. Quando três parâmetros são usados, o primeiro é a mensagem inteira. Os dois segundos parâmetros ainda são usados na saída da -Confirm
mensagem.
## $PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION')
What if: MESSAGE
Referência rápida de parâmetros
Apenas no caso de você vir aqui apenas para descobrir quais parâmetros você deve usar, aqui está uma referência rápida mostrando como os parâmetros mudam a mensagem nos diferentes -WhatIf
cenários.
## $PSCmdlet.ShouldProcess('TARGET')
What if: Performing the operation "FUNCTION_NAME" on target "TARGET".
## $PSCmdlet.ShouldProcess('TARGET','OPERATION')
What if: Performing the operation "OPERATION" on target "TARGET".
## $PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION')
What if: MESSAGE
Eu tendo a usar aquele com dois parâmetros.
ShouldProcessReason
Temos uma quarta sobrecarga que é mais avançada do que as outras. Ele permite que você obtenha o motivo pelo qual ShouldProcess
foi executado. Eu só estou adicionando isso aqui para completar porque podemos apenas verificar se $WhatIfPreference
é $true
em vez disso.
$reason = ''
if($PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION',[ref]$reason)){
Write-Output "Some Action"
}
$reason
Temos que passar a $reason
variável para o quarto parâmetro como uma variável de referência com [ref]
. ShouldProcess
$reason
preenche com o valor None
ou WhatIf
. Eu não disse que isso era útil e eu não tive nenhuma razão para usá-lo.
Onde colocá-lo
Você usa ShouldProcess
para tornar seus scripts mais seguros. Assim, você o usa quando seus scripts estão fazendo alterações. Gosto de colocar a $PSCmdlet.ShouldProcess
chamada o mais próximo possível da mudança.
## general logic and variable work
if ($PSCmdlet.ShouldProcess('TARGET','OPERATION')){
# Change goes here
}
Se eu estiver processando uma coleção de itens, eu a chamo para cada item. Assim, a chamada é colocada dentro do loop foreach.
foreach ($node in $collection){
# general logic and variable work
if ($PSCmdlet.ShouldProcess($node,'OPERATION')){
# Change goes here
}
}
A razão pela qual eu coloco ShouldProcess
firmemente em torno da mudança, é que eu quero o máximo de código para executar quanto possível quando -WhatIf
é especificado. Quero que a configuração e a validação sejam executadas, se possível, para que o usuário veja esses erros.
Eu também gosto de usar isso em meus testes Pester que validam meus projetos. Se eu tenho um pedaço de lógica que é difícil de zombar em pester, muitas vezes posso embrulhá-lo ShouldProcess
e chamá-lo com -WhatIf
nos meus testes. É melhor testar parte do seu código do que nenhum.
$WhatIfPreference
A primeira variável de preferência que temos é $WhatIfPreference
. Isso ocorre $false
por padrão. Se você defini-lo para $true
então sua função é executada como se você especificou -WhatIf
. Se você definir isso em sua sessão, todos os comandos executarão -WhatIf
a execução.
Quando você chama uma função com -WhatIf
, o valor de $WhatIfPreference
fica definido como $true
dentro do escopo da sua função.
ConfirmarImpacto
A maioria dos meus exemplos são para -WhatIf
, mas tudo até agora também funciona com -Confirm
para avisar o usuário. Você pode definir o ConfirmImpact
da função como alto e ele solicita ao usuário como se fosse chamado com -Confirm
.
function Test-ShouldProcess {
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param()
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
}
Esta chamada para Test-ShouldProcess
está executando a -Confirm
ação devido ao High
impacto.
PS> Test-ShouldProcess
Confirm
Are you sure you want to perform this action?
Performing the operation "Test-ShouldProcess" on target "TARGET".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y
Some Action
A questão óbvia é que agora é mais difícil usar em outros scripts sem avisar o usuário. Neste caso, podemos passar um $false
para -Confirm
para suprimir o prompt.
PS> Test-ShouldProcess -Confirm:$false
Some Action
Abordarei como adicionar -Force
suporte em uma seção posterior.
$ConfirmPreference
$ConfirmPreference
é uma variável automática que controla quando ConfirmImpact
lhe pede para confirmar a execução. Aqui estão os valores possíveis para ambos $ConfirmPreference
e ConfirmImpact
.
High
Medium
Low
None
Com esses valores, você pode especificar diferentes níveis de impacto para cada função. Se você tiver $ConfirmPreference
definido para um valor maior que ConfirmImpact
, então você não será solicitado a confirmar a execução.
Por padrão, $ConfirmPreference
está definido como High
e ConfirmImpact
é Medium
. Se você quiser que sua função avise automaticamente o usuário, defina como ConfirmImpact
High
. Caso contrário, defina-o para Medium
se for destrutivo e use Low
se o comando for sempre seguro executado em produção. Se você defini-lo como none
, ele não solicitará mesmo se -Confirm
foi especificado (mas ainda lhe dará -WhatIf
suporte).
Ao chamar uma função com -Confirm
, o valor de $ConfirmPreference
fica definido como Low
dentro do escopo da sua função.
Supressão de prompts de confirmação aninhados
O $ConfirmPreference
pode ser captado por funções que você chama. Isso pode criar cenários em que você adiciona um prompt de confirmação e a função chamada também solicita ao usuário.
O que eu tendo a fazer é especificar -Confirm:$false
os comandos que eu chamo quando eu já tenho manipulado o prompting.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
$file = Get-ChildItem './myfile1.txt'
if($PSCmdlet.ShouldProcess($file.Name)){
Remove-Item -Path $file.FullName -Confirm:$false
}
}
Isso nos leva de volta a um aviso anterior: há nuances sobre quando -WhatIf
não é passado para uma função e quando -Confirm
passa para uma função. Prometo que voltarei a isso mais tarde.
$PSCmdlet.DeveContinuar
Se você precisar de mais controle do que ShouldProcess
o fornecido, você pode acionar o prompt diretamente com ShouldContinue
. ShouldContinue
$ConfirmPreference
ignora , ConfirmImpact
, -Confirm
, $WhatIfPreference
, e -WhatIf
porque ele solicita toda vez que é executado.
Em um rápido relance, é fácil confundir ShouldProcess
e ShouldContinue
. Eu tendo a lembrar de usar ShouldProcess
porque o parâmetro é chamado SupportsShouldProcess
no CmdletBinding
.
Você deve usar ShouldProcess
em quase todos os cenários. Foi por isso que abordei primeiro esse método.
Vamos dar uma olhada ShouldContinue
em ação.
function Test-ShouldContinue {
[CmdletBinding()]
param()
if($PSCmdlet.ShouldContinue('TARGET','OPERATION')){
Write-Output "Some Action"
}
}
Isso nos fornece um prompt mais simples com menos opções.
Test-ShouldContinue
Second
TARGET
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
O maior problema é ShouldContinue
que ele exige que o usuário o execute interativamente, porque ele sempre solicita ao usuário. Você deve sempre criar ferramentas que podem ser usadas por outros scripts. A maneira de fazer isso é implementando -Force
o . Voltarei a esta ideia mais tarde.
Yes to all
Isso é tratado automaticamente, ShouldProcess
mas temos que fazer um pouco mais de trabalho para ShouldContinue
. Há uma segunda sobrecarga de método onde temos que passar alguns valores por referência para controlar a lógica.
function Test-ShouldContinue {
[CmdletBinding()]
param()
$collection = 1..5
$yesToAll = $false
$noToAll = $false
foreach($target in $collection) {
$continue = $PSCmdlet.ShouldContinue(
"TARGET_$target",
'OPERATION',
[ref]$yesToAll,
[ref]$noToAll
)
if ($continue){
Write-Output "Some Action [$target]"
}
}
}
Eu adicionei um foreach
loop e uma coleção para mostrá-lo em ação. Puxei a ShouldContinue
chamada da if
declaração para facilitar a leitura. Chamar um método com quatro parâmetros começa a ficar um pouco feio, mas eu tentei fazê-lo parecer o mais limpo possível.
Implementação -Force
ShouldProcess
e ShouldContinue
precisam ser implementadas -Force
de diferentes maneiras. O truque para essas implementações é que ShouldProcess
sempre deve ser executado, mas ShouldContinue
não deve ser executado se -Force
for especificado.
DeveProcessar -Força
Se você definir o seu ConfirmImpact
como high
, a primeira coisa que seus usuários vão tentar é suprimi-lo com -Force
. Essa é a primeira coisa que faço de qualquer maneira.
Test-ShouldProcess -Force
Error: Test-ShouldProcess: A parameter cannot be found that matches parameter name 'force'.
Se você se lembrar da ConfirmImpact
seção, eles realmente precisam chamá-lo assim:
Test-ShouldProcess -Confirm:$false
Nem todo mundo percebe que precisa fazer isso e -Force
não reprime ShouldContinue
.
Portanto, devemos implementar -Force
para a sanidade de nossos usuários. Dê uma olhada neste exemplo completo aqui:
function Test-ShouldProcess {
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param(
[Switch]$Force
)
if ($Force -and -not $Confirm){
$ConfirmPreference = 'None'
}
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
}
Adicionamos o nosso próprio -Force
interruptor como parâmetro. O -Confirm
parâmetro é adicionado automaticamente ao usar SupportsShouldProcess
no CmdletBinding
.
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param(
[Switch]$Force
)
Focando na -Force
lógica aqui:
if ($Force -and -not $Confirm){
$ConfirmPreference = 'None'
}
Se o usuário especificar -Force
, queremos suprimir o prompt de confirmação, a menos que eles também especifiquem -Confirm
. Isso permite que um usuário force uma alteração, mas ainda confirme a alteração. Em seguida, definimos $ConfirmPreference
o âmbito local. Agora, usar o -Force
parâmetro define temporariamente o como nenhum, desativando o $ConfirmPreference
prompt de confirmação.
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
Se alguém especificar ambos -Force
e -WhatIf
, então -WhatIf
precisa ter prioridade. Essa abordagem preserva o -WhatIf
processamento porque ShouldProcess
sempre é executado.
Não adicione uma verificação para o $Force
valor dentro da if
instrução com o ShouldProcess
. Esse é um anti-padrão para este cenário específico, embora seja isso que mostro na próxima seção para ShouldContinue
.
DeveContinuar -Força
Esta é a maneira correta de implementar -Force
com ShouldContinue
o .
function Test-ShouldContinue {
[CmdletBinding()]
param(
[Switch]$Force
)
if($Force -or $PSCmdlet.ShouldContinue('TARGET','OPERATION')){
Write-Output "Some Action"
}
}
Ao colocar o $Force
à esquerda do -or
operador, ele é avaliado primeiro. Escrevê-lo desta forma provoca um curto-circuito na execução da if
declaração. Se $force
for $true
, então o ShouldContinue
não é executado.
PS> Test-ShouldContinue -Force
Some Action
Não temos que nos preocupar com -Confirm
ou -WhatIf
neste cenário porque eles não são suportados pela ShouldContinue
. É por isso que tem de ser tratado de forma diferente do ShouldProcess
.
Questões relativas ao âmbito de aplicação
Usando -WhatIf
e -Confirm
são supostos para aplicar a tudo dentro de suas funções e tudo o que eles chamam. Eles fazem isso definindo $WhatIfPreference
ou $true
definindo $ConfirmPreference
para Low
no escopo local da função. Quando você chama outra função, chama para ShouldProcess
usar esses valores.
Isso realmente funciona corretamente na maioria das vezes. Sempre que você chamar um cmdlet interno ou uma função no mesmo escopo, ele funcionará. Ele também funciona quando você chama um script ou uma função em um módulo de script do console.
O único lugar específico onde ele não funciona é quando um script ou um módulo de script chama uma função em outro módulo de script. Isso pode não parecer um grande problema, mas a maioria dos módulos que você cria ou extrai do PSGallery são módulos de script.
A questão central é que os módulos de script não herdam os valores para $WhatIfPreference
ou $ConfirmPreference
(e vários outros) quando chamados de funções em outros módulos de script.
A melhor maneira de resumir isso como uma regra geral é que isso funcione corretamente para módulos binários e nunca confie nele para funcionar para módulos de script. Se você não tiver certeza, teste-o ou simplesmente assuma que ele não funciona corretamente.
Eu pessoalmente sinto que isso é muito perigoso porque cria cenários onde você adiciona -WhatIf
suporte a vários módulos que funcionam corretamente isoladamente, mas não funcionam corretamente quando eles ligam uns para os outros.
Temos um RFC do GitHub trabalhando para corrigir esse problema. Consulte Propagar preferências de execução além do escopo do módulo de script para obter mais detalhes.
Para finalizar
Eu tenho que procurar como usar ShouldProcess
cada vez que eu preciso usá-lo. Demorei muito tempo a distinguir ShouldProcess
de ShouldContinue
. Eu quase sempre preciso procurar quais parâmetros usar. Portanto, não se preocupe se você ainda ficar confuso de vez em quando. Este artigo estará aqui quando você precisar. Tenho certeza de que farei referência a ele muitas vezes.
Se você gostou deste post, por favor, compartilhe seus pensamentos comigo no Twitter usando o link abaixo. Eu sempre gosto de ouvir de pessoas que obtêm valor do meu conteúdo.
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários