VMware Cloud Community
angoletti1
Contributor
Contributor

HowTo search for all VMX files in all datastores and register them into VC?

Hi,

I want to search in all datastores for VMX files and if the VM for each founded file is not registered in VC, register it.

How can this be done by the powershell using the easiest way?

Thanks in advance

Chris

Reply
0 Kudos
56 Replies
LucD
Leadership
Leadership

I'm not sure this is the easiest way but at least it works for me.

Some notes:

1) the script uses 2 methods from the SDK

a) SearchDatastoreSubFolders_Task: to find all .vmx files in a specific datastore

b) RegisterVM_Task: to register a guest from it's .vmx file

2) Contrary to a sample in the FAQ the RegisterVM_Task method seems to require a resourcepool

3) The guests will be registered in a specific datacenter and cluster in that datacenter

4) The folder where the guests will be registered is the hidden folder "vm"

5) the pool where the guests will be registered is the hidden resourcepool "Resources"

6) the RegisterVM_Task method will fail if there is already a registered guest with the same name

7) the name of the guest that will be registered is the filename of the .vmx file. For example for PC1.vmx it will be guest PC1

$folder = Get-View (Get-Datacenter -Name <datacenter-name> | Get-Folder -Name "vm").ID
$pool = Get-View (Get-Cluster -Name <cluster-name> | Get-ResourcePool -Name "Resources").ID
$guestname = [regex]"^([\w]+).vmx"

$esxImpl = Get-VMHost -Name <VMHost-name> 
$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 = "[http://" + $ds.Summary.Name + "|http://" + $ds.Summary.Name + "]"
  
  $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}

  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){
      $vmx = $file.FolderPath + $file.File[0].Path
      $res = $file.File[0].Path -match $guestname
      $folder.RegisterVM_Task($vmx,$matches[1],$FALSE,$pool.MoRef,$null)	
    }
  }
}


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

Reply
0 Kudos
bassarchist
Contributor
Contributor

Thanks for the pointer LucD! But I seem to be having some difficulties ...

First of all, how can I specify the DataStore to parse for VMX's? In my case, I've got an iSCSI LUN -- the script seems to only pull local storage devices.

Second, it doesn't appear that $file.File[0] ever gets populated in the bottom half of your suggestion.

I get this error every time this array is referenced:

Cannot index into a null array.

(line numbers differ -- i was playing with some debug output)

$vmx = $file.FolderPath + $file.File[0].Path

I've got some things to try to fix these issues, but if you can replicate my error and have ideas, I'd be most grateful! Smiley Happy

Thanks again!

-Mars

(edit)

Oh yeah, I forgot to mention -- I 'sploded out your script into my own formatting, so the line numbers would be goofy in the first place. When I copy'n'paste your solution, I get this error (which I solved by using my own formatting):

Parsing error:

At line 1, position 345

Unexpected token 'in' in expression or statement.

It's referring to this:

foreach($dsImpl in $dsBrowser.Datastore){ (.....)

Apparently, it doesn't like the 'in' used in that foreach ... not sure what that was all about.

Reply
0 Kudos
bassarchist
Contributor
Contributor

DUH! I just had an epiphany.

It's not that your script pulls the 'wrong' storage device, it's that it specifically references a VMHost -- which you can't do when you want to parse VMX's located on an iSCSI Lun! It hadn't even occurred to me until i noticed the VMHosts and the Networked storage was listed in parallel in the datastore browser in VIC 😛

We don't have any VMX's on our VMHost:storage1, which might explain why that array never gets populated. I think I was concentrating on the wrong part of the script ...

Ok SO ....

I'll try to mess around with that Get-iSCSILun function i saw you post somewhere else. Any pointers? 😛

Reply
0 Kudos
LucD
Leadership
Leadership

There were 2 problems in the script.

1) the forum SW doesn't like square brackets.

To avoid the reformatting I have attached the script.

2) the script had a bug when an empty datastore was encountered.

That problem should be fixed now by the updated if-statement in line 33.

Concerning your remark about the iSCSI LUNs, that should be of no importance to the script.

The script scans the datastores, irrespective if they are on SAN, iSCSI, NFS or local disks.


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

Reply
0 Kudos
bassarchist
Contributor
Contributor

Brilliant!

I see that it's correctly locating our network storage device, and I see the correct number of tasks sent to VIC, but now I'm getting this error:

In VIC, the status of each of the tasks reports " " is invalid or too long." (yeah, the status begins with a double-quote)

In the script editor under locals, i've located this:

$file.DynamicType -- Cannot process argument because the value of "obj" is null. Change the value of argument "obj" to a non-null value."

$file.DynamicProperty -- Cannot process argument because the value of "obj" is null. Change the value of argument "obj" to a non-null value."

$file.File[0].DynamicType -- Cannot process argument because the value of "obj" is null. Change the value of argument "obj" to a non-null value."

$file.File[0].DynamicProperty -- Cannot process argument because the value of "obj" is null. Change the value of argument "obj" to a non-null value."

These values don't appear directly accessible via the script, are these values returned by the SDK functions you mentioned? Or, rather, null returns of an SDK function? 😛

Setting any of those to "1" didn't seem to help either, except in doing so, I only received the error on $file.DynamicType and $file.DynamicProperty. Does that make sense?

I figured it was worth a shot 😛

So to reiterate -- the script locates all the vmx's properly, sends the task to VIC, but it looks like there is a bit of data missing ...

(edit)

Oh wait .. I've received that error on more than just those four keys ... on second glance, it looks like those errors are to be expected because some of those values are useless. For example, for the variable $searchspec, everything except $searchspec.matchpattern gets the same error mentioned above. It's obvious in the script that .matchpattern is the only one we are explicitly defining ... so any other ideas on why VIC doesn't like the task?

(edit again twice here sorry)

Ok I've officially really confused myself. My head hurts.

I noticed that the value of $vmx at the end of the script had the path up to the vmx, but did not include the filename! After some playing with it, VIC did in fact add one VM, with the name "cp_vms", which just happens to be the name of the resource pool they are supposed to be part of. Obviously, when it tried to add all the other vms, it errored with "The name 'cp_vms' already exists.

So I started over fresh with your script again to see what I could do.

So the first thing that confused me was that two variables were defined with different names. It looks like PowerTools will cover the user in this case, and just define the values for each variation of the same variable name. In this case, $searchspec, $searchSpec, $datastorepath, and $datastorePath were all defined, all with the same values according to the 'locals' section. I tried cleaning that up ... but it didn't seem to help.

Also, what is $res for? I see it defined, but it's not utilized anywhere. Is it used by $folder.RegisterVM_Task() by chance? Am I trying too hard?!? 😛

And one last thing .. the values I changed to fit our environment were: VIserver, the vmhost, the name of the datacenter, the name of the cluster, and the name of the resource pool. I did not change the name of the folder, because I wasn't really sure how this part was relevant. I tried running a few variations from the command like of what I thought that value 'should' be, but failed miserably 😕

Does that value need to be changed as well?

Thanks again for any assistance ... sorry to be such a newb, I'm trying! 😛

Reply
0 Kudos
LucD
Leadership
Leadership

I'll start from your last edit.

1) $vmx is indeed the pathname to the VMX file.

That is the first parameter to the RegisterVM_Task method.

There is no need to specify the VMX filename with the RegisterVM_Task. It will look for it in the folder.

2) In PowerShell variable names are case-insensitive.

Although I admit it's not good coding practice to use that "feature". You got me there Smiley Wink

3) The variables $res is indeed not used because I don't really want it but it is to avoid that the result of that line is displayed on screen.

The key is the "-match" operator in the same line.

This will use a regular expression (in $guestname) on the variable $file.File[0].

The "-match" operator stores the matching results automatically in the array $matches.

I use that regex construct to get the VM name from the VMX filename.

That is the 2nd parameter to the RegisterVM_Task method.

4) The values you need to change for your environment are now all indicated with triangular brackets.

I have updated the script in my previous post.

You do not need to change the folder ("vm") and the resource pool ("Resources").

These are predefined objects present in every VC (but not visisble in the VI CLient).

I hope this will get you further.


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

Reply
0 Kudos
bassarchist
Contributor
Contributor

LucD -- You are invaluable to this community Smiley Happy

So I had another Aha! moment. A couple, actually.

I realized that your regexp looks for a word and an extension, from the beginning of the pathname. That was my first problem -- we've got a vm that we clone from called "Base Install.vmx". "Install.vmx" was all that matched this expression.

Then I realized that because of the way we are trying to populate this environment (explanation below), I had sixty folders, each with their own pathnames, but all of the files were also called "Base Install.vmx". Thats why I got that error sixty some times -- it was trying to add all my different VMs with the same name, which is not what it should have been!

I ran into this issue because I'm a novice VMWare user taking on tasks that are bigger than my understanding. But thanks to you, I think I know enough to do it "the right way".

The story:

We used to "clone to new vm" from an existing vm we called "Base Install". That worked, because every time we cloned, VIC changed configurations, generated new mac addresses, changed filenames, etc. But now we've built up sixty-some VMs, and need to get all of them over to a new environment, with minimal downtime. I realized that I couldn't script "new vm from clone", only "new vm from template". But I tried to make our old procedure work (partially because I was instructed to), by writing a bash script to copy from Base Install a bunch of times, changing the pathname as it went. Then I'd have had to write another script to rename all the VMX files and change all the configuration settings. Then I'd use the script you provided here to add everything from inventory. But thats kind of a janky work around, when I could instead just create a template, and script adding new VMs from template, which would automatically register them under whichever ESX server I scripted.

The crux:

I'm not entirely confident I can make a script like that work, due to my inexperience. However, I'm gonna try :smileygrin:

So thats basically what happend. Now I need a script that will clone from a template multiple times, changing the pathname and filename each successive iteration. As I understand, this will automatically register the newly created VM, right? It sounds simple enough, especially with the "new-vm" switches. I'll give it a shot on my own, but any input/examples/solutions are certainly welcome!

Thanks again to all the help here, I'm learning more than I knew I didn't know, which is awesome Smiley Wink

Also, if there is a simpler way of doing what I'm trying to do, I'd definitely like to know! Thanks again!

Reply
0 Kudos
LucD
Leadership
Leadership

You could give my GPS application a try (see ).

It does in fact what you want (and a bit more).

Since the configuration data is stored in an XML file it shouldn't be too hard to create the file for cloning sixty VMs from within a file editor.

I shall be publishing a newer version of the application shortly.


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

Reply
0 Kudos
bassarchist
Contributor
Contributor

Most excellent! Thats a tool we can certainly put to good use!

Naturally, I'm having some issues getting it to work. Here's what I've done so far:

  • Sysprep files (sysprep.exe and setupcl.exe) exist in c:\sysprep

  • Our BaseInstall image has been converted to a Template

  • GPS is able to locate and communicate with all the moving parts (datacenters, datastores, folders, etc are all found, commands are successfully submitted to VIC)

  • XML files are created, saved, and loaded properly

  • Tried configuring and not configuring OS options, just in case GPS was having issues communicating with Sysprep ...

Using VIC/VC 2.5.0, ESX 3.5.0 (the documentation refers to 3.5 U2, however), PowerShell 1.0, not sure which build

However, when I submit the job, VIC logs "The operation is not supported on the object". Furthermore, PowerShell prints some errors to standard out:

Get-Template: Template with the name 'Virtual1-1' not found, using the specified filter(s).

Subsequently, a bunch of other stuff breaks too.

At this point, I guess I don't fully understand how GPS is supposed to work -- the Virtual1-1 was a guest I added, that I wanted to be created from the template selected in the interface. Is this not how the tool is used?

Reply
0 Kudos
LucD
Leadership
Leadership

1) The sysprep files should not be stored in C:\sysprep

See the Basic System Administration guide, appendix B "Installing the Microsoft Sysprep Tools".

2) GPS v2 should work with with ESX 3.5 and VC 2.5 as well.

I only tested the published version with U2

3) Is " Virtual1-1" a template in your VC ?

From the message it looks as if it is not a template.

The tool creates a new guest (name defined in the field Computername under OS Options).

The template from which the new guest is deployed is selected in the drop-down list Template under Guest Options.

Is "Virtual1-1" in the list when you do a Get-Template from the VITK prompt ?


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

Reply
0 Kudos
bassarchist
Contributor
Contributor

1) I see! I was using Microsoft's documentation for a single client. Oops. Ok, all the files are where they need to be on the VC Server, and I've confirmed by deploying a template with OS customizations successfully.

2) I hear there was quite the snafu with U2 😛 Is that all cleared up now?

3) No, Virtual1-1 is not a template on my machine, that was supposed to be the target VM's name, which is why i'm confused. The template is called "BaseInstall", and does display correctly, after the tools successfullylocates all the other information (datastore, resource pool, vmhost, etc). It appears that when the task is submitted, GPS gets confused and wants to use the target name as the template. At least, thats what my untrained eye catches. Grr. What can I do to debug?

Reply
0 Kudos
benbjamin24
Enthusiast
Enthusiast

hey everyone,

I was having a problem with the -match expression in this script, it was giving me the following error.

Cannot index into a null array.

At line 36, position 51

$folder.RegisterVM_Task($vmx,$matches[1],$FALSE,$pool.MoRef,$null)

but i found a workaround in the meantime that seems to work fine for the moment.

just changed the line

$res = $file.File[0].Path -match $guestname

to

$res = $file.File[0].Path

$res = $res.TrimEnd(".vmx")

and that seemed to solve my problem.

thanks for the script.

Ben

Ben VCP
Reply
0 Kudos
artist-06
Contributor
Contributor

Hi all,

I wanted to share my experience with register vm script and powercli. I have been playing with powercli all week. The register-vm automation task pulled me to this area. I have given a VC with loads of orphaned VMs. Resolution was to remove from inventory and readd to inventory which was a painful repetitive task.

I installed VI Toolkit, powercli etc and then I was amazed what things could be done with Powercli. I could not get this register script work to start with. I finally edited these lines at the beginning to my VCs values below;

Connect-VIServer -Server localhost

$folder = Get-View (Get-Datacenter -Name Cristie | Get-Folder -Name "vm").ID

$pool = Get-View (Get-Cluster -Name Cristie | Get-ResourcePool -Name "Resources").ID

$guestname = "^(+).vmx"

$esxImpl = Get-VMHost -Name 10.5.5.80

localhost: Script running in VC

Datacenter name "Cristie", Cluster Name is "Cristie", Host Name is "10.5.5.80"

To resolve the main problem (orphaned VMs), I wrote a tiny script to remove all the VMs which were PoweredOff. Note you cannot PowerOn Oprhaned VMs.

remove-vm script is here;

Connect-VIServer -Server localhost

get-vm -name *| where{$_.PowerState -eq "PoweredOff"}

foreach($i in $vms)

{

Write-Host "Removing VM";

remove-vm -vm $i -Confirm:$false

Write-Host "$i vm is removed";

}

I watched all my Powered Off VMs disappearing in Vsphere Client and then ran the Register-VM script which registered allremoved VMs back again.

Thanks for the community.

I hope someone else finds this helpful and not waste so much time like I did.

Burak Uysal

Reply
0 Kudos
frdgn
Contributor
Contributor

Hi Luc,

I find your script to be an invaluable example.I run into a problem reregistering VM's from a different datastore than the original one though.

Here is the situation: We ran into serious trouble with our SAN storage System recently and decided to run remote copy jobs to a different Storage group to be able to restart machines from there.

I've written a script to shut down the machines in question, remove them from inventory, activate the Lun's on the remote storage and rescan the hba's on my esx servers with resignaturing in place.

Result is with slight modifications to your script I'am able to search the new Lun's for my VM's. So far so good, but when I'm trying to register these copies registration fails because there is still a record of the sanfs uuid in the vmx file.

I know i can register these machines from an esx server using the local filepath through vmware-cmd -s register, I just wonder if there is a way to do this through PowerCli as well.

Greetings Frank

Reply
0 Kudos
LucD
Leadership
Leadership

Thanks Frank.

Isn't there a question (about the move or copy thing) when you try to register the VMX file ?

Did you try to register such a VMX file from the VIC ? And did this produce a question ?

And what was the exact error you got when registering the VMX file from the script ?


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

Reply
0 Kudos
frdgn
Contributor
Contributor

Hi Luc,

Didn't expect an answer that quick, I 'm at home now so I can't give you the exact wording.

There are no questions about copying or moving even from the VIC

But the error message relates to the san://uuid. Registering from the VIC gives me the same error too. So I'm not really surprized because basically you are using VC methods as well.

What I'm really looking for is a seamless way for "emergency services" to fail over without expert guidance, so if there is a way to do hthis through PowerCli I'd be happy. If not I'll have to find a way to register the VM's over ssh or remotecli and not compromise the passwords

geetings

Frank

Reply
0 Kudos
smccreadie
Enthusiast
Enthusiast

Hello,

Im trying to use the register-vmx function outlined in this thread and i have ran into an issue. The function works as advertised but in my case im trying to search through multiple datastores and register all vmx files found. Where im having trouble is loading the list of datastores to search through into the function. I have the list of datastores as a text file. I tried a foreach loop with some success but it seems to bring in the datastore names with trailing white spaces or something that prevents it from working correctly. Has anyone tried this with this function? Here is a snippet of the foreach loop i tried:

$dstores = "C:\Scripts\PHXDRFailover\FailedoverDatastores.txt"

function Register-Vmx{

Get-Content $dstores | ForEach-Object {

$_ = $_.Trim()

This seems to work, it even appears to remove the trailing white spaces from each object, but the script still fails to search through the datastores. If I replace the value of $dstores with the Datastore name manually and run the script, it works. This leads me to believe that somewhow there is a formatting issue with teh pipeline objects created by the foreach cmdlet. Any ideas? Thanks in advance.

Sean

Reply
0 Kudos
smccreadie
Enthusiast
Enthusiast

Sorry guys i think i meant to post this question in this thread, which is related to the same function that LucD wrote. Thanks

Sean

http://communities.vmware.com/message/1328192

Reply
0 Kudos
LucD
Leadership
Leadership

I would not place the loop over the datastores inside the Register-Vmx function.

Put that in the main program

function Register-Vmx{
	param($vcname, $cluster ,$dsname)
        ......
}

$dstores = "C:\Scripts\PHXDRFailover\FailedoverDatastores.txt"

Get-Content $dstores | ForEach-Object {
   $dsname = $_.Trim()
   Register-Vmx "MyVCName" "MyClusterName" $dsname
}


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

Reply
0 Kudos