かつて山市良と呼ばれたおじさんのブログ
セイテクエンジニアのブログ かつて山市良と呼ばれたおじさんのブログ vol.169 KVPでIPアドレスが取得できなくなったのは仕様|Windows Server 2016 EOSまであと362日
2026年01月15日配信
執筆者:山内 和朗
Windows Server 2016の製品ライフサイクルとサポート終了日(End of LifeCycle《EOL》、End of Support《EOS》)である2027年1月12日までのカウントダウンが進んでいます。前回は、知っていると便利に使え、オンプレミスのHyper-V環境やAzureで重要なデータ交換(KVP)サービスについて詳しく説明しました。KVPの仕組み自体に変更はありませんが、Windows 10およびWindows Server 2016以降では、KVPで非推奨になり取得できなくなったデータがあることを前回指摘しました。
KVPデータの取得方法は、Windows Server 2012でHyper-VのWMI名前空間が「root¥virtualization¥v2」に変更されてから、おそらく代わっていません。しかし、VMのゲストOSがWindows 10/Windows Server 2016以降になってから、それまで取得できていたキー値ペアの一部が取得できなくなりました。
取得できなくなったデータとは、NetworkAddressIPv4やNetworkAddressIPv6などネットワークアドレスのデータです。Windows 10/Windows Server 2016以降のVMに対して前回のスクリプト「get-kvpdata.ps1」を実行すると、該当するキー値ペアの値(データ)に次のように表示されます。
ゲストOSが日本語版の場合:
次の Msvm_KvpExchangeDataItem.Name キーは非推奨です: NetworkAddressIPv4、NetworkAddressIPv6、RDPAddressIPv4、RDPAddressIPv6。Msvm_GuestNetworkAdapterConfiguration を使用してゲスト IP アドレスを構成してください。
ゲストOSが英語版の場合:
The following Msvm_KvpExchangeDataItem.Name keys are deprecated: NetworkAddressIPv4, NetworkAddressIPv6, RDPAddressIPv4, and RDPAddressIPv6. Please configure guest IP addresses using Msvm_GuestNetworkAdapterConfiguration.
つまり、現在サポートされているWindowsバージョン(ESUでカバーされるWindows Server 2012/2012 R2を除く)がゲストOSの場合、KVPを介してIPアドレス情報を取得できません(画面1)。そのため、KVP経由でIPアドレス情報を取得して何かをする管理用スクリプトがある場合(例、ゲストOSのIPアドレスを調べてリモートデスクトップ接続を開始するなど)、新しいWindowsバージョンでは期待通りに動作しなくなってしまいます。

画面1 Windows 10/Server 2016以降のVMでは、IPアドレス情報をKVPから取得できなくなった。Linuxゲストは、レガシWindowsと同様にこれまで通りIPアドレス情報を取得できる
非推奨のメッセージには、代わりにWMIの「Msvm_GuestNetworkAdapterConfiguration」クラスを使用するように説明されています。そこで、「Msvm_GuestNetworkAdapterConfiguration」を使用してネットワーク情報を取得するスクリプト「get-vmnicinfo.ps1」を作成しました(画面2)。KVPで取得できなくなったIPアドレス情報を取得する代替手段として利用できます。
[get-vmnicinfo.ps1](プレーンテキストで表示)
param(
[Parameter(Mandatory)]
[string]$VMName
)
$ns = 'root\virtualization\v2'
$vm = Get-VM -Name $vmname
$vmid = $vm.vmId
Get-CimInstance -Namespace $ns -ClassName Msvm_GuestNetworkAdapterConfiguration |
Where { $_.InstanceID -like "*$vmid*" } |
Select InstanceID,
ProtocolIFType,
@{Name='ProtocolIFTypeName';Expression={
switch ($_.ProtocolIFType) {
4096 { 'IPv4 only' ; break }
4097 { 'IPv6 only' ; break }
4098 { 'IPv4/IPv6' ; break }
default { "Unknown ($($_.ProtocolIFType))" }
}
}},
DHCPEnabled,
IPAddresses,
Subnets,
DefaultGateways,
DNSServers
例えば、次のように実行すると、Hyper-Vホスト上で現在実行中のVMのIPアドレスを一覧表示できます。
| $vms = Get-VM|Where {$_.State -eq "Running"} foreach ($vm in $vms) { $ip = (.\get-vmnicinfo.ps1 -VMName $vm.Name).IPAddresses; Write-Host $vm.Name`t$ip } |

画面2 WMIの「Msvm_GuestNetworkAdapterConfiguration」クラスを使用して、Hyper-VホストからVMのネットワーク情報を取得する
「get-vmnicinfo.ps1」は、InstanceIDに2つのGUID(VMのvmIDとNICのadapterID)を含むため、人に優しくないかもしれません。そこで、もう1つの代替手段として、Get-VMNetworkAdapterを使用してIPアドレス情報を取得するようにした「get-kvpdata2.ps1」を作成しました。このスクリプトは、Get-VMNetworkAdapterを使用してVMのネットワークアダプター(1つ以上)を取得し、KVPデータのキー値ペアに追加する形でNetworkAdapter#(#は0、1、2...)という値の名前に、ネットワーク情報を1つにまとめたカスタムオブジェクトを値のデータにして出力します(画面3)。
[get-kvpdata2.ps1](プレーンテキストで表示)

画面3 KVPデータ取得スクリプトのキー値ペアに追加する形で、Get-VMNetworkAdapterで取得したネットワーク情報を出力
KVPの通信チャネルは静的IPインジェクションによる、Hyper-VホストからVMゲストのIPアドレスの書き換えにも利用されていると言いました。それをデモできる簡単なスクリプト「set-vmipfromhost.ps1」を作成してみました。このスクリプトはコードは複雑ですが、ネットワークアダプターが1つしか無いVMを前提とした簡単なものです。入力間違いなどには対応していないので注意してください。設定が変更されたかどうか確認するために、最後に「get-vmnicinfo.ps1」を実行しています(画面4、画面5)。

画面4 Hyper-VホストからVMのゲストOSに静的IPアドレスを設定する

画面5 Hyper-VホストからVMのゲストOSのIPアドレスをDHCP割り当てに変更する
「set-vmipfromhost.ps1」のコードは他の人が書いたコードを流用しているので説明することはできません(実はよくわかっていません)。詳しくは、私の旧ブログの以下のポストのリンク先をご覧ください。
Hyper-V ホストから仮想マシンの IP を設定する (Static IP Injection 利用で) |山市良のえぬなんとかわーるど(アーカイブ)
[set-vmipfromhost.ps1](プレーンテキストで表示)
param(
[Parameter(Mandatory)]
[string]$VMName
)
#$vmName = Read-Host "Specify the name of your virtual machine"
$ns = "root\virtualization\v2"
$vsvms = Get-WmiObject -Namespace $ns -Class Msvm_VirtualSystemManagementService
$vmcs = Get-WmiObject -Namespace $ns -Class Msvm_ComputerSystem -Filter "ElementName='$vmName'"
if (-not $vmcs) {
Write-Host "Error: VM $vmname is missing."
exit 1;
}
$vs_settingdata = ($vmcs.GetRelated("Msvm_VirtualSystemSettingData", "Msvm_SettingsDefineState", $null, $null, "SettingData", "ManagedElement", $false, $null) | %{$_})
$et_settingdata = $vs_settingdata.GetRelated("Msvm_SyntheticEthernetPortSettingData")
$vm_netconf = ($et_settingdata.GetRelated("Msvm_GuestNetworkAdapterConfiguration", "Msvm_SettingDataComponent", $null, $null, "PartComponent", "GroupComponent", $false, $null) | % {$_})
$yesno = Read-Host "Enable DHCP? (y/n[n])"
if ($yesno.toLower() -eq "y") {
$vm_netconf.DHCPEnabled = $true
} else {
$vm_netconf.DHCPEnabled = $false
$vm_netconf.IPAddresses = @((Read-Host "Specify static ipv4 address"))
$vm_netconf.Subnets = @((Read-Host "Specify subnetmask"))
$vm_netconf.DefaultGateways = @((Read-Host "Specify default gateway"))
$vm_netconf.DNSServers = @((Read-Host "Specify preferred dns server"))
}
$ret = $vsvms.SetGuestNetworkAdapterConfiguration($vmcs.Path, $vm_netconf.GetText(1))
if ($ret.ReturnValue -ne 0) {
Write-Host "Error: something went wrong"
}
.\get-vmnicinfo.ps1 $vmName
KVP関連でもう1つデモしてみましょう。これも旧ブログのアイデアを持ってきたものです。
Hyper-V Scripting: “実行中”の仮想マシン=ゲスト OS が“実行中”ではない |山市良のえぬなんとかわーるど(アーカイブ)
Get-VMのStateプロパティでVMの実行状態を取得できますが(例、if ((Get-VM -Name $vmName).State -eq "Running") { ・・・ })、それはVMの状態であり、必ずしもVMのゲストOSが実行中であるとは限りません。例えば、ゲストOSをインストールしていなくても、VMを起動すれば実行中(Running)になります。ゲストOSまで完全に起動しているかどうかを確認するには、KVPデータを取得できるかどうかで判断できます。KVPデータを取得できるということは、ゲストOSの起動が完了しており、データ交換サービス(Windowsの場合はwmikvpexchange、Linuxの場合はhv_kvp_daemon)が実行中になっているからです。
次の「startwait-vm.ps1」は、VMを開始して、ゲストOSが起動するまで待機するデモ用スクリプトです。このスクリプトは、稼働中のVMのゲストOSの実行状態のチェックにも使用できます(画面6)。このスクリプトでは「get-kvpdata.ps1」の実行によってゲストOSの状態を判断しています。KVPはVMが起動した直後からいくつかの値を返すので、ゲストOSの具体的な情報(OSName)が返ってくるまで2秒ごとに実行を繰り返しています。最初は if((get-vmintegration -VMname $vmname)[0].PrimaryOperationalStatus -eq "Ok") { ・・・ } で判断使用と思いましたが、VMが起動して直後に「Ok」が返ってくるためゲストOSの起動の判断には使用できませんでした。

画面6 VMを開始し(開始されていなければ)、VMのゲストOSが実行中になるまで待機するスクリプト
[startandwait-vm.ps1](プレーンテキストで表示)
param(
[Parameter(Mandatory)]
[string]$VMName
)
$timeout = 60
$state = (Get-VM -Name $vmName).State
if ($state -ne "Running") {
Start-VM -Name $vmName | Out-Null
Write-Host "Starting VM '$vmName'..."
do {
Start-Sleep -Seconds 2
$timeout = $timeout - 2
$state = (Get-VM -Name $vmName).State
$ret =
Write-Host " Current state: $state"
} while (($state -ne 'Running') -and ($timeout -gt 0))
if ($state -eq 'running') {
Write-Host "VM '$vmName' is now Running"
} else {
Write-Host "Error: the timeout has expired."
}
} else {
Write-Host "VM '$vmName' is already Running"
}
$timeout = 60
$state = "Checking the guest's status"
$osname = ""
Write-Host "Waiting Guest OS startup '$vmName'..."
do {
Start-Sleep -Seconds 2
$timeout = $timeout - 2
#if ((get-vmintegrationService -VMname $vmname)[0].PrimaryOperationalStatus -eq "Ok") {
# $state = "Running"
#}
$items = .\get-kvpdata.ps1 -VMname $vmName
if ($items) {
foreach ($item in $items) {
if ($item.Name -eq "OSName" ) {
$osname = $item.Data
if ($osname -ne "") {
$state = "Running"
}
}
}
}
Write-Host " Current state: $state"
} while (($state -ne 'Running') -and ($timeout -gt 0))
if ($state -eq 'running') {
Write-Host "VM '$vmName' Guest OS '$osname' is Running"
} else {
Write-Host "Error: the timeout has expired."
}