Hyper-V provider fails under PowerShell 7 due to duplicate `VirtualMachine` CLR types
Debug output
Failed to configure CPUs: Cannot process argument transformation on parameter 'VM'.
Cannot convert the "VirtualMachine (...)" value of type
"Microsoft.HyperV.PowerShell.VirtualMachine" to type
"Microsoft.HyperV.PowerShell.VirtualMachine".
The debug log confirms Vagrant selected PowerShell 7.6.5:
preferred powershell executable name: pwsh
using preferred powershell "pwsh" - ...\pwsh.EXE
Expected behavior
The Hyper-V provider should configure and start the VM when Vagrant selects a supported PowerShell 7 installation.
Actual behavior
Vagrant selects pwsh by default, imports the VM, then fails in configure_vm.ps1 while calling Set-VagrantVMCPUS.
PowerShell 7 resolves Vagrant’s typed $VM parameter and the object returned by Hyper-V\Get-VM from different assemblies:
Get-VM runtime object:
Microsoft.HyperV.PowerShell.VirtualMachine
Microsoft.HyperV.PowerShell.Objects.dll
Set-VagrantVMCPUS parameter:
Microsoft.HyperV.PowerShell.VirtualMachine
Microsoft.HyperV.PowerShell.dll
These are different CLR types despite having identical full names. Windows native Hyper-V cmdlets use Microsoft.HyperV.PowerShell.Objects.dll; the conflicting conversion is introduced by the wrapper annotation in vagrant.
Setting the following makes the same vagrant up succeed:
$env:VAGRANT_PREFERRED_POWERSHELL = "powershell"
Reproduction information
Vagrant version
Vagrant 2.4.9
Host operating system
Microsoft Windows 11 Enterprise
Version 10.0.22631
64-bit
PowerShell Core 7.6.5
Windows PowerShell 5.1
Hyper-V enabled
Guest operating system
Red Hat Enterprise Linux 9.2
This is a host-side VM configuration failure, before guest boot so it's not guest-specific and will also occur if the guest was Windows.
Steps to reproduce
- Install PowerShell 7 alongside Windows PowerShell and enable Hyper-V.
- Ensure
VAGRANT_PREFERRED_POWERSHELLis unset. - Use the minimal Vagrantfile below.
- Run:
Remove-Item Env:\VAGRANT_PREFERRED_POWERSHELL -ErrorAction SilentlyContinue
vagrant destroy -f
vagrant up --provider hyperv --debug
- Observe the error
Failed to configure CPUs: Cannot process argument transformation on parameter 'VM'.
Cannot convert the "VirtualMachine (...)" value of type
"Microsoft.HyperV.PowerShell.VirtualMachine" to type
"Microsoft.HyperV.PowerShell.VirtualMachine".
- Repeat using Windows PowerShell:
$env:VAGRANT_PREFERRED_POWERSHELL = "powershell"
vagrant destroy -f
vagrant up --provider hyperv
- Observe that VM configuration succeeds.
Manual Reproduction
We can observe the same failure manually
Import-Module Hyper-V -Force
$module = "<VAGRANT_INSTALL>\embedded\gems\gems\vagrant-2.4.9\" +
"plugins\providers\hyperv\scripts\\\utils\VagrantVM\VagrantVM.psm1"
Import-Module $module -Force
$vm = Hyper-V\Get-VM | Select-Object -First 1
$vm.GetType().AssemblyQualifiedName
(Get-Command Set-VagrantVMCPUS).
Parameters["VM"].ParameterType.AssemblyQualifiedName
# CPUCount is omitted; this exercises binding without modifying the VM.
Set-VagrantVMCPUS -VM $vm
Under pwsh.exe this fails with the duplicate-type conversion error. Under powershell.exe it succeeds.
Vagrantfile
Vagrant.configure("2") do |config|
config.vm.box = "generic/rocky9"
config.vm.provider "hyperv" do |hyperv|
hyperv.cpus = 1
end
end
Root cause
VagrantVM.psm1 declares $VM as:
[Microsoft.HyperV.PowerShell.VirtualMachine] $VM
Under PowerShell 7, that annotation resolves from Microsoft.HyperV.PowerShell.dll, while Hyper-V\Get-VM returns the type from Microsoft.HyperV.PowerShell.Objects.dll.
The annotation appears in:
Set-VagrantVMMemory
Set-VagrantVMCPUS
Set-VagrantVMVirtExtensions
Set-VagrantVMAutoActions
Set-VagrantVMService
Set-VagrantVMSwitch
Vagrant prefers pwsh over powershell and only checks executable availability and min/major version, this incompatibility becomes forced.
Workaround
Force vagrant to use powershell (PowerShell 5.1) and not pwsh (PowerShell 7)
$env:VAGRANT_PREFERRED_POWERSHELL = "powershell"
Solution
Fix the Hyper-V type in all six wrapper functions.
We can use PSType which checks that the object passed is the expected type name but doesn't attempt a CLR conversion (which triggers the wrong assembly selection).
ex. in plugins\providers\hyperv\scripts\\\utils\VagrantVM\VagrantVM.psm1, modify Set-VagrantVMCPUS
function Set-VagrantVMCPUS {
param (
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[PSTypeName("Microsoft.HyperV.PowerShell.VirtualMachine")]
$VM,
[Parameter(Mandatory = $false)]
[int] $CPUCount
)
if ($CPUCount) {
Hyper-V\Set-VM -VM $VM -ProcessorCount $CPUCount
}
return $VM
}
and do it for the rest
Set-VagrantVMMemory
Set-VagrantVMCPUS
Set-VagrantVMVirtExtensions
Set-VagrantVMAutoActions
Set-VagrantVMService
Set-VagrantVMSwitch
Or just stop passing objects and use the VM ID instead, but that's a bigger change
function Set-VagrantVMCPUS {
param (
[Parameter(Mandatory = $true)]
[Guid] $VMID,
[int] $CPUCount
)
$vm = Hyper-V\Get-VM -Id $VMID -ErrorAction Stop
Hyper-V\Set-VM -VM $vm -ProcessorCount $CPUCount
}
Source: hashicorp/vagrant