13 October 2013

Get VM by IP or MAC address via PowerCLI

I had need to get VMs based on IP, but, not particular IPs, just IPs in a certain subnet. Previously here on vNugglets we had a post about how to Find VM by NIC MAC Address. I have since added this logic to a function, and added the ability to get a VM by its IP address. With my recent need, I added another piece that allows for the IP address "wildcarding". The function, Get-VMByAddress, with its three ways to find a VM by IP- or MAC address:
function Get-VMByAddress {
    <#  .Description
        Find all VMs w/ a NIC w/ the given MAC address or IP address (by IP address relies on info returned from VMware Tools in the guest, so those must be installed).  Includes FindByIPWildcard, so that one can find VMs that approximate IP, like "10.0.0.*"
        .Example
        Get-VMByAddress -MAC 00:50:56:00:00:02
        VMName        MacAddress
        ------        ----------
        dev0-server   00:50:56:00:00:02,00:50:56:00:00:04

        Get VMs with given MAC address, return VM name and its MAC addresses
        .Example
        Get-VMByAddress -IP 10.37.31.120
        VMName         IPAddr
        ------         ------
        dev0-server2   192.168.133.1,192.168.253.1,10.37.31.120,fe80::...

        Get VMs with given IP as reported by Tools, return VM name and its IP addresses
        .Example
        Get-VMByAddress -AddressWildcard 10.0.0.*
        VMName   IPAddr
        ------   ------
        someVM0  10.0.0.119,fe80::...
        someVM2  10.0.0.138,fe80::...
        ...

        Get VMs matching the given wildcarded IP address
    #>

    [CmdletBinding(DefaultParametersetName="FindByMac")]
    param (
        ## MAC address in question, if finding VM by MAC; expects address in format "00:50:56:83:00:69"
        [parameter(Mandatory=$true,ParameterSetName="FindByMac")][string]$MacToFind_str,
        ## IP address in question, if finding VM by IP
        [parameter(Mandatory=$true,ParameterSetName="FindByIP")][ValidateScript({[bool][System.Net.IPAddress]::Parse($_)})][string]$IpToFind_str,
        ## wildcard string IP address (standard wildcards like "10.0.0.*"), if finding VM by approximate IP
        [parameter(Mandatory=$true,ParameterSetName="FindByIPWildcard")][string]$AddressWildcard_str
    ) ## end param


    Process {
        Switch ($PsCmdlet.ParameterSetName) {
            "FindByMac" {
                ## return the some info for the VM(s) with the NIC w/ the given MAC
                Get-View -Viewtype VirtualMachine -Property Name, Config.Hardware.Device | Where-Object {$_.Config.Hardware.Device | Where-Object {($_ -is [VMware.Vim.VirtualEthernetCard]) -and ($_.MacAddress -eq $MacToFind_str)}} | select @{n="VMName"; e={$_.Name}},@{n="MacAddress"; e={($_.Config.Hardware.Device | Where-Object {$_ -is [VMware.Vim.VirtualEthernetCard]} | %{$_.MacAddress} | sort) -join ","}}
                break;
                } ## end case
            {"FindByIp","FindByIPWildcard" -contains $_} {
                ## scriptblock to use for the Where clause in finding VMs
                $sblkFindByIP_WhereStatement = if ($PsCmdlet.ParameterSetName -eq "FindByIPWildcard") {{$_.IpAddress | Where-Object {$_ -like $AddressWildcard_str}}} else {{$_.IpAddress -contains $IpToFind_str}}
                ## return the .Net View object(s) for the VM(s) with the NIC(s) w/ the given IP
                Get-View -Viewtype VirtualMachine -Property Name, Guest.Net | Where-Object {$_.Guest.Net | Where-Object $sblkFindByIP_WhereStatement} | Select @{n="VMName"; e={$_.Name}}, @{n="IPAddr"; e={($_.Guest.Net | %{$_.IpAddress} | sort) -join ","}}
            } ## end case
        } ## end switch
    } ## end process
} ## end function

A few examples of using the function to find VMs by various addresses (as shown in the comment-based help, too):
PS vN:\> Get-VMByAddress -MAC 00:50:56:00:00:02
VMName        MacAddress
------        ----------
dev0-server   00:50:56:00:00:02,00:50:56:00:00:04


PS vN:\> Get-VMByAddress -IP 10.37.31.120
VMName         IPAddr
------         ------
dev0-server2   192.168.133.1,192.168.253.1,10.37.31.120,fe80::...


PS vN:\> Get-VMByAddress -AddressWildcard 10.0.0.*
VMName    IPAddr
------    ------
someVM0   10.0.0.119,fe80::...
someVM2   10.0.0.138,fe80::...
...

There are several places on the web that talk about getting VMs' IPs, getting their MAC addresses, exporting them, and so on, but this is for getting VMs based on the given address. Note: Since IP address info in vCenter is gotten from guest OSes via VMware Tools, this function is going to rely on that info. And, since the function uses everyone's favorite cmdlet, Get-View, and the calls with this cmdlet are properly tuned, you know that it is going to be FaF!

8 comments:

  1. I tried this script in my env but doesn't work. Seems that it isn't usable out of the box. Could you help me?

    ReplyDelete
    Replies
    1. Hello, Alessandro-

      What bad news! Yes, we can help, if you help us help: what are some details about your environment (host / vCenter / PowerShell / PowerCLI versions), what is the example command that you are running, and, what symptom(s) do you get when this call seems to not work?

      Delete
  2. Awesome function! Strangely when I run Get-View -ViewType VirtualMachine -Property Name, Guest.Net the Guest IP addresses aren't returned inside the GuestInfo. Only when I get all of the VM properties, i.e no "filter" for just the Name and Guest.Net properties, do I get the IP Addresses within the Guest.Net object. Interested to know if others are able to limit the properties returned to just Name and Guest.Net and still have the IP Addresses returned??

    ReplyDelete
  3. Get-VM | where {$_.Guest.IPAddress -match "IP Here"}

    Replace "IP Here" with the IP you're searching for in quotes. Since -match is used, you can also wildcard search using this by simply searching for only that which will match. Thus, 172.16.1. will search for all addresses 172.16.1.0-255. .16.1.254 will find all addresses ending in .16.1.254 with any first octet.

    ReplyDelete
  4. This is terrible.
    use -filter with get-view and set filter as a variable.
    I manage over 6000 vms and piping get-vm is not an option as it would take FOREVER. THe fastest and most efficient way to do something like this would be the following:

    $filter = @{'Guest.IPAddress' = "$IP"}

    get-view -ViewType VirtualMachine -Filter $filter |
    select Name,
    @{n='GustOS';e={$_.Summary.Config.GuestFullName}},
    @{n='Host';e={get-vmhost -id $_.runtime.host}},
    @{n='IP';e={$_.Guest.IPAddress}}

    replace $IP with the IP you wish to search for and of course change what you like to select out of it, i chose the name, host it lives on and the guest os.

    Here is the function wrapped around it:

    Function Get-VMfromIP {
    [CmdletBinding()]
    param(
    [parameter(Mandatory=$true,Position=0)]
    [System.Net.IPAddress]$IP
    )

    $filter = @{'Guest.IPAddress' = "$IP"}
    get-view -ViewType VirtualMachine -Filter $filter |
    select Name,
    @{n='GustOS';e={$_.Summary.Config.GuestFullName}},
    @{n='Host';e={get-vmhost -id $_.runtime.host}},
    @{n='IP';e={$_.Guest.IPAddress}}
    }

    Now just execute Get-VMfromIP -IP

    You can thank me later :)


    ReplyDelete
  5. example above would be Get-VMfromIP -IP 10.10.0.1

    Or whatever IP you are searching for...sorry it didnt show up in my original comment.

    ReplyDelete
  6. Hr when I use it wrapped in the function I get nothing back:
    PS C:\Powershell> .\Get-VMfromIP.ps1 -IP 10.10.0.1
    PS C:\Powershell>
    However when I hardcode the IP it works perfectly. Any suggestions?

    ReplyDelete
  7. Thanks for sharing!

    I did change a couple things though. I did a search/replace on the following variables:

    $MacToFind_str --> $MAC
    $IpToFind_str --> $IP
    $AddressWildcard_str --> $AddressWildcard

    That way when tabbing through values in a powershell session, the results match up with the syntax you have mentioned in the examples.

    ReplyDelete