かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  vol.247 Hyper-V ホストの棚卸データの差分比較|Hyper-V 健康診断

 

 

vol.247 Hyper-V ホストの棚卸データの差分比較|Hyper-V 健康診断

2026年11月02日配信
執筆者:山内 和朗

 これまでHyper-Vホストの構成情報を1つのCLIXMLファイル(.xml)として保存する棚卸スクリプト「get-hypervhostinventory.ps1」(プレーンテキストで表示/ダウンロード)とそのデータを参照して概要をレポートする「get-hypervhostsummary.ps1」(プレーンテキストで表示/ダウンロード)を紹介し、棚卸データの構造について説明しました。今回は2つのCLIXMLファイル(.xml)の棚卸データ間、およびCLXMLファイル(.xml)と最新値の差分比較のためのスクリプトを作成して紹介します。

 前回説明したように、棚卸スクリプト「get-hypervhostinventory.ps1」でCLIXMLファイル(.xml)に保存した新旧棚卸データは、次のコマンドラインを実行することで、それぞれ別の変数にオブジェクトとして読み込むことができます。前回は、Import-Clixmlで読み込んだオブジェクトの構造を説明し、そのプロパティや保存されたイベントログを参照する方法を説明しましたが、今回は、棚卸データの新旧差分を一括で比較するためのスクリプトを紹介します。

$oldinv = Import-Clixml .\HyperVHost-Inventory-コンピューター名-XXXXXX-XXXXXX.xml
$newinv = Import-Clixml .\HyperVHost-Inventory-コンピューター名-YYYYYY-YYYYYY.xml

 

 PowerShellのコマンドレットには、単一のオブジェクトを返すものと、複数のオブジェクトからなるコレクションを返すものがあります。Hyper-Vホストの棚卸しデータには、その両方が含まれています。例えば、前者には棚卸しデータのComputerInfoプロパティ(Get-ComputerInfoの実行結果)やHyperV.VMHostプロパティ(Get-VMHostの実行結果)があります。一方、後者にはOSInfo.WindowsFeaturesプロパティ(Get-WindowsFeatureの実行結果)やVirtualMachinesプロパティ(Get-VMの実行結果に追加情報を付加したもの)があります

(棚卸データのオブジェクト構造については、前回の記事の一覧表を参照してください)。

 

 このように、棚卸しデータには構造の異なるオブジェクトが混在しているため、データ全体を一括して差分比較することは容易ではありません。少なくとも、単一オブジェクトとオブジェクトのコレクションを分け、それぞれに適した方法で比較する必要があります。

 

単一オブジェクトの差分比較の例

 

 次に示すPowerShellスクリプト「Compare-Properties.ps1」は、比較対象の2つのオブジェクトを渡すことで、単一オブジェクトのプロパティの違いを差分として返します。このスクリプトは、棚卸データのComputerInfo、HyperV.VMHost、HyperV.VMReplicationServer、およびOSInfo(OSInfo.HotFixとOSInfo.WindowsFeaturesは比較対象除外)に使用できます。

 

[Compare-Properties.ps1](プレーンテキストで表示/ダウンロード)

param(
    [Parameter(Mandatory)]
    $oldObject,

    [Parameter(Mandatory)]
    $newObject
)

$result = $oldObject.PSObject.Properties.Name | ForEach-Object {

    $prop = $_

    $oldValue = $oldObject.$prop
    $newValue = $newObject.$prop

    # null または単純値のみ対象
    # IsPrimitive は Boolean、Int32、Int64、Double など
    $isSimple =
        $null -eq $oldValue -or
        $oldValue.GetType().IsPrimitive -or
        $oldValue -is [string] -or
        $oldValue -is [decimal] -or
        $oldValue -is [datetime] -or
        $oldValue -is [timespan] -or
        $oldValue -is [guid] -or
        $oldValue.GetType().IsEnum

    if ($isSimple) {
        $oldText = if ($null -eq $oldValue) {
            $null
        } else {
            $oldValue.ToString()
        }
        $newText = if ($null -eq $newValue) {
            $null
        } else {
            $newValue.ToString()
        }
        if ($oldText -ne $newText) {
            [pscustomobject]@{
                Property = $prop
                OldValue = $oldText
                NewValue = $newText
            }
        }
        return
    }

    # ----------------------------------------
    # Get-VMHost の例外
    # コレクション型プロパティ
    # ----------------------------------------
    switch ($prop) {
        'InternalNetworkAdapters' {
            $oldText = @($oldValue).Count
            $newText = @($newValue).Count
        }
        'ExternalNetworkAdapters' {
            $oldText = @($oldValue).Count
            $newText = @($newValue).Count
        }
        'SupportedVmVersions' {
            $oldText = (
                @($oldValue) |
                ForEach-Object { $_.ToString() } |
                Sort-Object
            ) -join ','
            $newText = (
                @($newValue) |
                ForEach-Object { $_.ToString() } |
                Sort-Object
            ) -join ','
        }
        'SecureBootTemplates' {
            $oldText = (
                @($oldValue) |
                ForEach-Object { $_.Id.ToString() } |
                Sort-Object
            ) -join ','
            $newText = (
                @($newValue) |
                ForEach-Object { $_.Id.ToString() } |
                Sort-Object
            ) -join ','
        }
        'GuestIsolationTypes' {
            $oldText = (
                @($oldValue) |
                ForEach-Object { $_.ToString() } |
                Sort-Object
            ) -join ','
            $newText = (
                @($newValue) |
                ForEach-Object { $_.ToString() } |
                Sort-Object
            ) -join ','
        }
        default {
            # HostNumaStatus / NumaStatus は除外
            return
        }
    }
    if ($oldText -ne $newText) {
        [pscustomobject]@{
            Property = $prop
            OldValue = $oldText
            NewValue = $newText
        }
    }
    # ----------------------------------------
    # Get-VMHost の例外終わり
    # ----------------------------------------
}

$result

 

 単一オブジェクトを比較する場合でも、すべてのプロパティをそのまま比較対象にすることはできません。プロパティの値には、文字列や数値、真偽値などの単純な値だけでなく、配列やコレクション、さらに別のオブジェクトを格納しているものがあります。特に、CLIXMLから読み込んだオブジェクトはデシリアライズされるため、配列やコレクション、複合オブジェクトでは、保存前のオブジェクトと型や内部構造が異なる場合があります。このようなプロパティを単純に比較すると、実際の設定内容は同じでも差分として検出されることがあります。そこでこのスクリプトでは、文字列(string)、数値(decimal、Int32、Int64、Doubleなど)、真偽値(Boolen)、日時(DateTime)、列挙値(Enum)など、1つの値として比較できる単純値のプロパティだけを対象にしています。このスクリプトで漏れてしまう可能性がある設定については、必要に応じて、その構造に適した方法で個別に比較する必要があります。このスクリプトでは、Get-VMHostについて、比較から除外されるコレクションプロパティの例外処理を含めています。

 

 例えば、棚卸データのHyper-V.VMHostプロパティ下に格納されたオブジェクト(Get-VMHostの実行結果)の新旧を比較するには、次のように実行します($oldinvと$newinvは読み込み済みと想定)。

$oldVMHost = $oldinv.HyperV.VMHost
$newVMHost = $newinv.HyperV.VMHost
.\Compare-Properties $oldVMHost $newVMHost

 

 棚卸データの差分は、次のような形式で一覧表示されます(画面1)。

Property                    OldValue  NewValue
--------                    --------  --------
プロパティ名                古い値    新しい値

 

画面1 Compare-Properties.ps1で新旧棚卸データのComputerInfoとHyperV.VMHostの差分を比較

画面1 Compare-Properties.ps1で新旧棚卸データのComputerInfoとHyperV.VMHostの差分を比較

 

 このスクリプトに与えるオブジェクトは、棚卸データでなくても構いません。PowerShellのコマンドレットの実行結果をオブジェクトとして渡して比較することもできます。例えば、次のように実行すると最新の棚卸データ($newinvが最新として)のHyper-V.VMHostプロパティと、ローカルホストで実行したGet-VMHostの実行結果の差分を比較することができます(画面2)。

$newVMHost = $newinv.HyperV.VMHost
.\Compare-Properties $newVMHost (Get-VMHost)

 

画面2 棚卸データと現在のコマンドレット実行結果(Get-VMHost、Get-VMReplicationServer)の差分を比較

画面2 棚卸データと現在のコマンドレット実行結果(Get-VMHost、Get-VMReplicationServer)の差分を比較

 

コレクションの差分比較の例

 

 棚卸データに含まれるコレクションの比較対象として、OSInfo.WindowsFeatures(Get-WindowsFeaturesの実行結果)を対象とした差分比較スクリプト「Compare-WindowsFeatures.ps1」を作成しました。このスクリプトの基本は、Get-WindowsFeatureが返すWindows Serverのサーバーの役割と機能のインストール状態(InstallState)を比較するものです。OSのアップグレード前後の比較や、別のサーバーとの間の比較もできるように、役割や機能の増減にも対応しています。

 

[Compare-WindowsFeatures.ps1](プレーンテキストで表示/ダウンロード)

param(
    [Parameter(Mandatory)]
    $oldFeatures,
    [Parameter(Mandatory)]
    $newFeatures
)

$result = @()

foreach ($newFeature in $newFeatures) {
    $oldFeature = $oldFeatures |
        Where-Object Name -eq $newFeature.Name

    if (-not $oldFeature) {
        $result += [pscustomobject]@{
            ChangeType = 'Added'
            Name       = $newFeature.Name
            DisplayName = $newFeature.DisplayName
            OldValue   = $null
            NewValue   = $newFeature.InstallState
        }
        continue
    }

    if ($oldFeature.InstallState -ne $newFeature.InstallState) {
        $result += [pscustomobject]@{
            ChangeType = 'Changed'
            Name       = $newFeature.Name
            DisplayName = $newFeature.DisplayName
            OldValue   = $oldFeature.InstallState
            NewValue   = $newFeature.InstallState
        }
    }
}

foreach ($oldFeature in $oldFeatures) {
    if ($oldFeature.Name -notin $newFeatures.Name) {
        $result += [pscustomobject]@{
            ChangeType = 'Removed'
            Name       = $oldFeature.Name
            DisplayName = $oldFeature.DisplayName
            OldValue   = $oldFeature.InstallState
            NewValue   = $null
        }
    }
}

$result 

 

 スクリプトの出力結果は、次のような形式で一覧表示されます(画面1)。ChangeTypeは、Changed/Added/Removed(変更/追加/削除、OldValue/NewValueは、Installed/Available/Removed(インストール済み/利用可能/削除済み《ペイロードの削除》)のいずれかになります。

ChangeType Name                    DisplayName                        OldValue   NewValue
---------- ----                    -----------                        --------   --------
変更の種類 役割または機能名        表示名                             旧い値     新しい値

vol247_scr03-1

画面3 棚卸データの新旧には差異はなし。現在の状態(Get-WindowsFeature)や他のサーバーとの差分も比較できる

 

 棚卸データに含まれるさらに複雑で、かつHyper-Vにおいて重要なコレクションは、VirtualMachines(Get-VMの実行結果+追加情報)です。次回は、複数のVMの設定を一括で差分比較するスクリプトを紹介します。

 

サーバー設定仕様書の自動生成サービス、SSD-assistanceのご紹介

 弊社の主力製品の1つ設定仕様書自動生成サービス「SSD-assistance」は、設定仕様書作成のためのデータ収集やドキュメント作成の手間と時間を省き、高品質で標準化された設定仕様書を自動生成するサービスです。SSD-assistanceは、「Hyper-V ホスト 設定仕様書」の作成に対応している上、「差分比較リスト」で構成の変更差分を視覚的に強調表示させることができます。これまで、そして次回以降の記事でやろうとしていることと同等のことは、実はSSD-assistanceを利用すれば誰でも、簡単に、すぐに実現することができます。ただこの連載記事では、SSD-assistanceでは手の届かない、Hyper-Vの深くて、細かいところまで手を伸ばせればと考えています。

Hyper-Vホスト設定仕様書のサンプル

Hyper-Vホスト設定仕様書のサンプル(クリックで拡大)

差分比較リストのサンプル

差分比較リストのサンプル(クリックで拡大)

 

リファレンス(Microsoft Learn):

Get-ComputerInfo(PowerShell Management)|Get-VMHost(Hyper-V)|Get-VM(Hyper-V)|Get-VMReplicationServer(Hyper-V)|Export-Clixml(PowerShell Utility)|Import-Clixml(PowerShell Utility)

 

Hyper-V 小技・裏技集 目次|(1) |(2)|(3)|(4)

blog_yamanxworld_subscribe

blog_yamanxworld_comment

blog_yamanxworld_WP_ws2025

最新記事