Home > PowerCLI, PowerShell > VMX Raiders Revisited

VMX Raiders Revisited

August 11th, 2011 Leave a comment Go to comments

This afternoon news of a new (and interesting) blog post from Julian Wood hit the Twitter-verse.

He took part from the logic of my Raiders of the Lost VMX script and used the relatively new VMFilePath parameter of the New-VM cmdlet, to shorten my script considerable. Well done, great job !

But I was intrigued by a remark in Julian’s post. He was obviously not too fond of using MoRefs in a script. But in Julian’s final script I still saw a Get-View cmdlet and the use of the HostDatastoreBrowser. Remembering a remark from one of my co-authors on the PowerCLI book, that the Get-View cmdlet would disappear, once the PowerCLI snapin would be sufficiently evolved, I decided to tackle Julian’s script and give it another simplification treatment and make it more PowerCLI ๐Ÿ˜€

The script

$Cluster = "MyCluster"
$Datastores = "MyDS"
$VMFolder = "MyFolder"

$ESXHost = Get-Cluster $Cluster | Get-VMHost | select -First 1

foreach($Datastore in Get-Datastore $Datastores) {
	# Collect .vmx paths of registered VMs on the datastore
	$registered = @{}
	Get-VM -Datastore $Datastore | %{$_.Extensiondata.LayoutEx.File | where {$_.Name -like "*.vmx"} | %{$registered.Add($_.Name,$true)}}

   # Set up Search for .VMX Files in Datastore
	New-PSDrive -Name TgtDS -Location $Datastore -PSProvider VimDatastore -Root '\' | Out-Null
	$unregistered = @(Get-ChildItem -Path TgtDS: -Recurse | `
		where {$_.FolderPath -notmatch ".snapshot" -and $_.Name -like "*.vmx" -and !$registered.ContainsKey($_.Name)})
	Remove-PSDrive -Name TgtDS

   #Register all .vmx Files as VMs on the datastore
   foreach($VMXFile in $unregistered) {
  	  New-VM -VMFilePath $VMXFile.DatastoreFullPath -VMHost $ESXHost -Location $VMFolder -RunAsync


Line 9: I added a fail-safe, where I avoid that .vmx files that belong to already registered VMs, would be used again. This is done by adding the VMX-path of all registered VMs on the datastore to a hash table.

Line 10: The VMX-path is retrieved for each registered VM and added to the hash table

Line 13: To avoid the use of the HostDatastoreBrowser object, and make it more pure PowerCLI, I use the datastore provider that comes with PowerCLI. The big disadvantage of this is that the datastore provider is way slower than the HostDatastoreBrowser methods.

Line 14-15: With datastore provider the script retrieves all .vmx files that are not in a snapshot folder and that do not belong to a registered VM

Line 16: Remove the datastore provider

Line 19-21: Register all .vmx files that were found


  1. Christoph
    September 16th, 2015 at 13:18 | #1

    I’m quite new to scripting and maybe I made a mistake how I used it. It took me a while to figure it out but I had to make small change to make that script work in my invironment to find unregistered vms.

    Change in Line 15:

    where {$_.FolderPath -notmatch “.snapshot” -and $_.Name -like “*.vmx” -and !$registered.ContainsKey($_.Name)})

    where {$_.FolderPath -notmatch “.snapshot” -and $_.Name -like “*.vmx” -and !$registered.ContainsKey($_.DatastoreFullPath)})

    I made some more changes because I just need to know unregistered vms, I don’t want to register them.

  2. JHoSee
    August 31st, 2015 at 22:01 | #2

    Thank you so much for this script! It helped me recover over 500 VMs lost from inventory. Much appreciated…

  3. Almero
    August 22nd, 2014 at 10:30 | #3

    Hi Guys , I need to register certain VMs to DR Tests from CSV. Testing script before I run in real DR .

    I am having issues with VMFilePath varaible .

    My CSV DRVMs.csv >
    [LOCAL] TESTvm1/TESTvm1.vmx
    [LOCAL] TESTvm2/TESTvm2.vmx
    [FAKESAN1] TESTvm_onSAN/TESTvm_onSAN.vmx

    My Ps1 >
    add-pssnapin VMware.VimAutomation.Core

    $ESXHost = “”
    $VMXFiles = Import-Csv “C:\Elmo\Ps\Shop\DR\DRVMs.csv”

    Connect-VIServer $ESXHost

    ForEach ($VMXFile in $VMXFiles)

    New-VM -VMFilePath $VMXFile -vmhost $ESXHost

    But I get the following Error > I am missing something obvious here ๐Ÿ™‚

    New-VM : 2014/08/22 10:28:53 AM New-VM ‘@{[LOCAL] TESTvm1/TESTvm1.vmx=[LOCAL] TESTvm2/TESTvm2.vmx}’ is not valid datastore path.
    At C:\ELMO\PS\Shop\DR\ELMO Start DR VMs.ps1:27 char:16
    + New-VM <<<< -VMFilePath $VMXFile -vmhost $ESXHost
    + CategoryInfo : InvalidArgument: (:) [New-VM], InvalidArgument
    + FullyQualifiedErrorId : Common_DatastorePathHelper_ValidateDatastorePath_InvalidPath,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewVM

    • August 22nd, 2014 at 11:05 | #4

      Hi Almero,
      You seem to be passing 2 VMX files on the VMFilePath parameter.
      Can you check your CSV file ?

    • Almero
      August 22nd, 2014 at 11:14 | #5

      LucD , I got it working , sorry for hassle .

      Added VMXFILE to top of my CSV .
      Added this line , even though I still dont understand , but it all works now .

      $vms= $VMXFile.VMXFile
      New-VM -VMFilePath $vms -vmhost $ESXHost

  4. December 11th, 2013 at 09:59 | #7

    i’ve change the datastore (2nd line) to select all datastores.
    if it works – maybe give it as an option?
    $Datastores = @(Get-Cluster -Name “Cloud Cluster” | Get-VMHost | Get-Datastore | where {$_.Type -eq “VMFS” -or “NFS”} | % {$_.Name})


  5. kris
    August 9th, 2013 at 20:05 | #8

    Hi Luc
    How would you take input from csv file when csv file looks like this—-Thanks for your help in advance.
    0609455774-WinXPx32,[VM Datastore2] 0609455774-WinXPx32_1/0609455774-WinXPx32.vmx
    0187541347-Win7,[VM Datastore2] 0187541347-Win7_1/0187541347-Win7.vmx
    1240043273-IIS,[VM Datastore2] 1240043273-IIS_1/1240043273-IIS.vmx
    0663697505-win2k8mssql,[VM Datastore2] 0663697505-win2k8mssql_1/0663697505-win2k8mssql.vmx
    0504883356-Win7,[VM Datastore2] 0504883356-Win7_1/0504883356-Win7.vmx
    0692920522-IIS,[VM Datastore2] 0692920522-IIS_1/0692920522-IIS.vmx
    1612413179-Dev,[VM Datastore2] 1612413179-Dev_1/1612413179-Dev.vmx
    0829482277-mssql2005,[VM Datastore2] 0829482277-mssql2005_1/0829482277-mssql2005.vmx
    1443716454-win2k8mssql,[VM Datastore2] 1443716454-win2k8mssql_1/1443716454-win2k8mssql.vmx

    • August 9th, 2013 at 21:28 | #9

      Hi Kris, not sure I understand the question.
      This script looks for unregistered VMs, by searching for VMX files on the datastore(s).
      What is in the CSV file, a list of VMX files of unregistered VMs ?

      • Kris
        September 17th, 2013 at 00:19 | #10

        My question was that I have list of VM’s in a csv file and I want to extract vmx file path information for these specific vm’s in csvfile not for the whole datacenter.
        What I want to do is something like
        $vm= import-csv “c:\temp\vm1.csv -useculture | % {
        $vms=get-vm $_.”name”
        foreach($vm in $vms)
        {Get-View -ViewType virtualmachine | % { $_.Config.Files.VmPathName }}

        It does not read the vm name from .csv file and, I was wondering if you could help me in sorting this out.

  6. saito
    April 3rd, 2013 at 18:14 | #11

    when try to run script or create a new vm I get error:

    “New-VM The specified path is not correct. Element ‘inMaintenanceMode’ doesn’t exist.”

    I have tried many things but no chance.

    any idea?

    • April 3rd, 2013 at 22:56 | #12

      Hi Saito, could it be that the ESXi host or the datastore is placed in maintenance ?
      It could help to debug if I could see the content of the $VMXFile variable in the last foreach loop.
      Try changing that part like this, and let me know what the content of $VMXFile looks like.

      foreach($VMXFile in $unregistered) {
      New-VM -VMFilePath $VMXFile.DatastoreFullPath -VMHost $ESXHost -Location $VMFolder -RunAsync

  7. Randy
    August 14th, 2012 at 05:07 | #13

    I’m attempting migrate VMs (remove, re-register) from one vCenter to another (both share the same, multiple datastores) by iterating an array of VM names I supply using $arrVMNames = Get-Content (“C:\scripts\vmnames.txt”).

    The problem I’m having is saving the vmx file path to a variable and using it: New-VM -VMFilePath $VMPaths -VMHost $esxhost

    Can you elaborate on how to save & reuse the file path correctly in this manner?


  8. djr
    July 5th, 2012 at 18:37 | #14

    Hi, is there a way to randomize the host the vm’s are registered on. in your example they all get registered on the first node? unless I am mistaking… We have an 8 node cluster, so it would be great if it could register to any of the 8 nodes.

    • July 5th, 2012 at 19:22 | #15

      Hi djr, everything is poshible.
      Seriously, with the Get-Random cmdlet you can easily accomplish that.
      The code becomes something like this

      $Cluster = "MyCluster"
      $Datastores = "MyDS"
      $VMFolder = "MyFolder"

      $clusterObj = Get-Cluster -Name $Cluster

      foreach($Datastore in Get-Datastore $Datastores) {
      # Collect .vmx paths of registered VMs on the datastore
      $registered = @{}
      Get-VM -Datastore $Datastore | %{$_.Extensiondata.LayoutEx.File | where {$_.Name -like "*.vmx"} | %{$registered.Add($_.Name,$true)}}

      # Set up Search for .VMX Files in Datastore
      New-PSDrive -Name TgtDS -Location $Datastore -PSProvider VimDatastore -Root '\' | Out-Null
      $unregistered = @(Get-ChildItem -Path TgtDS: -Recurse | `
      where {$_.FolderPath -notmatch ".snapshot" -and $_.Name -like "*.vmx" -and !$registered.ContainsKey($_.Name)})
      Remove-PSDrive -Name TgtDS

      #Register all .vmx Files as VMs on the datastore
      foreach($VMXFile in $unregistered) {
      $ESXHost = Get-VMHost -Location $clusterObj | Get-Random
      New-VM -VMFilePath $VMXFile.DatastoreFullPath -VMHost $ESXHost -Location $VMFolder -RunAsync

      See also the 3th tip in my article in PowershellMagazine.

  9. Brent McDonald
    March 16th, 2012 at 20:15 | #16

    I get this error when adding VMs to a folder called _Templates. Each of my data centers have a folder called this, and they are both managed by the same vCenter. Is there away to work around this?

    New-VM : 3/16/2012 2:09:49 PM New-VM The specified parameter ‘Location’ expects a single value, but your name criteria ‘_Template
    s’ corresponds to multiple values.
    At C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\scripts\_add.ps1:20 char:12
    + New-VM <<<< -VMFilePath $VMXFile.DatastoreFullPath -VMHost $ESXHost -Location $VMFolder -RunAsync
    + CategoryInfo : InvalidResult: (System.Collecti…dObjectInterop]:List`1) [New-VM], VimException
    + FullyQualifiedErrorId : Core_ObnSelector_SelectObjectByNameCore_MoreResultsThanExpected,VMware.VimAutomation.ViCore.Cmdlets.Commands

    • March 19th, 2012 at 08:42 | #17

      Hi Brent,
      Yes, you can change line 3 into

      $VMFolder = Get-Folder -Name "MyFolder" -Location (Get-Datacenter -Name MyDC)

      That way, you will avoid having an array, but you will have the single folder with that name in that specific datacenter.

  10. February 21st, 2012 at 05:04 | #18

    LucD, I am having an issue with this script…

    The names that go into the registered are in the form
    [Volume1] vmname/vmname.vmx

    When later that is .containskey against the names that come out of the $unregistered, it doesn’t find a match where it should… the names it is trying to match the above to look like

    Which straight up
    [Volume1] vmname/vmname.vmx -ne vmname.vmx

    I have tried wildcards…but .containskey doesn’t seem to like that very much

    I might be missing something but I have checked all the possibilities I can think of…

    • Chris Brinkley
      September 24th, 2013 at 02:04 | #19

      Fixed by changing line 12 to this:
      Get-VM -Datastore $Datastore | %{$_.Extensiondata.LayoutEx.File | where {$_.Name -like “*.vmx”} | %{$registered.Add($_.Name.split(‘/’)[-1],$true)}}

      It’s comparing apples and oranges, I just changed apples to oranges by adding this bit:


  11. kenny
    August 13th, 2011 at 05:36 | #20

    This is fantastic!

    Doing migrations with data centers and srdf etc, this will work wonders!

    Thanks very much Luc!

  12. August 12th, 2011 at 13:59 | #21

    Luc, as always rising to the challenge!

    I didn’t even think of using the new functionality to “map” a drive to a datastore and then search within it.

    This is a fantastic addition and far simpler than delving into Get-View!

    Having MoRefs and Get-View is great to be able to access the entire API but it’s not really native PowerCLI and can be confusing. As the cmdlets are developed, I suppose more API functionality will be added to PowerCLI which just makes it easier to understand.

    PowerCLI just keeps getting better!


    • August 12th, 2011 at 15:02 | #22

      @Julian, thanks but it was you that used the VMFilePath parameter first ๐Ÿ™‚

      Btw the datastore provider is terribly slow compared to the HostDatastoreBrowser search.
      Will be better in the next build I hope.

  13. Shawn Cannon
    August 11th, 2011 at 19:49 | #23

    I just tried to run this on my NFS Datastore and it would not work. Do I still need to use your older VMX method to import from NFS Datastores?

    • August 11th, 2011 at 21:04 | #24

      Hi Shawn, something apparently went wrong during the copy of the code.
      I corrected that and tested against an NFS datastore. It seems to work correctly now.
      Let me know when you have the time to test on your side, if you also see success.

      The original Raiders script still has it’s value.
      It offers way more possibilities than this short script.

  1. September 14th, 2011 at 11:32 | #1
  2. February 24th, 2012 at 09:47 | #2
  3. February 11th, 2014 at 16:19 | #3
  4. June 8th, 2015 at 16:21 | #4