# encoding: ascii
# api: powershell
# title: VMware guest information
# description: I needed to write a script to generate a VMware guest inventory so I needed to know what was available within the PowerCLI interface and where to find it. So I wrote this script to dump everything it could find about a single VMware guest. It has proved very useful to me. I hope others will also find it useful.
# version: 4.1
# type: script
# author: Bruce Shreffler
# license: CC0
# x-poshcode-id: 3129
# x-archived: 2016-03-04T19:40:20
# x-published: 2012-12-27T10:18:00
#
#
# vminfo.ps1
#
# Give this a VMware guest and it will attempt to dump all the information available.
#
# Example: .\vminfo.ps1  "guestname"
#
# This scripts assumes that you have already connected to a VirtualCenter server.
#
# This was developed and tested using VMware vSphere PowerCLI version 4.1.1.2816
# on an ESXi 4.1 build 381591 cluster

Param($guest)

####################################################################
# ProcessObject - This recursive function does the bulk of the work. 
#
#Input Parameters
#
# $xobj - Object to dump info on
# $pref - Prefix to use on print lines
# 
# When an object is encountered then we recursively call ourselves  to
# process that object after first updating the prefix.
#
# Snapshot objects can point to both a parent and a child. Dumping both
# Parent and Child objects results in an endless loop. Therefore dump only
# Child objects.

function ProcessObject ($xobj, $pref){
    $plen = $pref.length
	$pad = $width - $plen
	If ($pad -lt 0){$pad = 0}
	# Get information on all of the members of this object because what is done with a member is
	# dependent on whether the member is an object or is a data item and if it is a data item
	# what kind of data item is it
	$xom = $xobj | get-member -membertype property
    foreach ($ent in $xom){
		$xnam = $ent.name
		#write-host ">>>>>>>Processing entry" $ent.name
        $xnlen = $xnam.length
        $strs = $ent.Definition.split(" ")
		
		# process 1st level simple things like strings and numbers including simple arrays
		
		# There are some fields in a VMware guest "Get-View" description we will ignore
		# because they are obsolete and have either had their descriptive names changed
		# or a new cmdlet is now available and it is the preferred method for finding
		# out about the object.
		# 
		# The following "objects" may be encountered but will be ignored. These are legacy objects 
		# from ESX 3.5 but were promoted to having their own cmdlets in ESX 4. Therefore we will
		# ignore them when found as members of some other object prefering to examine them using the
		# new cmdlets.
		#
		# legacy guest objects
		#
		# CDDrives == Get-CDDrives
		# FloppyDrives == Get-FloppyDrive
		# HardDisk == Get-HardDisk
		# NetworkAdapters == Get-NetworkAdapter
		# UsbDevices == Get-UsbDevice
		#
		# legacy host objects
		#
		# ConsoleNic == Get-VMHostNetworkAdapter
		# PhysicalNic == Get-VMHostNetworkAdapter
		# VirtualNic == Get-VMHostNetworkAdapter
		# VirtualSwitch == Get-VirtualSwitch
		# ScsiLun == Get-ScsiLun
		#
		#
		
        if ($xnam -match "^(CDDrives|FloppyDrives|HardDisks|NetworkAdapters|UsbDevices)$" ){
			"{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", "== skipping legacy device object ==" #| Out-File -FilePath $reportfile -Append
		}
		elseif ($xnam -match "^(ConsoleNic|PhysicalNic|ScsiLun|VirtualNic|VirtualSwitch)$" ){
			#Write-Host "****** skipping legacy Network Adapter object- $xnam"
			"{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", "== skipping legacy Network Adapter object ==" #| Out-File -FilePath $reportfile -Append
		}
		# The following are obsolete identifiers in ESX 3.5 that have new names as of ESX version 4.x
		# We will ignore all old identifiers and only display the new identifiers.
		#
		# Description - replaced by Notes
		# Host is replaced by VMHost
		# HostId is replaced by VMHostId
		# State == ConnectionState
		# VirtualMachineId == VMId
		elseif ($xnam -match "^(Description|Host|HostId|State|VirtualMachineId)$" ){
			"{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", "== skipping obsolete identifier ==" #| Out-File -FilePath $reportfile -Append
		}
		# Examining the following will result in recursive loops threrfore we will skip them.
		#
		# Parent
		# VM
		# VMHost
		elseif ($xnam -match "^(VMHost)$" ){
			"{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", "== skipping object to avoid infinite loop ==" #| Out-File -FilePath $reportfile -Append
		}
		elseif ($strs[0] -match "^System.String\[\]" ){
			if (!$xobj.$xnam.count) {
				$res = $xobj.$xnam
				#Write-Host "===  single entry array name ="  $xnam "result" $res
				"{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", $xobj.$xnam #| Out-File -FilePath $reportfile -Append
			}
			else {
				$count = $xobj.$xnam.count
				for ($i=0; $i -lt $count; $i++){
					$namlen = $xnam.length
					$ipad = $pad - 2 - $namlen
					If ($i -gt 9){$ipad--}
					If ($ipad -lt 1){$ipad = 1}
					"{0,-$plen}{1,-1}{2,-$namlen}{3,1}{4,1}{5,-$ipad}{6,-2}{7,-30}" -f $pref, ".", $xnam, "[", $i, "]", ":", $xobj.$xnam[$i] #| Out-File -FilePath $reportfile -Append
				}
			}
		}
		elseif ($strs[0] -match "^System.(String|Int|Nullable|Boolean|DateTime|Double)" ){
			# If this is a "value__" then show the number and the symbolic name
			if ($xnam -eq "value__"){
				$vpad = $pad + 3
				"{0,-$plen}{1,$vpad}{2,-2}{3,-2}{4,-30}" -f $pref, ": ", $xobj.$xnam, "-", $xobj #| Out-File -FilePath $reportfile -Append
			}
			else {
				"{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", $xobj.$xnam #| Out-File -FilePath $reportfile -Append		
			}
		}
        elseif ($strs[0] -match "^VMware.Vim(.|Automation.Types|Automation.ViCore)"){
			# Hack to avoid closed loop when crawling snapshot structures
			# Each child points back to its parent and each parent points to its children
			# hence and endless loop. Therefore do not process parents
			if ($xnam -match "^(Parent|ParentSnapshot|VM)$"){
				$pad = $width-$plen
				If ($pad -lt 0){$pad = 0}
				"{0,-$plen}{1,-1}{2,-$pad}{3,-2}" -f $pref, ".", $xnam, ": == skipping $xnam to avoid infinite loop ==" #| Out-File -FilePath $reportfile -Append
			}
			else {
	            $pad = $width-$plen
				If ($pad -lt 0){$pad = 0}
	            $yobj = $xobj.$xnam
				$newp = $pref + "." + $xnam
				if (!$yobj){
					"{0,-$plen}{1,-1}{2,-$pad}{3,-2}" -f $pref, ".", $xnam, ": =undefined=" #| Out-File -FilePath $reportfile -Append
				}
				else{
					if (!$yobj.count){
						ProcessObject $yobj $newp
					}
					else {
						$lc = $yobj.count
						for ($z = 0; $z -lt $lc; $z++){
							$zobj = $yobj[$z]
							$zperf = $newp + "[" + $z + "]"
							ProcessObject $zobj $zperf
						}
					}
				}
			}
		}
		elseif ($strs[0] -match "^System.(Collections)" ){
			Foreach ($entry in $xobj.$xnam){
				$newpref = $pref + "." + $xnam
				$cpad = $pad - $xnam.length - 1
				If ($cpad -lt 0){$cpad = 0}
				"{0,-$plen}{1,-1}{2,-$Cpad}{3,-2}{4,-30}" -f $newpref, ".", $entry.Key, ":", $entry.value #$strs[0] #| Out-File -FilePath $reportfile -Append
			}
		}
		else {
            "{0,-$plen}{1,-1}{2,-$pad}{3,-2}{4,-30}" -f $pref, ".", $xnam, ":", $ent.Definition #$strs[0] #| Out-File -FilePath $reportfile -Append
		}
	}	
}

function POLauncher ($xobj, $pref){
	if (!$xobj.count){
		ProcessObject $xobj $pref
	}
	else {
		$lc = $xobj.count
		for ($z = 0; $z -lt $lc; $z++){
			$zobj = $xobj[$z]
			$zperf = $pref + "[" + $z + "]"
			ProcessObject $zobj $zperf
		}
	}
}

# end function ProcessObject


$reportfile = "vminfo-rpt.txt"
$first = 20
$width = 70

$xvm = get-vm -name $guest
$prefix = $xvm.name + " Get-VM"
POLauncher $xvm $prefix


# Display information about virtual devices attached to the guest
# Process devices CDDrives|Harddisk|FloppyDrives|NetworkAdapters|UsbDevices

$dvm = get-HardDisk -vm $xvm
$prefix = $xvm.name + " Get-HardDisk"
POLauncher $dvm $prefix

$cdvm = get-CDDrive -vm $xvm
$prefix = $xvm.name + " Get-CDDrive"
POLauncher $cdvm $prefix

$flpvm = get-FloppyDrive -vm $xvm
$prefix = $xvm.name + " Get-FloppyDrive"
POLauncher $flpvm $prefix

$netvm = get-NetworkAdapter -vm $xvm
$prefix = $xvm.name + " Get-NetworkAdapter"
POLauncher $netvm $prefix

$usbvm = get-UsbDevice -vm $xvm
$prefix = $xvm.name + " Get-UsbDevice"
POLauncher usbvm $prefix

#Get-View return more information than Get-Vm
$xview = $xvm | get-view
$prefix = $xvm.name + " Get-View"
POLauncher $xview $prefix

# Return information about any snapshots.
$snap = get-snapshot $xvm
if ($snap){
	$prefix = $xvm.name + " Get-Snapshot"
	POLauncher $snap $prefix
}

#Tell ua about the host we are running on
$vmhost = $xvm.VMHost
$prefix = $vmhost.name + " Get-VMHost"
POLauncher $vmhost $prefix

# probably don't want to do this one unless you want to be buried in data
#$vmhostv = Get-View $vmhost
#$prefix = $vmhostv.name + " Get-View"
#POLauncher $vmhostv $prefix

#Dump information about the Host's devices 

$nics = Get-VMHostNetworkAdapter $vmhost
foreach ($nic in $nics){
	$prefix = $vmhost.name + " " + $nic.Name
	POLauncher $nic $prefix
}



$vmguest = get-vmguest $xvm
$prefix = $xvm.name + " Get-VMGuest"
#POLauncher $vmguest $prefix


$vmdatastore = get-datastore -vm $xvm
$prefix = $xvm.name + " Get-Datastore"
POLauncher $vmdatastore $prefix

$dc = Get-Datacenter -vm $xvm
$prefix = $xvm.name + " Get-Datacenter"
#POLauncher $dc $prefix
