3

Background

I am creating a script, using PowerCLI, to perform mass OVF exports using VMware's ovftool. The script works by performing the following functions:

  • Through PowerCLI arguments, take in the vCenter address, naming scheme of the VMs to export, and where the OVFs should be exported
  • Connect to the vCenter instance
  • Put all VMs that follow the specified naming scheme into a list
  • Loop through the list and export each VM to an ovf using the ovftool and the built path to the VM

The Script: VMs-to-OVF.ps1

# Take in vCenter address, naming scheme of VMs to be exported, and where the OVFs should be exported

param (

[string]$vcenter = $(throw "

    vCenter address required.`n

    Example:`n

    .\VMs-to-OVF.ps1 -vcenter <192.168.1.200>`n

    .\VMs-to-OVF.ps1 -vcenter <vcenter.test.com>"),

[string]$vmNamingScheme = $(throw "

    VM naming scheme required.`n

    Example:`n

    .\VMs-to-OVF.ps1 -vcenter <vcenterIP/DNS> -vmPath </DATACENTER/vm/`n

    test/> -vmNamingScheme <test-vm-1>`n

    .\VMs-to-OVF.ps1 -vcenter <vcenterIP/DNS> -vmPath </DATACENTER/vm/`n

    test/> -vmNamingScheme <test-vm-*>`n"),

[string]$exportLocation = $(throw "

    Export location required.`n

    Example:`n

    .\VMs-to-OVF.ps1 -vcenter <vcenterIP/DNS> -vmPath </DATACENTER/vm/`n

    test/> -vmNamingScheme <test-vm-1> -exportLocation 192.168.1.100:\`n

    .\VMs-to-OVF.ps1 -vcenter <vcenterIP/DNS> -vmPath </DATACENTER/vm/`n

    test/> -vmNamingScheme <test-vm-*> -exportLocation X:\`n")

)

# Connect to vCenter

Connect-VIServer -Server $vcenter

# $VMs is an array of VM names that will be exported
# $vmNamingScheme gives the VM naming pattern we are looking for

$VMs = $(get-vm -name $vmNamingScheme | select name | format-list | out-string)
$VMs = $VMs.replace("Name : ","")
$VMs = $VMs.replace(" ","")
$VMs = $VMs.split("`n")
$VMs = $VMs|?{$_ -ne $VMs[1]}

# This loop iterates through the $VMs array and performs an OVF export, to the location
# specified in $exportLocation, for each VM name in the array
# $vmPath is comprised of the path to the VM and $VM holds the actual VM name

foreach ($VM in $VMs){
    if ($VMs -ne $null){ 

    # ***THIS SCRIPT ASSUMES THE get-vmfolderpath SCRIPT IS IN THE SAME DIRECTORY AS ITSELF***
    # get the folder path of the VM using the get-vmfolderpath script (this will align with
    # the path in vSphere Folders and Templates view)

    $vmPath = "get-vm -name $VM | .\get-vmfolderpath.ps1"
    &$vmPath

    # ***THIS SCRIPT ASSUMES THE DEFAULT INSTALL PATH FOR THE ovftool PROGRAM
    # run the ovftool command with the proper arguments

    &'C:\Program Files\VMware\VMware OVF Tool\ovftool.exe' vi://$vcenter$vmPath$VM $exportLocation
    }
}

Accompanying Script: get-vmfolderpath

param(
    [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
    [string]$folderid,
    [switch]$moref
)

$folderparent=get-view $folderid

if ($folderparent.name -ne 'vm'){
    if($moref){
        $path=$folderparent.moref.toString()+'\'+$path
    }
    else{
        $path=$folderparent.name+'\'+$path
    }
    if ($folderparent.parent){
        if($moref){
            get-vmfolderpath $folderparent.parent.tostring() -moref
        }
        else{
            get-vmfolderpath($folderparent.parent.tostring())
        }
    }
}
else {
    if ($moref){
        return (get-view $folderparent.parent).moref.tostring()+"\"+$folderparent.moref.tostring()+"\"+$path
    }
    else {
        return (get-view $folderparent.parent).name.toString()+'\'+$folderparent.name.toString()+'\'+$path
    }
}

Errors

The ovftool command built in each iteration of the for loop will work if you copy and paste the text into the PowerCLI console, but not when run directly from the script. The following errors are produced when the custom ovftool command is run from within the script:

The term 'get-vm -name VM-NAME | .\get-vmfolderpath.ps1' is not
recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify
that the path is correct and try again.At
C:\Users\username\Desktop\VMs-to-OVF.ps1:85 char:4
+         &$vmPath
+          ~~~~~~~
    + CategoryInfo          : ObjectNotFound: (get-vm -name CA...mfolderpath.p
   s1:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Things I Have Checked:

  • The output of "get-vm -name vm_name | .\get-vmfolderpath.ps1" run directly in the PowerCLI console returns the proper VM path
  • All variables output the expected values
  • If the exact ovftool command generated in the script is run in the PowerCLI console then it will properly export the VM
Chris Willis
  • 65
  • 10
  • Why are you using `ovftool` instead of `Export-vApp`? What's the exact value of `$vmpath` when this fails? You are also seeming to have problems with arguments to things other than the `ovftool` call. – Etan Reisner Jun 12 '15 at 16:47
  • I made a slight modification to the post. I am now simply trying to construct the path of the VM using: `$vmPath = "get-vm -name $VM | .\get-vmfolderpath.ps1"` `&$vmPath` The error I am getting is slightly different here too. I have updated the Errors block in my post. Also, I wasn't aware that Export-vApp can be used to export OVFs. Is that true? – Chris Willis Jun 12 '15 at 17:33
  • The value of `$vmPath` is a command string. Not a command block, not the result of that command, etc. Do you want to be running that command at that point and storing the result in `$vmPath`? If so then you don't want the quotes. If you want to create a command block then I believe you want to replace the quotes with curly-braces. – Etan Reisner Jun 12 '15 at 17:53
  • There's a `-Format` flag to `Export-VApp` which accepts `OVF` and `OVA` with `OVF` being the default. But an `OVA` is just a tar archive so even if it could only do `OVA` you could always just extract it afterwards manually. – Etan Reisner Jun 12 '15 at 17:57
  • If I use the curly braces I get the error: `Get-VM VM with name 'VM-NAME' was note found using specified filter(s).` Also, if I use the command: `Export-VApp -Destination $exportLocation -VM $VM` I get the error `Export-VApp: Could not find find VirtualMachine with name VM-NAME`. I am starting to wonder if I am passing the name of the VM wrong or something. I am just taking guesses now though. – Chris Willis Jun 12 '15 at 18:29
  • What is `VM-NAME`? Can you use it with `Get-VM` and get the vm you expect? The `-VM` flag to `Export-VApp` expects a vm object not a string. So I don't quite know what that error in that situation means (unless you used a view perhaps and the vm moved/changed from under the view somehow). – Etan Reisner Jun 12 '15 at 18:32
  • Find a VM with a unique name. Call `Get-VM -Name "Unique VM Name"`. Does it work? If it does can use run `Export-VApp -Name "Unique VM Name"`? Can you use `$vm = Get-VM -Name "Unique VM Name"; Export-VApp -VM $vm`? I don't know what you are using for the names of your vm:s in your tests at the moment but it is just the name of the vm, not a path, not a moref, etc. – Etan Reisner Jun 12 '15 at 18:33
  • VM-NAME is just me censoring the actual name of the VM. It is just a string and not the contents of a get-vm. When I run `Get-VM -Name VM-NAME` it responds with the proper output. When I run `Export-VApp -VM VM-NAME -Destination Y:/` it will export the VM as an OVF to the Y drive. – Chris Willis Jun 12 '15 at 18:47
  • `-VM` takes a vm object not a name. But `Export-VApp` also have a `-Name` argument if you want to use that. – Etan Reisner Jun 12 '15 at 18:53
  • When I run `Export-VApp -Name $VM -Destination $exportLocation` from within the script I get `Export-VApp: Cannot bind parameter 'Name' to the target` If I run the same thing outside the script it successfully exports the VM. I have also tried to run the same command by first specifying `$VM = get-vm -name $VM` to get a VM object and using the -VM flag with Export-VApp. Neither way works. Thoughts? – Chris Willis Jun 12 '15 at 19:02
  • Was that `-Name [vmobject]`? Because that won't work. I'm having a hard time following what you are actually trying because your scripts are complicated and your variable name usage isn't consistent. – Etan Reisner Jun 12 '15 at 19:07
  • When I specify `$VM = get-vm -name $VM` before the Export-VApp command I would assume this converts $VM to a VM object? However, when I don't make that specification $VM is a string within the list $VMs, which is determined by this line in the script: `$VMs = $(get-vm -name $vmNamingScheme | select name | format-list | out-string)` before I start splitting and parsing it. – Chris Willis Jun 12 '15 at 19:36
  • Stop focusing on names. `Get-VM` gets you a list of vm objects. Those are *exactly* what you need to operate on to export. Just use them. Instead of massaging the names output into something vaguely usable again. – Etan Reisner Jun 12 '15 at 19:38

1 Answers1

0

Just closing the loop on this. I found a solution to my issue. My guess is that something is lost when you manipulate the list of VMs returned by the Get-VM cmdlet. In the VMs-to-OVFs script, if all of the "$VM =" lines are replaced by the single line below, then that information is retained.

$VMs = get-vm -name $vmNamingScheme

In the for loop use the ".Name" attribute to pass each individual VM object to the get-vmfolderpath script.

$vmPath = get-vm -name $VM.Name | .\get-vmfolderpath.ps1

Chris Willis
  • 65
  • 10