PowerShell Function to Determine the Hyper-V Host for a VM

This week, I thought I would share a PowerShell function that I wrote to determine what Hyper-V host server a VM (virtual machine) resides on. In this scenario, you have no idea which Hyper-V host a VM resides on.

#Requires -Version 3.0
function Get-MrVmHost {

<#
.SYNOPSIS
    Determines the HyperV host virtualization server for the specified virtual machines.

.DESCRIPTION
    Get-MrVmHost is an advanced function for determining the HyperV host virtualiztion server for one
    or more VMs (virtual machines).

.PARAMETER ComputerName
    The name of the VM (virtual machine) to determine the HyperV host for.

.PARAMETER Credential
    Specifies a user account that has permission to perform this action. The default is the current user.

    Type a user name, such as User01 or Domain01\User01. Or, enter a PSCredential object, such as one generated by the
    Get-Credential cmdlet. If you type a user name, this cmdlet prompts you for a password.

.EXAMPLE
     Get-MrVmHost -ComputerName Server01, Server02, Server03

.EXAMPLE
     Get-MrVmHost -ComputerName Server01, Server02, Server03 -Credential (Get-Credential)

.INPUTS
    None

.OUTPUTS
    PSCustomObject

.NOTES
    Author:  Mike F Robbins
    Website: http://mikefrobbins.com
    Twitter: @mikefrobbins
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [Alias('VMName')]
        [string[]]$ComputerName,

        [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
    )

    $Params = @{
        ComputerName = $ComputerName
        ScriptBlock = {Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters'}
        ErrorAction = 'SilentlyContinue'
        ErrorVariable = 'Problem'
    }

    if ($PSBoundParameters.Credential) {
        $Params.Credential = $Credential
    }

    Invoke-Command @Params |
    Select-Object -Property VirtualMachineName, HostName

    foreach ($p in $Problem) {
        if ($p.origininfo.pscomputername) {
            Write-Warning -Message "Unable to read registry key on $($p.origininfo.pscomputername)"
        }
        elseif ($p.targetobject) {
            Write-Warning -Message "Unable to connect to $($p.targetobject)"
        }
    }

}

First off, let me say that this function is written a bit unorthodox.

Typically you’ll see functions written where each item in the ComputerName array is iterated through one at a time. Since this function uses the PowerShell remoting Invoke-Command cmdlet, it’s able to perform the task in parallel so why would anyone want to constrain it to only checking one computer at a time? Most commonly, this is done for error handling to determine if a computer is unreachable, if the credentials are invalid, and if the command generates an error such as the registry key not existing.

Get-MrVmHost -ComputerName mr101, doesnotexist, mr102 -Credential $Cred

get-mrvmhost1a.png

I started out first by using the built-in Error variable, clearing it and then iterating through it if any errors occurred. I decided that wasn’t necessarily a good idea since something else could potentially pollute the built-in variable so I decided to specify my own variable by using the ErrorVariable parameter. The function uses the ErrorAction parameter with the SilentlyContinue value. Using that particular option silences the interactive errors but still populates the variable that’s specified with ErrorVariable so I’m able to iterate through the errors and display warnings for systems whose results weren’t returned due to some sort of error.

I thought this solution was unique and I’m interested in knowing what you think about it.

The function shown in this blog article can be downloaded from my PowerShell repository on GitHub.

ยต