Windows PowerShell: コマンドを再利用可能なツールにする

Windows PowerShell では、コマンドやコマンドレットを使用して実行した処理をパッケージ化して再利用することができます。

Don Jones

Windows PowerShell を使い始めたときには経験が乏しくても、成長の余地は十分あります。単純なコマンドを実行することから始めて、少し複雑なコマンドの実行に移行し、最終的には、Windows PowerShell で実行したコマンドをネイティブなコマンドレットに見間違うほどの外観と操作性を備えたツールとしてパッケージ化できるようになります。このようなコマンドを高度な関数 (通称、スクリプト コマンドレット) と言います。

たとえば、コンピューターから重要なインベントリ情報を取得する必要がある場合について考えてみましょう。Windows のバージョン、BIOS のシリアル番号、サービス パックのバージョン、プロセッサのアーキテクチャが必要だとします。これらの情報は次の 3 つのコマンドを実行して取得できます。

Get-WmiObject –class Win32_OperatingSystem –computername SERVER-R2 | Select-Object –property __SERVER,BuildNumber,Caption,ServicePackMajorVersion
Get-WmiObject –class Win32_BIOS –computername SERVER-R2 | Select-Object –property SerialNumber
Get-WmiObject –class Win32_Processor –computername SERVER-R2 | Select-Object –property AddressWidth

ただし、これらのコマンドを実行すると 3 つの結果セットが生成されるという問題があります。たとえば、すべての情報を 1 つの CSV ファイルや HTML ファイルに直接パイプライン処理して、インベントリ データを格納したり、Web ページとして表示することはできません。この場合は、あまり経験がないユーザーも使用できる 1 つのパラメーター化されたコマンドにデータをバンドルするのが適切です。そのためには、次の処理を実行するコマンドが必要になります。

  • 次のように 1 台または複数台のコンピューター名を文字列として受け取る
Get-Content names.txt | Get-OSInfo | ConvertTo-HTML | Out-File info.html
  • 次のように 1 台または複数台のコンピューター名を –computername パラメーターで受け取る
Get-OSInfo –computername Server-R2,ServerDC4 | Format-Table
  • 次のようにパイプラインから computername プロパティがある 1 つまたは複数のオブジェクトを受け取る
Get-ADComputer –filter * -searchbase "ou=West,dc=company,dc=com" | Select-Object @{label='computername';expression={$_.Name}} | Get-OSInfo | Export-CSV inventory.csv

このようにすると、コンピューター名の入力元について危惧する必要がなくなります。また、作成する出力の種類について悩む必要もありません。この 2 つの処理はシェル側で問題なく実行されます。

ログ ファイルのパスとファイル名を指定できる -logfile パラメーターを使用することもお勧めします。このコマンドでは、Windows Management Instrumentation (WMI) を使用して、コンピューターに接続して情報をクエリしていますが、指定のコンピューターにアクセスできないことがあります。アクセスできないコンピューター名もログ ファイルに記録する必要があります。ログ ファイルに記録しておけば、問題のトラブルシューティングを行ったり、ログ ファイルに記録されているコンピューターについてデータの取得を再試行できます。

エラー処理

最後の処理は、Windows PowerShell で Try…Catch 構文を使用して対応できます。まず、1 つ目の WMI クエリを Try…Catch ブロックでラップします。–ErrorAction Stop パラメーターを指定して、コマンドで生成されたエラーをキャッチするようにします。また、エラーの発生状況を追跡する変数を設定して、スクリプトで後続の 2 つの WMI クエリを実行して問題ないかどうかを把握できるようにします。

$continue = $True
Try {
  Get-WmiObject –class Win32_BIOS –computername $computername –EA Stop
} Catch {
  $continue = $False
  $computername | Out-File –path $logfile –append
}

入力処理

難しいのは入力処理です。入力は、パイプラインとパラメーターのいずれかを経由して受け取ります。パイプライン経由で受け取った入力は、コマンドレットの場合と同じようにパラメーター バインドを使用してパラメーターにリダイレクトできます。基本的な構造は次のとおりです。

Function Get-OSInfo {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
  [string[]]$computername,
  [string]$logfile 
 )
  BEGIN {}
  PROCESS {}
  END {}
}

コマンドレットの PROCESS ブロックは、少なくとも 1 回は必ず実行されます。そのため、パイプライン経由で入力が提供されない場合も 1 回は実行されます。パイプライン経由で入力が提供された場合、PROCESS ブロックは、パイプライン処理される項目ごとに 1 回実行されます。そのため、項目は $computername 変数に格納されます。

ここに問題があります。入力がパラメーター経由でしか提供されない場合 (箇条書きの 2 つ目の場合)、PROCESS ブロックは 1 回しか実行されず、$computername 変数には、パラメーターに渡された全コンピューターの名前が格納されます。$computername 変数に格納されたコンピューター名は、自分で列挙 (つまり、分解) する必要があります。これらの項目がパイプラインで渡された場合、項目は 1 つずつ処理するだけで問題ありませんが、これらの項目も $computername 変数に格納されています。

この問題は、この処理を実行する別の関数を作成することで解決できます。高度な関数を使用して、どの種類の入力も受け付けて、入力を個別のコンピューター名に分解します。その後、コンピューター名を 1 つずつ処理する別の関数 (作業関数) を呼び出します。メイン関数の PROCESS ブロックに配置するスクリプトの基本構造は次のとおりです。

PROCESS {
  if ($PSBoundParameters.ContainsKey('computername')) {
    foreach($computer in $computername) {
      OSInfoWorker –computername $computer –logfile $logfile
    }
  } else {
    OSInfoWorker –computername $computername –logfile $logfile
  }
}

仕上げ

技術面の大きな障害を解消できたので、これらのコマンドを組み合わせて、詳細な処理を実行するコマンドを追加します。たとえば、関数を実行する前に、古いログ ファイルを消去するなどの処理は、BEGIN ブロックに配置するのが適切です。その結果出来上がった 2 つの関数で構成されるスクリプトは少し長いものになりますが、実際にプログラムで操作している部分は、ごくわずかです。スクリプトの大半は、皆さんがコマンド ラインで実行する Windows PowerShell のコマンドで構成されています。1 つのコマンドレットのように動作させるために、そのようなコマンドが多くの宣言構造で囲まれているに過ぎません。

詳細については、私の Web サイト (ow.ly/39YcX、英語) でビデオ チュートリアルと併せて公開していますので、そちらをご覧ください。このサンプルは、テンプレートとして、ご利用していただけます。メイン関数は、単に入力を処理しているだけなので、ほぼそのままの状態で再利用できます。ニーズに合わせてパラメーターを変更すれば準備完了です。実際の処理は、もう 1 つの関数で行われます。

来月のコラムでは、簡単に配布できるスクリプト モジュールとしてパッケージ化する方法を紹介します。また、同僚の管理者が使用できるスクリプト モジュールを格納する共有の場所を用意する方法についても紹介します。

Don Jones

Don Jones は、Concentrated Technology の創設者で、ConcentratedTech.com (英語) で Windows PowerShell や他のテクノロジに関する質問に答えています。また、Nexus.Realtimepublishers.com (英語) の創設者でもあり、このサイトでは、彼の多くの著書が無料でオンライン ブックとして提供されています。

 

補足情報

今月のコラムでは、補足資料としてサンプル コードとビデオ チュートリアルを提供しています。ここからダウンロードして、ご利用ください。

関連コンテンツ