Windows PowerShell史上最高のインベントリ ツール
Don Jones
目次
ツールを詳しく調べる
試用する
コンピュータを一括で指定する
スクリプトを使用してできること
改良を加える
優れた自作ツール
これまでの数回のコラムで、Windows PowerShell を使用してコンピュータのインベントリ情報を収集するためのさまざまな技法について説明してきました。今月は、これらすべての技法を組み合わせて、Windows Management Instrumentation (WMI) からあらゆる種類の情報を収集でき、すぐに使えるツールを作成します。
このツールは、テキスト形式のコンピュータ名の一覧、またはActive Directory から照会したコンピュータ名を操作するように設計されています。
ツールを詳しく調べる
まず、スクリプト自体 (図 1 参照) について説明し、スクリプトを構成する要素について調べてから、使用しているいくつかのテクニックについて説明します。これらのテクニックの中には少し奇妙に見えたり場違いに思えるものもあるかもしれませんが、スクリプトで使用されているさまざまなすばらしい手法を説明するために、ここではそれらをひとまとめにしています。
図 1 スクリプト
1. function Get-WmiInventory {
2. param (
3. $wmiclass = "Win32_OperatingSystem"
4. )
5. PROCESS {
6. $ErrorActionPreference = "SilentlyContinue"
7. $computer = $_
8. trap {
9. $computer | out-file c:\errors.txt -append
10. set-variable skip ($true) -scope 1
11. continue
12. }
13. $skip = $false
14. $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop
15. if (-not $skip) {
16. foreach ($obj in $wmi) {
17. $obj | Add-Member NoteProperty ComputerName $computer
18. write $obj
19. }
20. }
21. }
22. }
1 行目で、関数名が Get-WmiInventory であることがわかります。そのすぐ下では、$wmiclass という入力パラメータを定義し、その値として "Win32_OperatingSystem" という既定値を指定しています。PROCESS スクリプト ブロックは、これがフィルタ処理関数であることを示しています。これは、パイプラインからコンピュータ名のコレクションを受け取るように設計されています。その情報を関数に渡す方法については、この後すぐに説明します。
6 行目で、シェルの通常のエラー報告動作を無効にしています。これは、独自のエラー ログ記録を用意するためです。7 行目では、(パイプラインから関数に渡される) 現在のコンピュータ名を単純に取得し、$computer 変数に格納します。
Windows PowerShell に関する Q&A
Q Windows PowerShell を使用して Windows Server 2008 Server Core を管理することはできますか。
A もちろんできます。Server Core ではシェルで必要となる .NET Framework が現状サポートされていないため、Windows PowerShell を Server Core にインストールできないことはおそらくご存じだと思います。しかし、これは問題にはなりません。賢明な管理者たちの多くは、サーバーにはできる限り何もインストールしないようにします。もちろん、Windows PowerShell も例外ではありません。代わりに、Windows PowerShell をクライアント コンピュータにインストールし、Windows Management Instrumentation (WMI)、Active Directory などの多くのテクノロジを使用して、Server Core をリモートに管理できます。たとえば、使い心地の良さをまったく変えることなく、Server Core ベースのドメイン コントローラ上で、Active Directory のほぼすべての側面を容易に管理できます。
では、13 行目に移ります。ここでは、$skip という変数を作成して、ブール値 False に設定します (これについては、後で詳しく説明します)。14 行目で Get-WmiObject コマンドレットを使用して、現在のコンピュータから目的の WMI 情報の取得を試みています。–ErrorAction (–EA) パラメータを指定していることに注意してください。これにより、なんらかの理由で WMI を取得できなかった場合に例外を生成することをシェルに指示します。
15 行目では、$skip 変数に False がまだ含まれているかどうかを確認します。False の場合は、16 行目で WMI から返されるすべてのアイテムを列挙し、(17 行目で) ComputerName プロパティをそれぞれの WMI オブジェクトに追加します。その結果、複数のコンピュータから WMI オブジェクトが返される場合に、この便利なプロパティに含まれる親コンピュータの名前を各オブジェクトのラベルとして使用できます。18 行目で、各オブジェクトをパイプライン出力します。これにより、別のコマンドレットがオブジェクトを使用できるようになったり、オブジェクトの一部のプロパティの表示にシェルの書式設定サブシステムが引き継がれたりするようになります。
しかし、問題が発生した場合はどうすればよいでしょうか。–EA Stop を指定したので、シェルによって 8 行目のトラップが実行されます。まず、コンピュータ名がテキスト ファイルに書き込まれ、インベントリに失敗したコンピュータ名のログが作成されます。その後、10 行目で $skip 変数をブール値 True に設定します。これにより、15 行目では何の出力も試みられなくなります。こうしておかないと、インベントリに失敗した各コンピュータでは、前のコンピュータのインベントリ情報が再度出力されることになります。
10 行目では、変数の設定に別のテクニックを使用しています。トラップ自体はプライベート変数のスコープなので、$skip 変数は含まれません。対象の $skip 変数は、1 つ上位の、トラップの親スコープにあります。Set-Variable コマンドレットを使用すると、-scope パラメータを使用して変数を変更し、目的の $skip 変数が 1 つ上位にあることを示すことができます。Set-Variable コマンドレットを使用する際には、変数名の前にドル記号 ($) が含まれないことにも注意してください。
試用する
このスクリプトをテストする簡単な方法は、次のように 1 つのコンピュータ名をパイプするだけです。
"localhost" | Get-WmiInventory
WMI クラスの名前を指定していないので、既定値の Win32_OperatingSystem が使用されます。別のクラスを指定するには、次のようにします。
"localhost" | Get-WmiInventory "Win32_LogicalDisk"
図 2 は結果を示しています。たくさんのコンピュータ名を含める場合は、次のように、コンピュータ名をコンマ区切りのリストにするだけです。
"localhost","server2","client17" |
Get-WmiInventory "Win32_LogicalDisk"
図 2 Get-WmiInventory の結果
コンピュータを一括で指定する
コンピュータ名のリストが長くなってしまう場合、コンピュータ名を個別に指定するのは非常に面倒です。(1 行につき 1 つのコンピュータ名を記載した) コンピュータ名の一覧を含むテキスト ファイルが既にある場合は、次のようにして名前をインベントリ関数にパイプできます。
Get-Content c:\names.txt | Get-WmiInventory "Win32_Service"
また、Get-QADComputer コマンドレットを使用することもできます。これは、quest.com/powershell から入手できる無償の Active Directory 用コマンドレットの 1 つとして提供されています。Active Directory 用コマンドレットをインストールし、Windows PowerShell を開いて、次のスクリプトを実行する必要があります。
Add-PSSnapin Quest.ActiveRoles.ADManagement
Get-QADComputer コマンドレットを実行して、Active Directory からすべてのコンピュータを取得します。ただし、大規模ドメインではこの処理にしばらく時間がかかるので注意してください。Help Get-QADComputer を実行して、コンピュータ一覧のフィルタ処理に使用できるいくつかのオプションを確認できます。たとえば、特定の組織単位 (OU) 内に存在しているコンピュータのみを取得するといったオプションもあります。また、スクリプトに少し変更を加えて、7 行目を次のように変更することも必要です。
$computer = $_
$computer = $_.Name
Get-QADComputer によって生成されるオブジェクトは、コンピュータ名を単純な文字列オブジェクトとしてではなく Name プロパティ内に格納するので、この処理が必要になります。
スクリプトを使用してできること
このスクリプトの既定の出力では、Windows PowerShell の書式設定サブシステムによって既定で作成されるあらゆる形式の出力を利用できます。たとえば、Win32_OperatingSystem クラスは、そのクラスから実際に取得できる情報の小さなサブセットです (プロパティは 6 つだけです)。このスクリプトの出力を標準的なあらゆるシェル コマンドレットにパイプして、カスタマイズすることができます。コンピュータ名と一緒にビルド番号とサービス パック情報を表示するには、次のスクリプトを実行します。
Get-Content c:\names.txt | Get-WmiInventory |
Format-List BuildNumber,ServicePackMajorVersion,ComputerName
インストールされているサービスのインベントリを取得して、HTML 形式のテーブルを生成する必要がある場合は、次のスクリプトを実行します。
Get-Content c:\names.txt | Get-WmiInventory "Win32_Service" |
ConvertTo-HTML | Out-File c:\services_inventory.html
論理ディスク情報の完全なインベントリを取得して、その情報をコンマ区切り値 (CSV) ファイルに書き込む場合は、次のスクリプトを実行します。
Get-Content c:\names.txt |
Get-WmiInventory "Win32_LogicalDisk" | Export-CSV c:\disks.csv
Get-WmiInventory 関数では、単純なテキストではなくオブジェクトが生成されます。そのため、データの用途が、統計的傾向を示すためであっても、管理レポートを作成するためであっても、重要な情報の簡易表示を行うことであっても、シェルはデータを必要なさまざまな形式に変換することができます。
改良を加える
このスクリプトの欠点の 1 つは、指定したクラスからすべての WMI オブジェクトが取得されることです。複数のクラスを指定すると、データの量が非常に多くなる可能性があります。通常、私は Win32_LogicalDisk を制限して、DriveType プロパティが 3 のローカル ディスクだけを取得するようにします。スクリプトを簡単に変更して、このようなフィルタ条件を含めることができます。改訂後のスクリプトは図 3 のようになります。この新しいスクリプトは次のように呼び出します。
Get-Content c:\names.txt |
Get-WmiInventory "Win32_LogicalDisk" "DriveType='3'"
図 3 フィルタ処理がサポートされる改訂後のスクリプト
1. function Get-WmiInventory {
2. param (
3. $wmiclass = "Win32_OperatingSystem"
4. $filter = ""
5. )
6. PROCESS {
7. $ErrorActionPreference = "SilentlyContinue"
8. $computer = $_
9. trap {
10. $computer | out-file c:\errors.txt -append
11. set-variable skip ($true) -scope 1
12. continue
13. }
14. $skip = $false
15. $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop –filter $filter
16. if (-not $skip) {
17. foreach ($obj in $wmi) {
18. $obj | Add-Member NoteProperty ComputerName $computer
19. write $obj
20. }
21. }
22. }
23. }
4 行目と 15 行目の変更では、$filter という新しいパラメータを収集し、そのパラメータを Get-WmiObject の –filter パラメータに渡しています。この関数を呼び出すとき $filter パラメータは 2 つ目のパラメータになるため、WMI クラスの名前の後ろに 2 つ目の値として渡します。特定の環境に合うようにこの関数をカスタマイズしようと思えば、もっとたくさんのことを実行できます。しかし、行ってはいけないことがいくつかあります。
- 関数の出力をパイプライン以外に送信してはいけません。これは、改訂後のスクリプトの 19 行目で行われています。つまり、関数から情報をファイルに出力しないでください。代わりに、関数のパイプライン出力を、Out-File などのコマンドレット (出力をファイルや他のメディアに移動します) に送信します。こうすることで、関数の柔軟性が保たれます。
- 関数内で情報を除外してはいけません。つまり、WMI オブジェクトのプロパティを削除したり、プロパティのサブセットだけを出力したりしないでください。この操作でも、その後のシナリオにおける関数の柔軟性が低くなります。情報をすべて出力する必要がない場合は、その出力を Format コマンドレットか Select-Object にパイプすれば、手元の作業で必要な情報だけを取得できます。
- 関数に別の作業を追加してはいけません。多くの作業を実行する "スーパー関数" を作成しないでください。各関数が受け持つ作業は 1 つだけにします。複雑な処理がある場合は、個別の処理を単独の関数として作成し、ある関数から別の関数に情報をパイプすることで、その処理を行います。その結果、それぞれの関数の柔軟性がさらに高まり、管理が容易になり、デバッグがはるかに簡単になります。
優れた自作ツール
こういったスクリプトは、System Center Configuration Manager などの強力な構成管理システムの代わりにはなりませんが、一連のリモート コンピュータから選択したインベントリ情報を収集するための簡単な方法が提供されます。このスクリプトは、Windows PowerShell が最も得意とすることのうちの 1 つを示している良い例です。それは、現在使用している Windows やその他の製品の開発時にマイクロソフトが予測できなかった問題に対して、迅速で臨機応変な解決策を提供することです。
このスクリプトでは WMI を使用しているため、Windows NT 4.0 以降のバージョンのコンピュータで使用できます。ただし、Windows PowerShell そのものをそれほど古いコンピュータにインストールすることはできません。また、多くのマイクロソフト製品の管理情報は WMI を使用して公開されるため、さまざまな製品の情報を収集することができます。私は、WMI エクスプローラ ツール (無償のツールはツール ゾーン scriptinganswers.com で入手できます) を使用して、コンピュータで収集できる WMI 情報を参照し、この記事で説明したようなスクリプトを使用して複数のコンピュータからその WMI 情報を収集しています。
Don Jones は、Concentrated Technology の共同経営者で、『Windows PowerShell: TFM』(SAPIEN Press) の共著者でもあります。ConcentratedTech.com で、Don は、毎週 WindowsPowerShell に関するヒントを紹介しています。また、このサイトから、Don に連絡を取ることもできます。