VMware Cloud Community
flipster
Contributor
Contributor
Jump to solution

Find unregistered virtual machines

Hi,

Does anyone know/have a way to find unregistered virtual machines?

Thanks in advance!

Tags (2)
Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Apparently it wasn't my day yesterday Smiley Sad

The attached file above (Find-unregistered-VMX-with-CSV-v4.ps1) should now be the correct version.

Sorry about that.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
54 Replies
LucD
Leadership
Leadership
Jump to solution

Did you have a look at ?

This script registers the guests but it shouldn't be too hard to replace the RegisterVM_Task method with a Write-Host that just list VMX file.

If it is, let me know and I will give you an adapted version of the script.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
flipster
Contributor
Contributor
Jump to solution

Hi LucD,

If it's not a big deal, could you kindly help get your script to just output either to the console or a file which vms are unregistered? I'm deifnitely trying to learn posh as quickyl as possible. But your help is always appreciated.

Thanks LucD,

PS we should start a blog on "How many ways can we Thank and Compliment LucD" 😃

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I have adapted the original script a bit to list just the VMX files of unregistered guests.

The script run through all your datacenters and for each datacenter through all the clusters.

In each cluster it scans every VMFS datastore for unregistered VMX files.


Get-Datacenter | % {
	$datacenter = $_
	Write-Host "Datacenter" $_.Name
	$folder = Get-View ($_ | Get-Folder -Name "vm").ID
	$_ | Get-Cluster | % {
		$cluster = $_
		Write-Host "`tCluster" $_.Name
		$pool = Get-View ($_ | Get-ResourcePool -Name "Resources").ID

		$esxImpl = Get-Cluster -Name $cluster | Get-VMHost | select -First 1
		$esx = Get-View $esxImpl.ID 
		$dsBrowser = Get-View $esx.DatastoreBrowser
		foreach($dsImpl in $dsBrowser.Datastore){
			$ds = Get-View $dsImpl
			if($ds.Summary.Type -eq "NFS"){continue}
			$vms = @()
			foreach($vmImpl in $ds.Vm){
				$vm = Get-View $vmImpl
				$vms += $vm.Config.Files.VmPathName
			}
			$datastorepath = ""
			Write-Host "`t`tDatastore" $ds.Summary.Name

			$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
			$searchSpec.matchpattern = "*.vmx"

			$task = Get-View ($dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec))

			while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued"){
				$task.UpdateViewData()
				sleep 5
			}

			if($task.info.result -ne $null){
				foreach ($file in $task.info.Result){
					$found = $FALSE
					foreach($vmx in $vms){
						if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
							$found = $TRUE
						}
					}
					if (-not $found -and $task.Info.Result[0].File -ne $null){
						$vmx = $file.FolderPath + $file.File[0].Path
						Write-Host "`t`t" $vmx
					}
				}
			}
		}
	}
}

Note1: if you want to also scan datastores that are not of the VMFS type you remove the line: if($ds.Summary.Type -eq "NFS")

Note2: if you only have standalone ESX hosts in your datacenter the script needs a few changes. Let me know if that is needed.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
flipster
Contributor
Contributor
Jump to solution

Hi LucD,

I powered off a vm and unregistered it. When I run the script, it doesn't pick it up as an unregistred vm.

Am I doing something wrong? I'm running this script against an entire vCenter Server.

Thanks!

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Are your datastores by any chance NFS ?

Is the VMX file on a datastore that is connected to one of the clusters ?

Does the script at least list all your datacenters and clusters correctly ?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
flipster
Contributor
Contributor
Jump to solution

LucD,

The datastores are on SAN storage. The VMX file is located on one of the datastores. Our cluster use boot and datastors on SAN. Yes, it does lsit all the clusters and datacenters correctly.

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

Hi,

Thanks for the gr8 script, I was searhing for the same. I am not able to see any output of the script, can be generate the output to some file ?

Thanks in advance

Gurjit Dhillon

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The script writes to the console with the Write-Host cmdlet.

Of course, if there no unregistered guests nothing will appear on screen.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

Hi ,

I have tried this script on my all the clusters, I can see output come with lots of listing of satastore name. Can I also get the VM name along with the Datastore name and also can this out put be generated in Xls file ?

Many thanks

Gurjit Dhillon

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The name of a VM doesn't have to correspond with the name of the .VMX file of that VM.

The "displayName" field in the .VMX file contains the name given to the VM.

It is difficult to extract the "displayName" field from a .VMX file when the VM is not registered.

The only way to this (in my opinion) is to:

*) register the VM from the .VMX file

*) get the VM's name

*) unregister the VM again

But this could give some problems (for example duplicate names for VMs).

The script higher up in this thread suffers from the problem the forum SW has with square brackets.

The line $dspath is incorrectly displayed. It should be: $datastorepath = "\[" + $ds.Info.Name + "\]"

Better to use the attached script instead of copying it from the browser.

See for a script to re-register VMs with the name in the "displayName" field.

The script below will create a CSV file with the same information you can see on the console.

If you don't want the console output, comment out the Write-Host cmdlets.

$report = @()
Get-Datacenter | % {
	$datacenter = $_
	Write-Host "Datacenter" $_.Name
	$folder = Get-View ($_ | Get-Folder -Name "vm").ID
	$_ | Get-Cluster | % {
		$cluster = $_
		Write-Host "`tCluster" $_.Name
		$pool = Get-View ($_ | Get-ResourcePool -Name "Resources").ID

		$esxImpl = Get-Cluster -Name $cluster | Get-VMHost | select -First 1
		$esx = Get-View $esxImpl.ID 
		$dsBrowser = Get-View $esx.DatastoreBrowser
		foreach($dsImpl in $dsBrowser.Datastore){
			$ds = Get-View $dsImpl
			if($ds.Summary.Type -eq "NFS"){continue}
			$vms = @()
			foreach($vmImpl in $ds.Vm){
				$vm = Get-View $vmImpl
				$vms += $vm.Config.Files.VmPathName
			}
			$datastorepath = ""
			Write-Host "`t`tDatastore" $ds.Summary.Name

			$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
			$searchSpec.matchpattern = "*.vmx"

			$task = Get-View ($dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec))

			while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued"){
				$task.UpdateViewData()
				sleep 5
			}

			if($task.info.result -ne $null){
				foreach ($file in $task.info.Result){
					$found = $FALSE
					foreach($vmx in $vms){
						if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
							$found = $TRUE
						}
					}
					if (-not $found -and $task.Info.Result[0].File -ne $null){
						$vmx = $file.FolderPath + $file.File[0].Path
						Write-Host "`t`t" $vmx
						$row = "" | Select Datacenter, Cluster, Datastore, VMXpath
						$row.Datacenter = $datacenter.Name
						$row.Cluster = $cluster.Name
						$row.Datastore = $ds.Name
						$row.VMXpath = $vmx
						$report += $row
					}
				}
			}
		}
	}
}
$report | Export-Csv "C:\Unregistered-VMX.csv" -NoTypeInformation

Use the attached script to avoid the square brackets problem.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

Hi,

May be the name of the directory where the vmx file is placed can be used as VM name or VM name can be extrated from the vmx file, example vm name is test-vm.vmx, after extraxtion from vmx file, vm name will be "test-vm" or listing of vmx file along with vm name example test-vm.vmx will also do.

Thanks in advance.

Gurjit Dhillon

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

That can be done, but mind that the VM name does not always correspond with the name of the VMX file.

$report = @()
Get-Datacenter | % {
	$datacenter = $_
	Write-Host "Datacenter" $_.Name
	$folder = Get-View ($_ | Get-Folder -Name "vm").ID
	$_ | Get-Cluster | % {
		$cluster = $_
		Write-Host "`tCluster" $_.Name
		$pool = Get-View ($_ | Get-ResourcePool -Name "Resources").ID

		$esxImpl = Get-Cluster -Name $cluster | Get-VMHost | select -First 1
		$esx = Get-View $esxImpl.ID 
		$dsBrowser = Get-View $esx.DatastoreBrowser
		foreach($dsImpl in $dsBrowser.Datastore){
			$ds = Get-View $dsImpl
			if($ds.Summary.Type -eq "NFS"){continue}
			$vms = @()
			foreach($vmImpl in $ds.Vm){
				$vm = Get-View $vmImpl
				$vms += $vm.Config.Files.VmPathName
			}
			$datastorepath = ""
			Write-Host "`t`tDatastore" $ds.Summary.Name

			$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
			$searchSpec.matchpattern = "*.vmx"

			$task = Get-View ($dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec))

			while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued"){
				$task.UpdateViewData()
				sleep 5
			}

			if($task.info.result -ne $null){
				foreach ($file in $task.info.Result){
					$found = $FALSE
					foreach($vmx in $vms){
						if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
							$found = $TRUE
						}
					}
					if (-not $found -and $task.Info.Result[0].File -ne $null){
						$vmx = $file.FolderPath + $file.File[0].Path
						Write-Host "`t`t" $vmx
						$row = "" | Select Datacenter, Cluster, Datastore, VMname, VMXpath
						$row.Datacenter = $datacenter.Name
						$row.Cluster = $cluster.Name
						$row.Datastore = $ds.Summary.Name
						$row.VMname = (([regex]"(\w+)\.vmx").Match($file.File[0].Path)).Groups[1].Value
						$row.VMXpath = $vmx
						$report += $row
					}
				}
			}
		}
	}
}
$report | Export-Csv "C:\Unregistered-VMX.csv" -NoTypeInformation


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

Hi,

I think it shows the Vm name along with the datastore, as shown below. But I got few error while execting the script. can you put some light on this error. I have attach the script I am running for your reference.

Unregistered VM - VMX file build-css-std-r2-sp2-stage-a/build-css-std-r2-sp2-stage-a.vmx

Cannot index into a null array.

At :line:26 char:37

+ if(($file.FolderPath + $file.File[ <<<< 0].Path) -eq $vmx){

Regards

Gurjit Dhillon

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I suspect you are referring to the script in .

The problem you are seeing is probably caused by the fact that the foreach statement runs at least once through a loop although the array is $null.

The solution is to add a test before the loop starts.

The reason why the array is $null is probably because there are no .VMX files on that datastore.

This is the script with the extra test.

$folder = Get-View (Get-Datacenter -Name <datacenter> | Get-Folder -Name "vm").ID
$pool = Get-View (Get-Cluster -Name <cluster> | Get-ResourcePool -Name "Resources").ID

$esxImpl = Get-VMHost -Name <ESX-host>
$esx = Get-View $esxImpl.ID 
$dsBrowser = Get-View $esx.DatastoreBrowser
foreach($dsImpl in $dsBrowser.Datastore){
	$ds = Get-View $dsImpl
	$vms = @()
	foreach($vmImpl in $ds.Vm){
		$vm = Get-View $vmImpl
		$vms += $vm.Config.Files.VmPathName
	}
	$datastorepath = ""

	$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
	$searchSpec.matchpattern = "*.vmx"

	$taskMoRef = $dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec) 
	$task = Get-View $taskMoRef 
	while ($task.Info.State -eq "running"){$task = Get-View $taskMoRef}

	if($task.info.result -ne $null){
		foreach ($file in $task.info.Result){
			$found = $FALSE
			foreach($vmx in $vms){
				if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
					$found = $TRUE
				}
			}
			if (-not $found -and $task.Info.Result[0].File -ne $null){
				$vmx = $file.FolderPath + $file.File[0].Path
				$params = @($vmx,$null,$FALSE,$pool.MoRef,$null)
				$folder.GetType().GetMethod("RegisterVM_Task").Invoke($folder, $params)

			}
		}
	}
}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

I can still see the below error, I have used your new sciprt called register-vm3.ps1.

Type : Task

Value : task-285208

Cannot index into a null array.

At :line:28 char:38

+ if(($file.FolderPath + $file.File[ &lt;&lt;&lt;&lt; 0].Path) -eq $vmx){

Regards

Gurjit Dhillon

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

That can probably happen when there is a subfolder on the datastore that doesn't contain a .VMX file.

This script contains an extra test to skip that situation.

$folder = Get-View (Get-Datacenter -Name <datacenter> | Get-Folder -Name "vm").ID
$pool = Get-View (Get-Cluster -Name <cluster> | Get-ResourcePool -Name "Resources").ID

$esxImpl = Get-VMHost -Name <ESX-host>
$esx = Get-View $esxImpl.ID 
$dsBrowser = Get-View $esx.DatastoreBrowser
foreach($dsImpl in $dsBrowser.Datastore){
	$ds = Get-View $dsImpl
	$vms = @()
	foreach($vmImpl in $ds.Vm){
		$vm = Get-View $vmImpl
		$vms += $vm.Config.Files.VmPathName
	}
	$datastorepath = ""

	$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
	$searchSpec.matchpattern = "*.vmx"

	$taskMoRef = $dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec) 
	$task = Get-View $taskMoRef 
	while ($task.Info.State -eq "running"){$task = Get-View $taskMoRef}

	if($task.info.result -ne $null){
		foreach ($result in $task.info.Result){
			if($result.File -ne $null){
				$found = $FALSE
				foreach($vmx in $vms){
					if(($result.FolderPath + $result.File[0].Path) -eq $vmx){
						$found = $TRUE
					}
				}
				if (-not $found -and $task.Info.Result[0].File -ne $null){
					$vmx = $result.FolderPath + $result.File[0].Path
					$params = @($vmx,$null,$FALSE,$pool.MoRef,$null)
					$folder.GetType().GetMethod("RegisterVM_Task").Invoke($folder, $params)

				}
			}
		}
	}
}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

Hi,

Many thanks LUC Smiley Happy, this version of script worked fine.I can see my unregister vm's are registered again on VC.

I have a small request, Can this script be modifed to just show the list of Vm's which are not registered on VC along, that mean not to registered any vm on VC with the Datastore name and if possible the list to export in xls file.

Regards

Gurjit Dhillon

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

In one of my previous replies in this thread I attached a script called Find-unregistered-VMX-with-CSV-v2.ps1 that finds the unregistered .VMX files and exports the results to a CSV file.

The attached script is the updated version with the extra check for folders that do not contain a .VMX file.

Get-Datacenter | % {
	$datacenter = $_
	Write-Host "Datacenter" $_.Name
	$folder = Get-View ($_ | Get-Folder -Name "vm").ID
	$_ | Get-Cluster | % {
		$cluster = $_
		Write-Host "`tCluster" $_.Name
		$pool = Get-View ($_ | Get-ResourcePool -Name "Resources").ID

		$esxImpl = Get-Cluster -Name $cluster | Get-VMHost | select -First 1
		$esx = Get-View $esxImpl.ID 
		$dsBrowser = Get-View $esx.DatastoreBrowser
		foreach($dsImpl in $dsBrowser.Datastore){
			$ds = Get-View $dsImpl
			if($ds.Summary.Type -eq "NFS"){continue}
			$vms = @()
			foreach($vmImpl in $ds.Vm){
				$vm = Get-View $vmImpl
				$vms += $vm.Config.Files.VmPathName
			}
			$datastorepath = ""
			Write-Host "`t`tDatastore" $ds.Summary.Name

			$searchspec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
			$searchSpec.matchpattern = "*.vmx"

			$task = Get-View ($dsBrowser.SearchDatastoreSubFolders_Task($datastorePath, $searchSpec))

			while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued"){
				$task.UpdateViewData()
				sleep 5
			}

			if($task.info.result -ne $null){
				foreach ($file in $task.info.Result){
					if($result.File -ne $null){
						$found = $FALSE
						foreach($vmx in $vms){
							if(($file.FolderPath + $file.File[0].Path) -eq $vmx){
								$found = $TRUE
							}
						}
						if (-not $found -and $task.Info.Result[0].File -ne $null){
							$vmx = $file.FolderPath + $file.File[0].Path
							Write-Host "`t`t" $vmx
							$row = "" | Select Datacenter, Cluster, Datastore, VMname, VMXpath
							$row.Datacenter = $datacenter.Name
							$row.Cluster = $cluster.Name
							$row.Datastore = $ds.Summary.Name
							$row.VMname = (([regex]"(\w+)\.vmx").Match($file.File[0].Path)).Groups[1].Value
							$row.VMXpath = $vmx
							$report += $row
						}
					}
				}
			}
		}
	}
}
$report | Export-Csv "C:\Unregistered-VMX.csv" -NoTypeInformation


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
gurjitd
Contributor
Contributor
Jump to solution

Hi Luc,

Hope you are doing fine.

I have not checked this script, I will be doing it today.Above you have mention in the directory script will check if they don’t have vmx file, but there are greater chances that vmx file resides on some

other directory on some other datastore. I believe this script should not behave like this. I was looking for all the VM which doesn’t have vmx file on any directory on any datastore.

Please correct me if I have not understood correctly.

Your support is highly appreciated.

Regards

Gurjit Dhillon

Reply
0 Kudos