Home > PowerCLI, Powershell, vCenter, VMware > Adding .VMX Files to vCenter Inventory with PowerCLI gets even easier

Adding .VMX Files to vCenter Inventory with PowerCLI gets even easier

Update: I’ve since found out that mattboren actually found out about this before me and posted something on the VMware communities which I missed. Well found Matt.

A fairly common request is to be able to register existing VMs in a datastore in the vCenter inventory.

This can be a life saver if you have had storage issues and have had to present a backup copy of a datastore which has a different name and need to add the VMs to the inventory, a very laborious process if done manually with right-click Add to Inventory.

This can also be useful in a business recovery process when you need to add VMs that have been mirrored by storage replication over to a secondary site and you need to add them into your inventory.

PowerCLI guru, Luc Dekens has developed a fantastic script called Raiders of the Lost VMX which searches a datastore for .VMX files, and adds them to the vCenter inventory. This script has been updated over the years with even more clever functionality.

Adding the VM to the inventory involved running the RegisterVM_Task Method against the VM Folder in VC.

By accident I discovered there’s actually an updated easier way to add existing VMs to the inventory if you have the .VMX file path. I’m not sure when this was added to PowerCLI but I found it when writing a script to add a new VM.

The New-VM cmdlet now has a new parameter -VMFilePath which allows you to specify a path to the virtual machine you want to register.

This is in the format: [mydatastore] SERVER01/SERVER01.vmx

You can therefore use New-VM to add an existing VM to the VC inventory without having to invoke any MoRef!

In its simplest form:

$VMXFile = "[mydatastore] SERVER01/SERVER01.vmx"
$ESXHost = "VMHost01.local
New-VM -VMFilePath $VMXFile -VMHost $ESXHost

You can also use the rest of the New-VM parameters to specify a VM Folder Location and a Resource Pool.

$VMXFile = "[mydatastore] SERVER01/SERVER01.vmx"
$ESXHost = "VMHost01.local"
$VMFolder = Get-Folder "Prod Servers"
$ResPool = "Servers_Prod1"
New-VM -VMFilePath $VMXFile -VMHost $ESXHost -Location $VMFolder -ResourcePool $ResPool

Even in a single line:

New-VM -VMFilePath "[mydatastore] SERVER01/SERVER01.vmx" -VMHost "VMHost01.local" -Location (Get-Folder "Prod Servers" -ResourcePool "Servers_Prod1"

You can combine this with a simple yet powerful search to go through a set of datastores, find the .VMX files and add them to your Inventory

If you are using NFS and can see Snapshot folders when browsing the inventory you want to exclude these from the search results as they are storage snapshot copies of your VMs, a task easily done in PowerCLI.

$Cluster = "LON_PROD1"
$Datastores = "lonservers*"
$VMFolder = "LondonAppServers"
$ESXHost = Get-Cluster $Cluster | Get-VMHost | select -First 1

foreach($Datastore in Get-Datastore $Datastores) {
   # Set up Search for .VMX Files in Datastore
   $ds = Get-Datastore -Name $Datastore | %{Get-View $_.Id}
   $SearchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
   $SearchSpec.matchpattern = "*.vmx"
   $dsBrowser = Get-View $ds.browser
   $DatastorePath = "[" + $ds.Summary.Name + "]"

   # Find all .VMX file paths in Datastore, filtering out ones with .snapshot (Useful for NetApp NFS)
   $SearchResult = $dsBrowser.SearchDatastoreSubFolders($DatastorePath, $SearchSpec) | where {$_.FolderPath -notmatch ".snapshot"} | %{$_.FolderPath + ($_.File | select Path).Path}

   #Register all .vmx Files as VMs on the datastore
   foreach($VMXFile in $SearchResult) {
      New-VM -VMFilePath $VMXFile -VMHost $ESXHost -Location $VMFolder -RunAsync
   }
}
  1. August 11th, 2011 at 18:23 | #1

    Nice find, great script.

  2. WoodITWork
    August 12th, 2011 at 10:42 | #2

    @LucD
    Thanks, Luc, that’s great coming from the master himself!

  3. May 17th, 2013 at 08:17 | #3

    Thanks for sharing this script, saved me some time creating it myself 🙂
    One minor comment, the VM’s are not placed in the folder because the variable name is from ($Folder instead of $VMFolder).

    OLD:
    New-VM -VMFilePath $VMXFile -VMHost $ESXHost -Location $Folder -RunAsync

    NEW:
    New-VM -VMFilePath $VMXFile -VMHost $ESXHost -Location $VMFolder -RunAsync

    • WoodITWork
      May 19th, 2013 at 22:55 | #4

      Thanks for spotting that, corrected.

  4. Travis
    May 21st, 2013 at 21:14 | #5

    Hi — First let me say thanks I’ve been testing this script for a good chunk of the day and it’s working great! I’ve added a few lines to do the registering on multiple hosts to split the load, since I’m anticipating registering hundreds of guests. As an extremely novice scripter I do have one question.

    When I comment out the actual registering of the VM’s and replace it with this (to get a list of the VMX files it would register):

    foreach($VMXFile in $SearchResult) {
    #New-VM -VMFilePath $VMXFile -VMHost $ESXHost -Location $VMFolder -RunAsync
    $VMXFile
    }

    I get duplicate VMX’s listed. I believe this is because we have various files named *.vmxf (linked VM files) or *.vmx~ (no clue what these are). I’m wondering if there is a way to specify it so only files ending EXACTLY in “*.vmx” should be registered. (I’ve been messing around with some $_.File -not “*.vmxf” attempts to do some hardcoded exclusions but no luck! hah)

    PS – Also get a few blank lines that list just the $DataStorePath, which are from Datastores that contain only VMDKs on them (from machines with disks on multiple datastores). I believe this is less of a concern, but curious all the same.

  5. Tim Gray
    November 15th, 2013 at 22:06 | #6

    I cam getting an
    “Exception calling “SearchDatastoresSubFolders” with “2” argument(s) error when I try to run this script.
    Does this script work on a ESXi 5.0 host and vSphere 5.0?

  6. Gavin
    August 2nd, 2014 at 04:29 | #7

    You are my hero, that’s all I need to say!

    • Julian Wood
      August 22nd, 2014 at 23:13 | #8

      Thanks, glad it was useful.

  7. ranvir
    October 27th, 2015 at 17:03 | #9

    @Tim Gray
    Hi Tim…I am getting the same Error, I wonder did you manage to resolve this? Can you please let me know ,How?

  8. Manoj
    May 30th, 2016 at 14:00 | #10

    Hello There,

    I am very new to scripting and not able to find issue. Need your help to fix this.

    I am working on a script to migrate a set of VM’s from Virtual Center 5.0 to to 5.5. When I am running below script getting below error.

    New-VM : Cannot validate argument on parameter ‘VMFilePath’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
    At C:\Users\msharm44\Desktop\VM Migration Scripts\VM MIG about to be final.ps1:82 char:24
    + New-VM -VMFilePath $vm.Path -VMHost (Get-Cluster $DestCluster | G …
    + ~~~~~~~~
    + CategoryInfo : InvalidData: (:) [New-VM], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewVM

    …………………………………………………Scripts starts below………………………………………………….

    $SourceVC = “Server1.domain.COM”
    $DestVC = “Server2.domain.COM”
    $DestCluster = “Server2_Pod01_Development_06”
    $DestFolder = “VMs Windows”

    #$UserFile = “User.fil”
    #$PassFile = “Pass.fil”

    $cred = Get-Credential

    # Functions ———————————————————————————

    function Log ($text) {
    $stamp = (Get-Date).ToString(“HH:mm:ss.fff”)
    Write-Host “$stamp | $text”
    }

    # Business part of script ——————————————————————-

    Start-Transcript -Path VM-Move-VC.log -Append

    # Disconnect any existing VI Server sessions
    if ($DefaultVIServers.Count) {
    Log(“Disconnect existing vCentre server connections…”)
    Disconnect-VIServer -Server * -Force -Confirm:$false
    }

    # Connect to source VC
    try {
    Log “Connecting to $SourceVC”
    $VCconn = Connect-VIServer -Server $SourceVC -Credential $cred -errorAction Stop
    } catch {
    Log(“Unable to connect to vCentre – ” + $_)
    Exit
    }

    # Get list of VMs to move
    $VMs = Get-Content “C:\tmp\source.csv”
    Log “VMXs to reregister…”
    $VMs2Move = @()
    foreach ($vm in $VMs) {
    $vm2move = “” | Select Name, Path
    $vm2move.Name = $vm.name
    $vm2move.Path = $vm.ExtensionData.Config.Files.VmPathName
    $VMs2Move += $vm2move
    Log ($vm2move.Name + ” ” + $vm2move.Path)
    }
    #$VMs | Get-View | %{$_.Config.Files.VmPathName} | Sort

    # Unregister VMs
    foreach ($vm in $VMs) {
    Log (“Unregister ” + $vm.Name)
    Remove-VM -VM $vm -DeletePermanently:$false -Confirm:$false
    }

    Disconnect-VIServer -Server $VCconn -Confirm:$false

    # Connect to destination VC
    try {
    Log “Connecting to $DestVC”
    $VCconn = Connect-VIServer -Server $DestVC -Credential $cred -errorAction Stop
    } catch {
    Log(“Unable to connect to vCentre – ” + $_)
    Exit
    }

    # Register VMs
    foreach ($vm in $VMs2Move) {
    Log (“Register ” + $vm.Name)
    New-VM -VMFilePath $vm.Path -VMHost (Get-Cluster $DestCluster | Get-VMHost | Get-Random) -Location (Get-Folder $DestFolder)
    }

    Disconnect-VIServer -Server $VCconn -Confirm:$false
    Stop-Transcript

  1. September 14th, 2011 at 10:33 | #1
 

Time limit is exhausted. Please reload the CAPTCHA.