Windows の管理

Windows PowerShell を使用してグループ ポリシーの管理を簡素化する

Thorbjörn Sjövold

 

概要:

  • Windows PowerShell とは
  • 以前の GPO の管理方法
  • スクリプトを Windows PowerShell に移行する

マイクロソフトのグループ ポリシー テクノロジは、すぐには定着しませんでした。これは、やや理解するのが難しかったこと、および当時の標準であったアカウントとリソースを使用したドメインとはまったく異なる新しいサービスである Active Directory を導入する必要があったからだと思います。

しかし今や、グループ ポリシーは Windows® インフラストラクチャを使用するほぼすべての組織の管理において重要な要素になりました。マイクロソフトの最新の管理テクノロジである Windows PowerShell™ についても同じことが起こる気がします。実際、Windows PowerShell により、グループ ポリシー管理者の作業は大幅に簡素化される可能性があります。

この記事では、VBScript (および一般的な COM ベースのスクリプト言語) などの Windows Scripting Host 言語向けに記述された Microsoft® グループ ポリシー管理コンソール (GPMC) API を Windows PowerShell から直接使用して、現在の環境で行っているグループ ポリシーの管理を簡素化する方法について説明します。

グループ ポリシーに関するタスクをスクリプト化する

数年前にマイクロソフトが GPMC をリリースしたとき、グループ ポリシー管理者は突如として、自由に使用できるたくさんの便利な機能を手に入れました。グループ ポリシー専用の MMC スナップインが提供されたことは、特にそれまで Active Directory® ユーザーとコンピュータを使用していたことを考えると、グループ ポリシーの管理にとって大きな前進であったと言えます。さらに、管理者がグループ ポリシー オブジェクト (GPO) のバックアップと復元、ドメイン間での GPO の移動、GPO とリンクに対するセキュリティ設定の構成、レポートの作成などのグループ ポリシーの管理タスクを、VBScript などの COM ベースの言語を使用して実行できる新しい API も提供されました。

残念なことに、GPMC では、グループ ポリシー オブジェクト内の構成済みの設定を編集することはできませんでした。つまり、GPO のバージョンの読み取り、変更日の読み取り、新しい GPO の作成、異なるドメインからの GPO のバックアップと復元、異なるドメインからの GPO のインポートなどの操作を GPO コンテナに対して実行することはできましたが、新しいリダイレクト フォルダや新しいソフトウェアのインストールの追加など、GPO の内容をプログラムから追加または変更することはできませんでした。このため、通常は GPO を作成し、グループ ポリシー オブジェクト エディタを使用してすべての設定を手動で構成した後、その GPO をバックアップしてテスト環境にインポートするという一連の作業を代わりに行っていました。そして、すべての設定を検証し、正常に機能していることを確認できたら、その GPO を運用環境にインポートしていました。上記のような機能の欠如はありましたが、手動による操作の代わりに GPMC API を使用したスクリプトを実行することにより、日々のグループ ポリシーの管理にかかる時間、作業、およびエラーが大幅に減少しました。

次のレベルへ

Windows PowerShell は、VBScript などのスクリプト言語とはどのように異なるのでしょうか。まず、Windows PowerShell はシェルです。また、少なくともここでの説明においては、シェルはコマンド ライン インタプリタと考えていただいてかまいません。VBScript はコマンド ラインから実行できますが、VBScript ファイルを 1 行ずつ実行することはできません。これとは対照的に、Windows PowerShell スクリプトは、その場で一連のコマンドとして作成できます。また、Windows PowerShell には、VBScript のサブルーチンとほぼ同じように動作し、Windows PowerShell コマンド プロンプトでリアルタイムに作成できる関数が用意されています。

さらに、VBScript は古い COM テクノロジに依存していますが、Windows PowerShell は Microsoft .NET Framework を基に構築されています。これは、今日開発されている大量の .NET コードを Windows PowerShell 内から直接使用できることを意味しています。

まとめると、Windows PowerShell により、スクリプトの完全なサポートと対話モードが 1 つのパッケージで提供されるということになります。ここで紹介する例はすべてコマンド ライン入力であるため、そのままコマンド ラインに入力することができますが、これらを Windows PowerShell スクリプト ファイルに保存して実行しても、同じように正しく機能します。

Windows PowerShell を使用して古いスクリプトを作り変える

新しいテクノロジの使用を開始するときに最も避けたいことは、過去の作業をすべて捨て去ることです。GPMC API から COM オブジェクトにアクセスするために使用できる方法は 3 つあります。基本的に、これらは古い VBScript を再利用するために使用することもできます。次にこれら 3 つの方法を示します。

  • C# やマネージ C++ などのプログラミング言語を使用して、Windows PowerShell コマンドレットを作成する。
  • Windows PowerShell を使用して MSScript.ocx の ScriptControl にアクセスし、古いスクリプトをラップする。
  • 再利用可能な Windows PowerShell 関数に COM 呼び出しをラップするか、COM オブジェクトを直接呼び出す。

主に重点を置いて説明するのは 3 つ目の方法ですが、最初にすべての方法について簡単に説明します。

Windows PowerShell コマンドレットを作成する

Windows PowerShell には、ファイルのコピー、出力の書式設定、日時の取得などを実行できる大量のコマンドレットが含まれていますが、独自のコマンドレットを作成することもできます。この作成プロセスは msdn2.microsoft.com/ms714598.aspx にすべて記載されています。要約すると、次のような手順を実行します。

  • C# などの .NET プログラミング言語で、クラス ライブラリ DLL を作成する。
  • 新しいクラスを基本クラスのコマンドレットから継承して作成する。
  • 名前、使用方法、入力パラメータなどを決定する属性を設定し、独自のコードを追加する。

Windows PowerShell は .NET Framework に基づいて構築されているため、文字列型やオブジェクト型など、コード内でパラメータとして返されたり渡されたりしている型は、Windows PowerShell でもまったく同じように使用できるため、特殊な型変換は必要ありません。

Windows PowerShell の最大の利点は、完全なプログラミング言語を自在に使用できることです。

MSScript.ocx の ScriptControl オブジェクトを使用して古いスクリプトをラップする

VBScript ファイルを実行するには、当然 VBScript エンジンが必要です。しかし、VBScript エンジンは COM オブジェクトであり、また COM オブジェクトは Windows PowerShell から使用できるため、VBScript エンジンを呼び出すことは可能です。次のようなコードを使用します。

$scriptControl = New-Object -ComObject ScriptControl
$scriptControl.Language = ‘VBScript’
$scriptControl.AddCode(
    ‘Function ShowMessage(messageToDisplay)
    MsgBox messageToDisplay
    End Function’)
$scriptControl.ExecuteStatement(‘ShowMessage
    “Hello World”’)

上記のコードを Windows PowerShell コマンド ライン インターフェイス (CLI) に入力すると、パラメータが指定された VBScript 関数 ShowMessage が呼び出されて実行され、メッセージ ボックスに "Hello World" というテキストが表示されます。

何人かの読者の皆さんは、"これで Windows PowerShell から COM を使用する方法をマスターできたから、この記事をもう読まなくても、古い GPMC スクリプト群を ScriptControl オブジェクトに詰め込めばいいだろう" と考えたかもしれませんが、残念ながらそれはお勧めできません。この方法では、スクリプトが長くなると、すぐにコードが複雑になり、デバッグが難しくなります。

COM オブジェクトをラップする

このため、3 つ目の方法を使用するのが最も適切です。この方法では、再利用可能な Windows PowerShell 関数に COM 呼び出しをラップすることにより、COM オブジェクトを GPMC API で使用できるようにします。次のコードは、Windows PowerShell で直接 .NET オブジェクトを作成する方法を示しています。このコードでは、FileInfo オブジェクトを使用してファイルのサイズを取得します。

$netObject = New-Object System.IO.FileInfo(
“C:\boot.ini”) # Create an instance of FileInfo 
               # representing c:\boot.ini

Windows PowerShell では、コード内のコメントに # を使用することに注意してください。この新しくインスタンス化した FileInfo オブジェクトを使用すると、次のコードを入力するだけで boot.ini のサイズを容易に取得できます。

$netObject.Length # Display the size in bytes of the
                  # file in the command line interface

ちょっと待ってください。COM オブジェクトと VBScript の変換について説明する予定だったのではないでしょうか。だいじょうぶです、ちゃんと説明しますから、次のコマンドを見てください。

$comFileSystemObject = New-Object –ComObject Scripting.FileSystemObject

構文は、基本的に上記で .NET Framework からネイティブ オブジェクトを作成するときに使用したものと同じです。異なる箇所は次の 2 つです。1 つ目は、.NET オブジェクトではなく COM オブジェクトを作成することを Windows PowerShell に指定する –ComObject スイッチが追加されています。2 つ目は、.NET コンストラクタの代わりに COM ProgID (ここでは Scripting.FileSystemObject) が使用されています。この ProgID は、皆さんがいつも使用してきたものと同じ名前を指定できます。対応する VBScript のコードを次に示します。

Set comFileSystemObject = CreateObject(
    “Scripting.FileSystemObject”)

VBScript を使用してファイルのサイズを取得するには、上記のコードと共に、次のコードをファイルに追加します。

Set comFileObject = comFileSystemObject.GetFile(
    “C:\Boot.ini”)
WScript.Echo comFileObject.Size

その後、Cscript.exe などを使用して、作成したファイルを実行します。Windows PowerShell では、次のコードによって同じ処理を実行できます (Windows PowerShell コマンド ラインから直接実行することもできます)。

$comFileObject = $comFileSystemObject.GetFile(
    “C:\boot.ini”)
$comFileObject.Size

もちろん、ファイルのサイズを読み取る VBScript を変換するために、ドライブ内のオブジェクトを管理する Windows PowerShell コマンドレットを使用することもできましたが、Windows PowerShell から COM へのアクセスが容易であることを示すために上記のコードを使用しました。Windows PowerShell には COM オブジェクトを作成することを指定しましたが、ここで実際に作成される $comFileSystemObject は、COM オブジェクトをラップし、そのオブジェクトのインターフェイスを公開する .NET オブジェクトであることに注意してください。ただしこの記事では、このことはあまり重要ではありません。

Windows PowerShell の実際の使用例

Windows PowerShell から COM にアクセスする方法について理解したところで、次はグループ ポリシーに焦点を当てます。ここで示す例は、Windows PowerShell から GPMC API を使用する方法について説明するための短いコード スニペットですが、technetmagazine.com/code07.aspx からダウンロードできる、この記事の関連コードには、グループ ポリシーを管理するための Window PowerShell 関数一式が含まれています。図 1 は、この関連コードに含まれる関数の一覧を示しています。

Figure 1 この記事の関連コードに含まれているカスタム関数

関数名 説明
BackupAllGpos ドメイン内のすべての GPO をバックアップします。
BackupGpo 1 つの GPO をバックアップします。
RestoreAllGpos バックアップからすべての GPO をドメインに復元します。
RestoreGpo バックアップから 1 つの GPO を復元します。
GetAllBackedUpGpos 特定のパスから GPO の最新のバックアップを取得します。
CopyGpo GPO の設定を別の GPO にコピーします。
CreateGpo 新しい空の GPO を作成します。
DeleteGpo GPO を削除します。
FindDisabledGpos ユーザーとコンピュータの部分が両方無効になっているすべての GPO を返します。
FindUnlinkedGpos リンクが含まれていないすべての GPO を返します。
CreateReportForGpo ドメイン内の 1 つの GPO に関する XML レポートを作成します。
CreateReportForAllGpos ドメイン内の各 GPO に関する XML レポートを個別に作成します。
GetGpoByNameOrID GPO を表示名または GPO ID で検索します。
GetBackupByNameOrId GPO のバックアップを表示名または GPO ID で検索します。
GetAllGposInDomain ドメイン内のすべての GPO を返します。

このセクションを読みながら、Windows PowerShell コマンド ラインを起動し、コマンドを入力してもかまいません。ただし、一部のコマンドは、その前に実行したコマンドに依存していることに注意してください。つまり、最初に作成した一部のオブジェクトは後で使用するため、途中で Windows PowerShell セッションを終了しないようにする必要があります。セッションを終了した場合、最初からやり直すことになるため、すべてのコマンドを再入力する必要があります。

では、Windows PowerShell を使用した新しい GPO の作成を開始しましょう。マイクロソフトのグループ ポリシー チームは、GPMC と的確に連携して動作し、作業の効率化に役立つ、多数の VBScript サンプルを作成しました。これらのサンプルは、%ProgramFiles%\GPMC\Scripts ディレクトリにあります。このディレクトリには、GPMC API に関するドキュメントが含まれた gpmc.chm ファイルも格納されています。では、CreateGPO.wsf スクリプトを細かく分析して、動作の中身を見ていきましょう。

ファイルの先頭付近に次のようなコードがあります。

Dim GPM
Set GPM = CreateObject(“GPMgmt.GPM”)

基本的に、上記のコードがすべてのグループ ポリシー管理セッションまたはスクリプトの開始点になります。ここで GPMgmt.GPM クラスのインスタンスを作成することにより、ほとんどの GPMC の機能にアクセスできるようになります。この操作を Windows PowerShell から実行する場合、次のコードを使用します。

$gpm = New-Object -ComObject GPMgmt.GPM

これでグループ ポリシーの管理を行うための準備ができたので、次はこのオブジェクトを使用して実行できる操作を確認します。通常、このような情報を得るにはドキュメントを参照しますが、Windows PowerShell には非常に優れた機能が用意されています。次のコードを入力すると、図 2 のような出力が得られます。

図 2 Get-Member の出力

図 2** Get-Member の出力 **(画像を拡大するには、ここをクリックします)

$gpm | gm

すばらしいですね。Get-Member (または gm) コマンドレットを使用すると、オブジェクトでサポートされているプロパティとメソッドを直接コマンド ラインから確認できます。もちろん、ドキュメントを読む場合とは異なりますが、使い慣れているオブジェクトのパラメータの数やオブジェクト名などを正確に思い出せない場合には役立ちます。ここで注意すべき重要なことがあります。GPMC のドキュメントに表示されているツリー形式の一覧では、GPM オブジェクトと他のすべてのクラスの前に I という文字が付いていますが、これは COM の内部構造によるものであり、この記事の内容には影響しません。これはネイティブ COM コードを記述する C++ プログラマが参照するためのものであり、インターフェイスと、それを実装するクラスを区別しています。また、GPMC API を使用する際に、上記のような方法で作成するオブジェクトは GPMgmt.GPM だけです。他のすべてのオブジェクトは、この GPM オブジェクトを起点としたメソッドを使用して作成されます。

次に、新しい GPO を作成しましょう。

図 3 は、GPO の作成が非常に容易であることを示しています。エラー処理 (たとえば、GPO の作成を許可されていない場合の処理) など、一部のコードを省略し、ドメイン名をハードコードしてありますが、だいたいの方法はおわかりいただけると思います。

Figure 3 GPO を作成する

$gpmConstants = $gpm.GetConstants() 
# This is the GPMC way to retrieve all 
# constants
$gpmDomain =$gpm.GetDomain(“Mydomain.local”, “”, $gpmConstants.UseAnyDC)
# Connects to the domain where the GPO should 
# be created, replace Mydomain.local with the 
# name of the domain to connect to.
$gpmNewGpo = $gpmDomain.CreateGPO() 
# Create the GPO
$gpmNewGpo.DisplayName = “My New Windows PowerShell GPO” 
# Set the name of the GPO

GPO の作成方法について理解したら、次は既存の GPO を開いてみましょう。ドメインへの参照である $gpmDomain が残っているので、次のコードを入力します。

$gpmExistingGpo = $gpmDomain.GetGPO(
  “{31B2F340-016D-11D2-945F-00C04FB984F9}”) 
# Open an existing GPO based on its GUID, 
# in this case the Default Domain Policy.
$gpmExistingGpo.DisplayName 
# Show the display name of the GPO, it 
# should say Default Domain Policy
$gpmExistingGpo.GenerateReportToFile($gpmConstants.ReportHTML, “.\DefaultDomainPolicyReport.html”

上記の場合は、既定のドメイン ポリシーの設定がすべて記載された HTML レポートが生成されますが、当然どのメソッドとプロパティを使用してもかまいません。たとえば、GPO が最後に変更された時間を示す ModificationTime を使用すると、GPO の設定がいつ変更されたかを確認できます。

これは非常に役立ちます。たとえば、よくユーザーからコンピュータの動作がおかしいという問い合わせが殺到することがあります。この場合、GPO の設定が変更、追加、または削除されたことが原因ではないかと疑っても、どの GPO を調べればよいかがわかりません。そんなとき、Windows PowerShell が救いの手を差し伸べてくれます。図 4 のようなスクリプトを Windows PowerShell コマンド ラインに入力すると、過去 24 時間以内に変更されたすべての GPO を確認できます。

Figure 4 変更された GPO を検出する

$gpmSearchCriteria = $gpm.CreateSearchCriteria() 
# We want all GPOs so no search criteria will be specified
$gpmAllGpos = $gpmDomain.SearchGPOs($gpmSearchCriteria) 
# Find all GPOs in the domain
foreach ($gpmGpo in $gpmAllGpos)
{
if ($gpmGpo.ModificationTime -ge (get-date).AddDays(-1)) {$gpmGpo.DisplayName}
# Check if the GPO has been modified less than 24 hours from now 
}

"以上" を意味する –ge 演算子に注意してください。他のスクリプト言語やプログラミング言語の < および > 演算子に慣れている場合、この演算子は奇妙に見えるかもしれません。しかし、皆さんが見慣れているそれらの演算子はリダイレクト (ファイルへの出力のリダイレクトなど) に使用されるため、Windows PowerShell で比較演算子として使用することはできません。

まとめ

図 5 には、ある GPO の設定を別の GPO にコピーするためのスクリプトのコードがすべて記載されています。ここまでの説明で、この新しいテクノロジをグループ ポリシーと共に使用し、COM オブジェクトや、COM オブジェクトを使用する VBScript コードを再利用する方法についておわかりいただけたと思います。

Figure 5 ある GPO の設定を別の GPO にコピーする

###########################################################################
# Function  : CopyGpo
# Description: Copies the settings in a GPO to another GPO
# Parameters : $sourceGpo     - The GPO name or GPO ID of the GPO to copy
#           : $sourceDomain   - The dns name, such as microsoft.com, of the domain where the original GPO is located
#           : $targetGpo      - The GPO name of the GPO to add
#           : $targetDomain   - The dns name, such as microsoft.com, of the domain where the copy should be put
#           : $migrationTable - The path to an optional Migration table to use when copying the GPO
# Returns   : N/A
# Dependencies: Uses GetGpoByNameOrID, found in article download
###########################################################################
function CopyGpo(
 [string] $sourceGpo=$(throw ‘$sourceGpo is required’),
 [string] $sourceDomain=$(throw ‘$sourceDomain is required’),
 [string] $targetGpo=$(throw ‘$targetGpo is required’),
 [string] $targetDomain=$(throw ‘$targetDomain is required’),
 [string] $migrationTable=$(“”),
 [switch] $copyAcl)
{
 
 $gpm = New-Object -ComObject GPMgmt.GPM # Create the GPMC Main object
 $gpmConstants = $gpm.GetConstants() # Load the GPMC constants
 $gpmSourceDomain = $gpm.GetDomain($sourceDomain, “”, $gpmConstants.UseAnyDC) # Connect to the domain passed 
                                                                              # using any DC
 $gpmSourceGpo = GetGpoByNameOrID $sourceGpo $gpmSourceDomain
 # Handle situations where no or multiple GPOs was found
 switch ($gpmSourceGpo.Count)
 {
   {$_ -eq 0} {throw ‘No GPO named $gpoName found’; return}
   {$_ -gt 1} {throw ‘More than one GPO named $gpoName found’; return} 
 }
 if ($migrationTable)
 {
   $gpmMigrationTable = $gpm.GetMigrationTable($migrationTable)
 }

 $gpmTargetDomain = $gpm.GetDomain($targetDomain, “”, $gpmConstants.UseAnyDC) # Connect to the domain passed 
                                                                              # using any DC

 $copyFlags = 0
 if ($copyAcl)
 {
   $copyFlags = Constants.ProcessSecurity
 }
 $gpmResult = $gpmSourceGpo.CopyTo($copyFlags, $gpmTargetDomain, $targetGpo)
 [void] $gpmResult.OverallStatus
 
}

Windows PowerShell は、既にグループ ポリシーがそうであるように、Windows 管理環境になくてはならない要素になるでしょう。しかし、移行または維持する必要がある VBScript コードは無数にあります。このチュートリアルがそれらの作業の役に立つことを願っています。

グループ ポリシーの管理や、以前は VBScript を使用していたその他の作業を強化するために使用できるリソースは多数用意されています。たとえば、ダウンロード可能なこの記事の関連コードに含まれている Windows PowerShell 関数や、TechNet Web サイトで提供されている、VBScript から Windows PowerShell への変換に役立つガイドなどがあります。このガイドには、VBScript で行っていた一般的な作業を Windows PowerShell で実行する方法に関するヒントが記載されています。このガイドは、microsoft.com/technet/scriptcenter/topics/winpsh/convert から参照できます。

また、GPMC API はすべてドキュメント化されており、グループ ポリシー サイト (microsoft.com/grouppolicy) からダウンロードできます。

大事なことを言い忘れていましたが、Windows PowerShell がインストールされていなければ、何も始まりません。microsoft.com/powershell からダウンロードしましょう。それでは Windows PowerShell をお楽しみください。

Thorbjörn Sjövoldは、グループ ポリシーをベースにしたシステム管理およびセキュリティ強化製品を提供する Special Operations Software (www.specopssoft.com) の CTO および創立者です。彼の連絡先は thorbjorn.sjovold@specopssoft.com (英語のみ) です。

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; 許可なしに一部または全体を複製することは禁止されています.