「Hyper-V小技・裏技集」シリーズでは、Hyper-Vの仮想環境を利用していて、知っていると便利な小技や裏技を紹介します。vol.237では、PowerShell DirectとCopy-VMFileの使いこなしについて説明しました。これらを活用した、VMのゲスト側タスクの自動化例を紹介しています。今回は、Hyper-Vが昔から備えている「静的IPインジェクション(Static IP Injection)」機能について。
静的IPインジェクション(Static IP Injection)とは
このブログの以下の記事では、Hyper-Vの統合サービスの1つである「データ交換(Data Exchange)」サービスについて紹介しました。この機能は、キー値ペア(Key Value Pair《KVP》)とも呼ばれ、VMのゲストとホストに関するキー値ペアを自動生成し、相互に相手の情報(ホストからゲスト、ゲストからホスト)を取得できるようにするサービスです。以前はゲストのIPアドレスも取得できましたが、Windowsゲストに関しては、その機能は廃止されたことも指摘しました。記事で紹介した 「get-kvpdata.ps1」 (プレーンテキストで表示/ダウンロード)は、指定したVMからKVPデータを取得するスクリプト、「get-vmnicinfo.ps1 」 (プレーンテキストで表示/ダウンロード)は、WindowsゲストのKVPで廃止されたIPアドレスの情報を取得するスクリプト(Linux ゲストに対しても機能します)です(画面1)。
vol.168 知ってる? 使ってる? Hyper-VのKVP|Windows Server 2016 EOSまであと364日
vol.169 KVPでIPアドレスが取得できなくなったのは仕様|Windows Server 2016 EOSまであと362日

画面1 KVPを使用してホストからVMのゲストの情報を取得する
参考:
データ交換: キーと値のペアを使用して、Hyper-V のホストとゲストの間で情報を共有する|Windows Server(Microsoft Learn)
KVPにはもう1つ重要な機能があります。それは、「静的IPインジェクション(Static IP Injection)」と呼ばれる、ホストからVMのゲストのIPアドレスを静的に設定する手段です。この技術は、Hyper-VレプリカのフェールオーバーTCP/IPに利用されており、フェールオーバー TCP/IP 用には Set-VMNetworkAdapterFailoverConfiguration コマンドレットが用意されています。しかし、Static IP Injection を直接的に扱う方法を説明する公式ドキュメントはありません。その方法は、この機能が登場した当時、公式ブログやコミュニティで説明されていました。
参考:
Hyper-V レプリカを使用して仮想マシンをレプリケートする > フェールオーバー用に静的 IP インジェクションを構成する (省略可能)|Windows Server(Microsoft Learn)
Hyper-V ホストから仮想マシンの IP を設定する (Static IP Injection 利用で)|山市良のえぬなんとかわーるど(アーカイブ)(旧ブログ)
当時、ブログやコミュニティで公開されていたスクリプトに少し修正を加えて set-vmipfromhost.ps1 としてここで公開します(画面2)。修正した部分は、サブネットマスクは255.255.255.0のように指定することも、プレフィックス長(24)のように指定することもできるように ConvertTo-SubnetMask Function を追加したところです。このスクリプトは、WindowsゲストとLinuxゲストの両方に対して使用できます。ただし、Linuxゲストでは、次の制限があります。
- Enable DHCP? (y/n[n]): y は Linux VM ではエラーになります。
[set-vmipfromhost.ps1](プレーンテキストでダウンロード)
# https://www.say-tech.co.jp/contents/blog/yamanxworld/2026vol169
# Add ConvertTo-Subnet function
param(
[Parameter(Mandatory)]
[string]$VMName
)
function ConvertTo-SubnetMask {
param(
[string]$PrefixLength
)
if ($PrefixLength -notmatch '^\d+$' -or
[int]$PrefixLength -lt 0 -or
[int]$PrefixLength -gt 32) {
return $PrefixLength
}
$mask = [uint32]::MaxValue -shl (32 - [int]$PrefixLength)
$bytes = [BitConverter]::GetBytes($mask)
[Array]::Reverse($bytes)
return ($bytes -join '.')
}
$vm = Get-VM -Name $vmname
if($vm.State -ne "Running") {
Write-Host "VM $($VMName) is not Running."
exit
}
#$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
$vm_netconf.DNSServers = @()
} else {
$vm_netconf.DHCPEnabled = $false
$vm_netconf.IPAddresses = (Read-Host "Specify static ipv4 address")
$vm_netconf.Subnets = (Read-Host "Specify subnetmask")
$vm_netconf.Subnets = ConvertTo-SubnetMask $vm_netconf.Subnets
$vm_netconf.DefaultGateways = (Read-Host "Specify default gateway")
$vm_netconf.DNSServers = @((Read-Host "Specify preferred dns server")) -split '\s*,\s*'
}
$ret = $vsvms.SetGuestNetworkAdapterConfiguration($vmcs.Path, $vm_netconf.GetText(1))
if ($ret.ReturnValue -ne 0) {
Write-Host "Error: something went wrong"
}
Get-CimInstance -Namespace $ns -ClassName Msvm_GuestNetworkAdapterConfiguration |
Where { $_.InstanceID -like "*$($vm.vmId)*" }|Select DHCPEnabled,IPAddresses,Subnets,DefaultGateways,DNSServers

画面2 Static IP Injectionを使用したホストからVMの静的IPアドレスの設定
このスクリプトはレガシな実装であるため、現在は、Windows PowerShell 5.1(powershell.exe)でなければ機能しません。PowerShell 7(pwsh.exe)で実行するとエラーになります。このスクリプトで使用しているGet-WMIObjectを含むWMIコマンドレットは削除されており、PowerShellから使用するとWindows PowerShell互換モードで呼び出され、その結果、スクリプトが期待通りに動作しないことがあるのです(例: System.Management.ManagementObject ではなく、デシリアライズ化された 《オブジェクトのコピーのようなもの》が返ってくるため、GetRelated()など各種メソッドが使用できないなど)。
そこで、WIndows PowerShell5.1とPowerShell 7の両方で動くようした(生成AIの支援を借りて) set-vmipfromhostv2.ps1 を作成しました。このスクリプトは、Windowsゲストに対してはうまく機能しますが、Linuxゲストに対しては以下の制限があります。
[set-vmipfromhostv2.ps1](プレーンテキストでダウンロード)
# Rewrited for PowerShell 7 with the help of ChatGPH
param(
[Parameter(Mandatory)]
[string]$VMName
)
function ConvertTo-SubnetMask {
param(
[string]$PrefixLength
)
if ($PrefixLength -notmatch '^\d+$' -or
[int]$PrefixLength -lt 0 -or
[int]$PrefixLength -gt 32) {
return $PrefixLength
}
$mask = [uint32]::MaxValue -shl (32 - [int]$PrefixLength)
$bytes = [BitConverter]::GetBytes($mask)
[Array]::Reverse($bytes)
return ($bytes -join '.')
}
$ns = "root\virtualization\v2"
# Virtual System Management Service
$vsvms = Get-CimInstance `
-Namespace $ns `
-ClassName Msvm_VirtualSystemManagementService
# VM
$vmcs = Get-CimInstance `
-Namespace $ns `
-ClassName Msvm_ComputerSystem `
-Filter "ElementName='$VMName'"
if (-not $vmcs) {
Write-Host "Error: VM $VMName is missing."
exit 1
}
# Msvm_ComputerSystem
# -> Msvm_VirtualSystemSettingData
$vs_settingdata = Get-CimAssociatedInstance `
-InputObject $vmcs `
-Association Msvm_SettingsDefineState `
-ResultClassName Msvm_VirtualSystemSettingData
# Msvm_VirtualSystemSettingData
# -> Msvm_SyntheticEthernetPortSettingData
$et_settingdata = Get-CimAssociatedInstance `
-InputObject $vs_settingdata `
-ResultClassName Msvm_SyntheticEthernetPortSettingData
# Msvm_SyntheticEthernetPortSettingData
# -> Msvm_GuestNetworkAdapterConfiguration
$vm_netconf = Get-CimAssociatedInstance `
-InputObject $et_settingdata `
-Association Msvm_SettingDataComponent `
-ResultClassName Msvm_GuestNetworkAdapterConfiguration
$yesno = Read-Host "Enable DHCP? (y/n[n])"
if ($yesno.ToLower() -eq "y") {
$vm_netconf.PSBase.CimInstanceProperties["DHCPEnabled"].Value = $true
}
else {
$vm_netconf.PSBase.CimInstanceProperties["DHCPEnabled"].Value = $false
$vm_netconf.PSBase.CimInstanceProperties["IPAddresses"].Value =
@((Read-Host "Specify static ipv4 address"))
$vm_netconf.PSBase.CimInstanceProperties["Subnets"].Value =
@(ConvertTo-SubnetMask((Read-Host "Specify subnetmask")))
$vm_netconf.PSBase.CimInstanceProperties["DefaultGateways"].Value =
@((Read-Host "Specify default gateway"))
$vm_netconf.PSBase.CimInstanceProperties["DNSServers"].Value =
@((Read-Host "Specify preferred dns server"))
}
# CIMインスタンスを embedded instance 文字列に変換
$cimSerializer =
[Microsoft.Management.Infrastructure.Serialization.CimSerializer]::Create()
$serialized =
$cimSerializer.Serialize(
$vm_netconf,
[Microsoft.Management.Infrastructure.Serialization.InstanceSerializationOptions]::None
)
$networkConfiguration =
[System.Text.Encoding]::Unicode.GetString($serialized)
# 設定をVMへ適用
$ret = Invoke-CimMethod `
-InputObject $vsvms `
-MethodName SetGuestNetworkAdapterConfiguration `
-Arguments @{
ComputerSystem = $vmcs
NetworkConfiguration = @($networkConfiguration)
}
if ($ret.ReturnValue -ne 0) {
Write-Host "Error: something went wrong. ReturnValue=$($ret.ReturnValue)"
}
else {
Write-Host "Success"
}

画面2 Windows PowerShell 5.1とPowerShell 7の両方に対応したSet-vmipfromhostv2.ps1。正しく機能しない場合もあるので注意
なお、Static IP Injectionが機能するためには、対象のVMで統合サービスの「データ交換」が有効になっている必要があります(既定)。Linuxゲストの場合は、さらに対応するディストリビューションおよびバージョンであることと、hv-kvp-daemon(linux-cloud-tools-*)
またはhypervkvpd(hyperv-daemons)の導入が必要です。ただし、Static IP Injectionに対応しているディストリビューションやバージョンでも、必ず動作するというわけではありません。今回はAlmalinux 9.xで確認しましたが、Almalinux 10.xやUbuntuの最近のバージョンでは動作を確認できませんでした。
参考:
Windows Server と Windows 上の Hyper-v がサポートされてる Linux および FreeBSD 仮想マシン|Windows Server(Microsoft Learn)
Hyper-V 小技・裏技集 (1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9)|(10)|(11)