PowerShellのWhatIfパラメーターをマスターする方法

「私は常にスクリプトをテストするわけではありませんが、テストを行うときは常に本番環境で行います」

嘘をつかないでください。私たちはみんな一度はやったことがあります。ただし、PowerShellスクリプトを本番環境で実行する際にはリスクを回避する方法があります。組み込みのPowerShell WhatIfパラメーターを使用することを忘れないでください!

環境に変更を加える前に、そのPowerShellコマンドが何をするのかを知ることができたら素晴らしいですよね?コマンドに対して「実行する場合、あなたは何をしますか?」と尋ねることができます。WhatIfパラメーターを使用できます。

すべてのコンパイル済みPowerShellコマンドレットには、WhatIfというパラメーターが含まれています。このパラメーターを使用すると、コマンドが予想どおりに動作するか、核融合を引き起こすかを評価することができます。

便利なWhatIfパラメーターは、組み込みのコマンドレットだけでなく、スクリプトや高度な関数でも利用できます!コードが環境に変更を加える場合、実装することを選択すれば、予期しない結果に対する保護機構が得られます。

前提条件

この記事は、実際に手を動かしながら進めるものです。この旅の間に一緒に進むためには、いくつかの準備が必要です。

この記事では、Windows 10のマシンでVisual Studio Code 1.38(2019年8月)を使用しています。

WhatIf PowerShellパラメーターの定義

要するに、WhatIfパラメータは、すべての高度な関数やCmdletで利用可能な組み込みのスイッチパラメータです(スクリプトや関数にPowerShellのCmdletBindingキーワードを追加することで利用できます)。使用すると、コマンドの予想される効果をコンソールに報告しますが、実際にコマンドを実行しません。

すべてのCmdletや高度な関数には、WhatIfパラメータが利用可能です。この記事では、Get-ServiceStop-Service、およびNew-ItemのCmdletを使用して、このパラメータをデモンストレーションします。

PowershellのWhatIfサポートのチェック

特定のコマンドがWhatIfをサポートしているかどうかわからない場合、2つの簡単な方法があります。

Get-Commandの使用

以下のようにSyntaxパラメータを使用して、Get-Commandコマンドを使用してコマンドのメタデータを表示できます。 -WhatIfの参照が表示されれば、WhatIfがサポートされています。

Get-Command <CommandName> -Syntax

タブ補完の使用

タブキーを使用して、PowerShellコンソールにチェックしたいコマンドを入力した後、スペース、ダッシュ、’Wh’を入力します。

WhatIfが表示されれば、そのコマンドにはWhatIfパラメータがあることがわかります。

PS> <CommandName> -Wh[tab]

CmdletとともにPowerShellのWhatIfパラメータを使用する

WhatIfパラメーターを活用する方法はさまざまあります。このセクションでは、組み込みのcmdletを使用してWhatIfパラメーターをすぐに利用する方法を学びます。

ファイルの作成

New-ItemコマンドレットにもWhatIfパラメーターがあります。この例では、New-Itemコマンドレットを使用して、同じ作業ディレクトリにnewfile1.txtという名前のファイルを作成します。

以下のコマンドを実行すると、newfile.txtという名前のファイルが作成されます。

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

しかし、もしコマンドが失敗した場合に問題を引き起こす可能性のあるファイルを作成する場合はどうでしょうか?心配ありません。末尾にWhatIfパラメーターを追加することができます。

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

サービスの停止

Stop-ServiceコマンドレットでもWhatIfパラメーターを使用することができます。この例では、最初の5つのサービスのリストを取得し、Stop-Serviceコマンドで停止しています。しかし、実際には停止していません。

代わりに、Stop-Serviceコマンドレットが停止するであろうサービスを示す出力メッセージがPowerShellコンソールに表示されます。

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

PowershellのWhatIfの動作をグローバルに変更する

この時点で、cmdletや高度な関数でWhatIfパラメーターを使用すると、操作がシミュレートされることを理解しているはずです。コマンドレベルでWhatIfの動作を影響させています。

WhatIfの動作は、自動変数$WhatIfPreferenceを操作することで、すべてのコマンドに影響を与えるようにも設定することができます。

$WhatIfPreference変数はブール型です。TrueまたはFalseのいずれかである必要があります。デフォルトでは、Falseに設定されており、コマンドレベルでオーバーライドされない限り、すべてのコマンドのWhatIfサポートが無効になります。Trueに設定された場合、WhatIfパラメータを明示的に使用するかどうかに関係なく、サポートしているすべてのコマンドが「WhatIfモード」になります。

$WhatIfPreferenceの値をTrueに変更することで、これをテストすることができます。変更方法は、$WhatIfPreference = $trueです。以下の例では、New-Itemを使用して、WhatIfパラメータを渡さない場合でも、パラメータが渡されたかのように動作します。

PS51> $WhatIfPreference = $true

$WhatIfPreferenceTrueに変更した場合は、$WhatIfPreference = $falseに戻すことを忘れないでください。

このセクションでは、既存のコマンドレットでのWhatIfサポートの使用方法について学びました。次の手順では、カスタムスクリプトと関数でのWhatIfサポートの構築方法について学びます。

関数でのPowerShell WhatIfサポートの実装

WhatIfサポートをスクリプトに実装する前に、やり方の間違いと正しい方法を知ることが重要です。次のセクションでそれらを見ていきます。

間違ったやり方:車輪の再発明を避ける

スクリプトの開発者が独自のWhatIfサポートを実装するために、いくつかのif/thenロジックを使って車輪を再発明することは珍しくありません。以下に例を示します。

開発者は独自のWhatIfスイッチパラメータを定義しました。そのパラメータの値を使用して、WhatIfパラメータが使用された場合と使用されなかった場合のロジックを処理するためにif/then構造を使用しています。

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

        [switch]$WhatIf
    )
    
    if ($WhatIf.IsPresent) { # WhatIfスイッチがオンの場合。
        Write-Host "WhatIf: I will remove the log file ""$name""" -ForegroundColor Yellow
    } else {
        # WhatIfスイッチがオフの場合。
        Write-Host "Delete log file ""$name""" -ForegroundColor Green
    }
}

上記の関数を以下のようにWhatIfパラメータを使用して実行すると、正常に動作したように見えます。この関数は実際には何も削除せず、コンソールにメッセージを返します。

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

問題がないなら、この方法には何が問題なのでしょうか?それは、高度な関数の組み込み機能を無視しているからです。コマンドがオフになった場合にコマンドが行う動作に焦点を当てるだけでなく、この機能を作成するために毎回関数に組み込む必要があります。

代わりに、車輪を再発明せず、SupportsShouldProcess キーワード $PSCmdlet.ShouldProcess()と組み合わせて使用してください。次に進みましょう。

正しく行う方法:SupportsShouldProcessの使用

[CmdletBinding()] キーワードを使用するすべての関数は「高度な」機能となります。このキーワードにより、WhatIfのサポートを含むさまざまな機能が追加されます。

すべての高度な関数はWhatIfの機能をサポートしますが、それを利用するかどうかはあなた次第です。以下のように、最初に[CmdletBinding()]の括弧の間にSupportsShouldProcessキーワードを使用する必要があります。

[CmdletBinding(SupportsShouldProcess)]

この関数では、WhatIfパラメータが関数に渡されたかどうかを確認するために、$PSCmdlet関数変数のShouldProcess()メソッドを呼び出すことができるようになります。WhatIfパラメータが使用されると、ShouldProcess()Falseを返します。それ以外の場合、常にTrueを返します。

Function Remove-LogFile {
     [cmdletbinding(SupportsShouldProcess)]
     param (
         [Parameter(Mandatory)]
         [string]$name
     )
     if ($PSCmdlet.ShouldProcess($name)) {
         ## -WhatIfパラメータは使用されませんでした。関数は通常通り処理されるべきです。
         Write-Host ("Delete log file " + $name) -ForegroundColor Green
     } else {
         ## -WhatIfパラメータが使用されました。関数は処理されないべきです。
     }
 }
WHATIF PARAMETER USED SHOULDPROCESS() RESULT
True False
False True

以下に、WhatIfパラメータを使用してRemove-LogFileが実行された場合の動作が表示されます。組み込みのcmdletと同じ動作が表示されます。

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

概要

この記事では、PowerShellのWhatIfパラメータについて学びました。どのように動作し、どのような利点があるかを理解することができるようになりました。

また、PowerShellの関数に対するフォールセーフが必要な場合、車輪を再発明する必要がないことを知るべきです。代わりに、既存のPowerShellのWhatIfサポートを活用しましょう!

さらに読む

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