Functions/Public/Get-VMHostNetworkLldpInfo.ps1

<#
        .Synopsis
        Display the LLDP info for each vmnic
        .Description
        Display the LLDP info for each vmnic of hosts provided. The Posh-SSH module is required. An SSH connection is established with each host to capture LLDP info. If the switch name or port ID are missing/incorrect, view the RawOutput property
        .Parameter VMHost
        The host you want to display the vmnic LLDP info of. Can be a single host or multiple hosts provided by the pipeline. Wildcards are supported
        .Parameter User
        A local host user with permission to establish an SSH connection. The user 'root' is default
        .Example
        PS C:\>Get-VMHostNetworkLldpInfo -VMHost esxi* -Nic vmnic0, vmnic1

        Displays the vmnic LLDP info of all ESXi hosts with names that begin with 'esxi'
        .Example
        PS C:\>Get-VMHostNetworkLldpInfo -VMHost esxi* -Nic *

        Displays the vmnic LLDP info, for all vmnics, of all ESXi hosts with names that begin with 'esxi'
        .Link
        https://github.com/Dapacruz/VMware.VimAutomation.Custom
#>

function Get-VMHostNetworkLldpInfo {
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position=0)]
        [Alias('Name', 'VMHosts')]
        [string[]]$VMHost = '*',

        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string[]]$Nic,

        [Parameter(ValueFromPipeline)]
        [Alias('UserName')]
        [string]$User = 'root',
        [boolean]$AcceptKey = $true
    )
    Begin {
        if (-not (Get-Module -Name Posh-SSH -ListAvailable)) {
            throw "The Posh-SSH module is required. Execute 'Install-Module -Name Posh-SSH -Scope CurrentUser' to install it."
        }

        $credential = Get-Credential -UserName $User -Message 'Enter host SSH credentials.'
        $results = @()

        trap { Get-SSHSession | Remove-SSHSession }
    }
    Process {
        # Expand to full hostname in case wildcards are used
        $VMHost = Get-VMHost -Name $VMHost | Sort-Object -Property Name

        foreach ($h in $VMHost) {
            Write-Host "Host: $h"
            $h_addr = (Get-VMHostNetworkAdapter -VMHost $h -VMKernel).Where{$_.ManagementTrafficEnabled -eq $true}.IP
            $Nic = Get-VMHostNetworkAdapter -VMHost $h -Physical -Name $Nic | Sort-Object -Property Name

            Write-Host 'Establisting an SSH connection ... ' -NoNewline
            try {
                $ssh = New-SSHSession -ComputerName $h_addr -Credential $credential -AcceptKey:$AcceptKey -ErrorAction Stop
                Write-Host 'success'
            } catch {
                Write-Host 'fail'
                Write-Warning "Failed to establish an SSH connection to $h ($h_addr)."
                continue
            }

            foreach ($vmnic in $Nic) {
                $raw = ''
                $device_id = ''
                $port_id = ''

                $obj = New-Object -TypeName PSObject
                $obj.PSTypeNames.Insert(0,'VMware.VimAutomation.Custom.Get.VMHostNetworkLldpInfo')
                Add-Member -InputObject $obj -MemberType NoteProperty -Name VMHost -Value $h
                Add-Member -InputObject $obj -MemberType NoteProperty -Name Nic -Value $vmnic
                
                try {
                    Write-Host "Listening for LLDP on $vmnic ... " -NoNewline
                    # Capture one LLDP frame
                    $cmd = "pktcap-uw --uplink $vmnic --ethtype 0x88cc -c 1 -o /tmp/vmnic_lldp.pcap > /dev/null"
                    Invoke-SSHCommand -SessionId $ssh.SessionId -Command $cmd -TimeOut 45 -ErrorAction Stop | Out-Null
                    
                    # Convert the packet capture to hex and save the ASCII content
                    $cmd = "tcpdump-uw -r /tmp/vmnic_lldp.pcap -v | grep -E 'System Name TLV|Subtype Interface Name|Port Description TLV'"
                    $raw = Invoke-SSHCommand -SessionId $ssh.SessionId -Command $cmd -ErrorAction Stop

                    Write-Host 'success'
                } catch {
                    # Kill the pktcap-uw process
                    $cmd = "kill -2 `$(lsof | grep pktcap-uw | awk '{print `$1}' | sort -u)"
                    Invoke-SSHCommand -SessionId $ssh.SessionId -Command $cmd -ErrorAction Stop | Out-Null

                    Write-Host 'fail'
                    Write-Warning "Operation timed out while listening for LLDP on $h ($vmnic)."
                } finally {
                    # Remove capture files
                    $cmd = "rm /tmp/vmnic_lldp.pcap"
                    Invoke-SSHCommand -SessionId $ssh.SessionId -Command $cmd -ErrorAction Stop | Out-Null
                }

                foreach ($tlv in $raw.Output) {
                    $tlv = $tlv.Trim()

                    $regex = "System Name TLV.*?`:\s(.*)"
                    if ($tlv -match $regex) {
                        $device_id = $tlv -replace $regex, '$1' -as [string]
                    }

                    $regex = "Subtype Interface Name.*?`:\s(.*)"
                    if ($tlv -match $regex -and $port_id -eq '') {
                        $port_id = $tlv -replace $regex, '$1' -as [string]
                    }

                    $regex = "Port Description TLV.*?`:\s(.*)"
                    if ($tlv -match $regex -and $port_id -eq '') {
                        $port_id = $tlv -replace $regex, '$1' -as [string]
                    }
                }
                
                if ($device_id) {
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Switch -Value $device_id
                } else {
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name Switch -Value 'n/a'
                }
                
                if ($port_id) {
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name PortId -Value $port_id                    
                } else {
                    Add-Member -InputObject $obj -MemberType NoteProperty -Name PortId -Value 'n/a'
                }
                
                # Include the raw output in case regex replace fails
                Add-Member -InputObject $obj -MemberType NoteProperty -Name RawOutput -Value ($raw.Output -as [string]).Trim()
                
                $results += $obj
            }
            
            Remove-SSHSession -SessionId $ssh.SessionId | Out-Null
            Write-Host ''
        }
    }
    End {
        Write-Output $results
    }
}