locked
Powershell type conversion problems RRS feed

  • Question

  • I've a very annoying issues, when using types in Powershell. I'm automating the process to create virtual machines in Hyper V. I want to use the correct type in and out of functions to have validation on parameters. When I create a VM, i normally get an object of type [Microsoft.HyperV.PowerShell.VirtualMachine]. Sometimes though, I get an array of what looks like [PSCustomObject], with the correct attributes. When I try to convert this to [Microsoft.HyperV.PowerShell.VirtualMachine], I get the following error:

    Cannot convert the "VirtualMachine (Name = 'VM0002') [Id = 'f58a6dc0-fd21-44f3-a7de-01998283f69e']" value of type "Microsoft.HyperV.PowerShell.VirtualMachine" to type "Microsoft.HyperV.PowerShell.VirtualMachine".
    At HypervisorManager.psm1:330 char:9
    +         [Microsoft.HyperV.PowerShell.VirtualMachine]$vm = Get-VM -Nam ...
    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
        + FullyQualifiedErrorId : RuntimeException

    When i run the following, I get a $false: (Get-VM VM0002) -is [Microsoft.HyperV.PowerShell.VirtualMachine]

    When I run the following, it looks, like the type is korrekt: $vm = Get-VM VM0002; $vm.gettype()

    What am I'm not understanding?

    Additional Info:
    Tested this on Server 2016 with Hyper V.

    The same commands return the correct type, as expected, on Windows 10 Hyper V.
    Monday, September 17, 2018 9:17 AM

All replies

  • What you are not understanding is PowerSHell.  Your example: "$vm = Get-VM VM0002; $vm.gettype()" is bogus as it is not a command.  It is two lines.

    $vm = Get-VM VM0002
    $vm.gettype()

    When you ask for an exact object by name you get one object.  When you ask an arbitrary match you may get a collection.

    When you probe a type then you need to use the type name.

    The following is not necessary in PowerShell:

    [Microsoft.HyperV.PowerShell.VirtualMachine]$vm = Get-VM -Name VM00*

    Just let PowerShell build the objects.  It is smart and it is a scripting environment and not a compiled language.

    $vm = Get-VM -Name VM0002

    If you want to return a typed array then you must specify that or you will get a PsCustomObject array.
    [Microsoft.HyperV.PowerShell.VirtualMachine[]]$vm = Get-VM -Name VM00*

    Note we have specified that th result object is an array of VM types.


    \_(ツ)_/

    Monday, September 17, 2018 10:31 AM
    Moderator
  • Thanks for your reply.

    The first command Works, because there is a semicolon in between. Just to make it fit in 1 line :)
    I just specified the type, because I got an error on type conversion. I can run the exact same commands on my Windows 10, without getting the type conversion error...

    If I use the type [Microsoft.HyperV.PowerShell.VirtualMachine[]] as parameter for my function, it will not accept a single object of type [Microsoft.HyperV.PowerShell.VirtualMachine] ...

    When I run the exact commands on my Windows laptop, the Work fine...They have the same version of the Hyper-V PS module but 2 different PSVersion:

    PSVersion                      5.1.14393.0
    PSVersion 5.1.17134.228 (Windows 10)

    I'll try to run Windows update on Server 2016, to see, if it helps.

    Monday, September 17, 2018 11:51 AM
  • It woks fine on my system and is a common PS construct. 

    The code posted works fine on all systems I have tested on.  2012,W10,2016.

    Something else in your code is causing an issue.

    This will not work in PowerShell Core (PS6).


    \_(ツ)_/

    Monday, September 17, 2018 7:51 PM
    Moderator
  • Hi, I'm now closer to the source of the problem. There are apparently 2 different Hyperv Powershell modules:

    1)

    PS C:\Windows\system32>  [Microsoft.HyperV.PowerShell.VirtualMachine].Assembly | fl


    CodeBase               : file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Microsoft.HyperV.PowerShell/v4.0_10.0.0.0__
                             31bf3856ad364e35/Microsoft.HyperV.PowerShell.dll

    2)

    PS C:\Windows\system32> [Microsoft.HyperV.PowerShell.VirtualMachine].Assembly | fl


    CodeBase               : file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Microsoft.HyperV.PowerShell.Objects/v4.0_10.0.0.0_
                             _31bf3856ad364e35/Microsoft.HyperV.PowerShell.Objects.dll

    When 2 is the loaded one, it Works, when it's 1, the types don't match.

    I've yet to find out, how to get around this.

    Import-Module Hyper-V is not consequently loading the same dll...



    Wednesday, September 19, 2018 11:52 AM
  • The Import-Module cmdlet has a -RequiredVersion parameter (and a -MinimumVersion, and -MaximumVersion). I'd try using the -MinimumVersion first.


    --- Rich Matheisen MCSE&I, Exchange Ex-MVP (16 years)

    Wednesday, September 19, 2018 6:24 PM
  • Sorry for the delay. Was travelling :)

    There are actually some interesting observations:

    PS D:\Automation\Templates> Get-Module Hyper-V -ListAvailable | fl


    Name              : Hyper-V
    Path              : C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Hyper-V\2.0.0.0\Hyper-V.psd1
    Description       :
    ModuleType        : Binary
    Version           : 2.0.0.0
    NestedModules     : {}
    ExportedFunctions :
    ExportedCmdlets   : {Add-VMAssignableDevice, Add-VMDvdDrive, Add-VMFibreChannelHba, Add-VMGpuPartitionAdapter...}
    ExportedVariables :
    ExportedAliases   : {gvm, savm, spvm, gvmr...}

    Name              : Hyper-V
    Path              : C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Hyper-V\1.1\Hyper-V.psd1
    Description       :
    ModuleType        : Binary
    Version           : 1.1
    NestedModules     : {}
    ExportedFunctions :
    ExportedCmdlets   : {Add-VMDvdDrive, Add-VMFibreChannelHba, Add-VMHardDiskDrive, Add-VMMigrationNetwork...}
    ExportedVariables :
    ExportedAliases   : {gvm, savm, spvm, gvmr...}

    There are actually 2 different modules, but this doesn't look like it's an issue. Even though, I use Import-Module with a minimum version, it's the same result.

    Actually the two assemblies, which have different name, of which one Works, have the same version number: Microsoft.HyperV.PowerShell, Version=10.0.0.0

    Any ideas?

    Wednesday, September 26, 2018 8:26 AM
  • Nice sleuthing! This has been a long standing issue for me as well. Your post gave me an idea, and it looks like it works: just make sure you load the Microsoft.HyperV.PowerShell.Objects assembly first (before you import the Hyper-V module).

    You can do this by running (e.g. in your PowerShell profile)

    [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.HyperV.PowerShell.Objects')

    In my case, I use a separate module to wrap a set of Hyper-V functions and just setting the RequiredAssemblies entry in the .psd1 file also does the trick:

    RequiredAssemblies = @('Microsoft.HyperV.PowerShell.Objects')

    Now I can finally use proper [Microsoft.HyperV.PowerShell.VirtualMachine] parameter validation.

    Thanks!

    Thursday, December 20, 2018 10:47 PM
  • @ Arnoud Jansveld: thanks!!! I spent a day to eventually reach the same conclusion and https://info.sapien.com/index.php/scripting/scripting-classes/implementingassembly-find-powershell-classes-in-modules helped me to see where the issue was (same type into 2 different assemblies). 

    My problematic code is inside a powershell class in a .psm1 module and I still have to understand why this happens.

    Thursday, May 16, 2019 5:20 PM