VMware Cloud Community
emcclure
Enthusiast
Enthusiast

Importing/Exporting OVF's

So I have a couple of separate scripts that Import and Export OVF's which is not the issue.  I've now been asked if I can do both based on a folder.  From what I've been asked they want me to write a script that will allow someone to select a folder or perhaps multiple VM's in vCenter and export them all as OVF's.  They also want to be able to import as well from a folder, but I'm thinking that unless I somehow have the export script put every OVF file for every VM that's exported it wouldn't be possible.  Is any of this possible?  Or is it just too convoluted and just opening multiple PowerCLI windows a better way to go?

Tags (3)
0 Kudos
41 Replies
LucD
Leadership
Leadership

When you use Export-Vapp with the CreateSeparateFolder switch, you should end up with different subfolders.
Or am I missing the point?


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

0 Kudos
emcclure
Enthusiast
Enthusiast

Nope not missing the point.  Looks like this is something that can't really be done with importing though?

0 Kudos
LucD
Leadership
Leadership

I think you can.
When all exports were done to separate subfolders, you could do something like this to import them one by one.
First get all subfolders, then in each subfolders locate the .ova file and import it.

The Import-VApp needs additional parameters.

$path = 'C:\Export'

Get-ChildItem -Path $path -Directory |

   ForEach-Object -Process {

   Get-ChildItem -Path $_.FullName -File -Filter *.ova |

   ForEach-Object -Process {

   Import-VApp -Source $_.FullName

   }

}


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

emcclure
Enthusiast
Enthusiast

Hi LucD,

So I added the CreateSeparateFolder to the export script I have and it works great.  I've also added in the changes you mentioned, but I'm not able to get to that part.  I made some changes to my Import script, including using the looping that you had shown me in another thread.  For whatever reason now when I get to the part where it opens the windows forms to select the folder it fails to do so.  I looked at my export script and original import script and when it gets to that point it runs just fine, so I'm not sure what's happening here.  I have done a reboot, but that didn't help.  Here's where it's getting stuck:

if($endAnswer -eq '4'){
   Write-Host "Select the main folder to import OVFs from" -ForegroundColor Green
   $initialDirectory = 'C:\'

   [System.Reflection.Assembly]::LoadWithpartialName("System.windows.forms") | Out-Null
   $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
   $OpenFileDialog.initialDirectory = $initialDirectory
   $OpenFileDialog.filter = "All files (*.*)|*.*"
   $OpenFileDialog.filterIndex = 1
   $OpenFileDialog.ShowDialog() | Out-Null
   $OpenFileDialog.fileName

   $filepath = $OpenFileDialog.fileName
   }

Prior to that I'm selecting the datacenter, host or cluster, host and datastore which all work perfectly.  After this part in the script it'd ask for a name for the VM, which I'm thinking I'd probably need to remove if the script you gave me is just using the name that was listed in the folder.  Then it'd be disk type and then the import of the OVF.  I'm guessing I'm missing something easy, but comparing to other scripts I have I just don't see what it is.

0 Kudos
LucD
Leadership
Leadership

You should be using a folder dialog I suspect.
Something like this

if($endAnswer -eq '4'){

   Write-Host "Select the main folder to import OVFs from" -ForegroundColor Green

   $initialDirectory = 'C:\Temp'


   [System.Reflection.Assembly]::LoadWithpartialName("System.windows.forms") | Out-Null

   $OpenFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog

   $OpenFolderDialog.SelectedPath = $initialDirectory

   $OpenFolderDialog.ShowNewFolderButton = $false

   $OpenFolderDialog.Description = 'Select the folder'

   $OpenFolderDialog.ShowDialog() | Out-Null

   $folderPath = $OpenFolderDialog.SelectedPath

}

$folderPath


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

0 Kudos
emcclure
Enthusiast
Enthusiast

Hi LucD,

I tried the code you provided, but I get the same result.  No window opens up for me to select a folder.

0 Kudos
LucD
Leadership
Leadership

Did you try to run that piece of code separately?

Seems to work for me.
Perhaps some of the preceding  code in your script is causing an issue?


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

0 Kudos
emcclure
Enthusiast
Enthusiast

Nope not separately.  Other scripts that have the windows forms work great, so that's why it's odd to me.  Here's the code up to the import-vapp point which is just after the windows forms part

Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false

$vCenter = Read-Host "Enter the vCenter name to connect to"
Connect-VIServer -Server $vCenter

while($true){
$endAnswer = '1'
while($endAnswer -ne 'Q'){
  if($endAnswer -eq '1'){
   $dc = Get-Datacenter | Select -ExpandProperty Name
   if ($dc.Count -gt 1) {
    $dc = $dc | Out-GridView -OutputMode Single -Title 'Select one datacenter'
   }
   $endAnswer = '2'
  }
   if($endAnswer -eq '2'){
   $type = "Host","Cluster" | Out-GridView -Title 'Select Host or Cluster' -OutputMode Single
   if ($type -eq "Cluster"){
    $cluster = Get-Cluster | Select Name | sort Name | Out-GridView -Title 'Select a cluster' -OutputMode Single
    $myCluster = Get-Cluster -Name $cluster.Name
    $vmHost = Get-VMHost -Location "$myCluster" -state "Connected" | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select a host in the cluster' -OutputMode Single
   }
   else
   {
   $vmHost = Get-VMHost | where {$_.state -eq "Connected"} | sort Name | Select Name,ConnectionState,PowerState | Out-GridView -Title 'Select a host' -OutputMode Single
   $myCluster = Get-Cluster -VMHost $vmHost.Name
   }
   $endAnswer = '3'
  }
   if($endAnswer -eq '3'){
   $datastore = Get-Datastore -VMHost $vmHost.Name | where {$_.type -eq "NFS"} | Select @{N="Cluster";E={$cluster.Name}},Name,CapacityGB,FreespaceGB,@{N='UsedSpace';E={$_.FreeSpaceGB/$_.CapacityGB*100}} | Out-GridView -Title 'Select a datastore' -OutputMode Single #Lists datastores and capacity#
   $myDatastore = Get-DataStore -Name $datastore.Name
   }
   $endAnswer = '4'
  }
   if($endAnswer -eq '4'){
   Write-Host "Select the main folder to import OVFs from" -ForegroundColor Green
   $initialDirectory = 'C:\Temp'

   [System.Reflection.Assembly]::LoadWithpartialName("System.windows.forms") | Out-Null
   $OpenFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
   $OpenFolderDialog.SelectedPath = $initialDirectory
   $OpenFolderDialog.ShowNewFolderButton = $false
   $OpenFolderDialog.Description = 'Select the folder'
   #$OpenFileDialog.filter = "All files (*.*)|*.*"
   #$OpenFolderDialog.filterIndex = 1
   $OpenFolderDialog.ShowDialog() | Out-Null
   #$OpenFolderDialog.fileName
   $folderpath = $OpenFolderDialog.SelectedPath
   }
   $folderpath
$vmName = Read-Host "Enter a name for the VM"
$diskStorage = "Thin","Thick","EagerZeroedThick" | Out-GridView -Title 'Select a disk type' -OutputMode Single

#$sVapp = @{
#Source = $filepath
#VMHost = $vmHost.Name
#Location = $myCluster
#Name = $vmName
#DiskStorageFormat = $diskStorage
#Datastore = $myDatastore
Get-ChildItem -Path $folderpath -Directory | ForEach-Object -Process {
  Get-ChildItem -Path $_.FullName -File -Filter *.ovf | For-Each-Object -Process {
  Import-vApp -Source $_.FullName -VMHost $vmHost.Name -Location $myCluster -Name $vmName -DiskStorageFormat $diskStorage -Datastore $mydatastore
}
  }

0 Kudos
LucD
Leadership
Leadership

Did you try with storing the code in a separate .ps1 file.
And then running that .ps1 from a fresh PowerShell prompt (not the ISE or VSC).

Windows forms act strangly sometimes when started from the ISE or VSC.

Write-Host "Select the main folder to import OVFs from" -ForegroundColor Green

$initialDirectory = 'C:\Temp'


[System.Reflection.Assembly]::LoadWithpartialName("System.windows.forms") | Out-Null

$OpenFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog

$OpenFolderDialog.SelectedPath = $initialDirectory

$OpenFolderDialog.ShowNewFolderButton = $false

$OpenFolderDialog.Description = 'Select the folder'

$OpenFolderDialog.ShowDialog() | Out-Null

$folderPath = $OpenFolderDialog.SelectedPath

$folderPath


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

0 Kudos
emcclure
Enthusiast
Enthusiast

Hi LucD,

I run everything in either PowerShell or PowerCLI.  Never in ISE.  Here's the results I'm getting:

When I copy the text into another script with the if($endAsnwer -eq '4') part of the script it fails.  If I comment that out then it runs.

When I comment out the if($endAnswer -eq '4') in the script that I'm trying to run then it'll prompt for the folder.

I have the script running now and it's hopefully importing the 2 OVF's I want it to do.  I'll update this when done.

One thing I don't like about the OpenFolderDialog is that it doesn't allow you to browse to a network share.  Now whoever is running this should be running it from the machine they want to import the OVF's from, but if not they'd need to map a drive or have an option to browse to a network location.  Would the previous form setup I used work with this (as far as getting all the sub folders) or is this the easier/better route to go?

0 Kudos
emcclure
Enthusiast
Enthusiast

Ok so the script completed but gave me some errors.  Some I can easily correct as I forgot to change a part of the VM naming to something else.  It appears that when it imported the VM it saved it as C:\Users\username\Desktop\OVFs and only imported 1 VM instead of the two that were in the sub folders.

What I was hoping that it would do is that when it imported the OVF's it'd put them in vCenter, but under the folder name or the name of the OVF so that way the user doesn't have to name any of them.  Is that possible?  Here's where I'm at with the code that needs to be worked on:

Write-Host "Select the main folder to import OVFs from" -ForegroundColor Green
   $initialDirectory = 'C:\Temp'

   [System.Reflection.Assembly]::LoadWithpartialName("System.windows.forms") | Out-Null
   $OpenFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
   $OpenFolderDialog.SelectedPath = $initialDirectory
   $OpenFolderDialog.ShowNewFolderButton = $false
   $OpenFolderDialog.Description = 'Select the folder'
   #$OpenFileDialog.filter = "All files (*.*)|*.*"
   #$OpenFolderDialog.filterIndex = 1
   $OpenFolderDialog.ShowDialog() | Out-Null
   #$OpenFolderDialog.fileName
   $folderPath = $OpenFolderDialog.SelectedPath
   #}
   $folderPath
#$vmName = Read-Host "Enter a name for the VM"
$diskStorage = "Thin","Thick","EagerZeroedThick" | Out-GridView -Title 'Select a disk type' -OutputMode Single

#$sVapp = @{
#Source = $filepath
#VMHost = $vmHost.Name
#Location = $myCluster
#Name = $vmName
#DiskStorageFormat = $diskStorage
#Datastore = $myDatastore
Get-ChildItem -Path $folderpath -Directory | ForEach-Object -Process {
  Get-ChildItem -Path $_.FullName -File -Filter *.ovf | ForEach-Object -Process {
  Import-vApp -Source $_.FullName -VMHost $vmHost.Name -Location $myCluster -Name $folderPath -DiskStorageFormat $diskStorage -Datastore $mydatastore
}
  }

Sleep 10
$folder = Get-Datacenter $dc.Name | Get-Folder -Type VM | sort Name | Out-GridView -Title 'Select a VM folder to move to' -OutPutMode Single
$VMmove = $folder.Name
Move-VM -VM $vmName -Destination $VMmove #Moves VM to the appropriate folder#

if($myCluster){
    $vlans = Get-Cluster -Name $mycluster.Name | Get-VMHost -Name $vmHost.Name | Foreach-Object {Get-VirtualPortGroup $_ | Select-Object @{N="Cluster";E={$cluster.Name}},Name,VLanId} | Out-GridView -Title 'Select a VLAN' -OutputMode Single #Gets available VLANs from the host#
}
else{
    $vlans = Get-VMHost -Name $vmHost.Name | Foreach-Object {Get-VirtualPortGroup $_ | Select-Object @{N="Cluster";E={$cluster.Name}},Name,VLanId} | Out-GridView -Title 'Select a VLAN' -OutputMode Single #Gets available VLANs from the host#
}

$vlan = $vlans
$NIC = Get-NetworkAdapter -VM $vmName
Set-NetworkAdapter -NetworkAdapter $NIC -NetworkName $vlan.Name -Confirm:$false #Applies selected VLAN to VM#

So I know I need to get the $vmName to be something other than $folderPath since a VM with the name of C:\Users\... isn't want anybody would want to see in vCenter.  I think everything else should work once what I mentioned earlier is fixed, or am I missing something else?

0 Kudos
LucD
Leadership
Leadership

Yes, that is a Windows dialog limitation.

If it is a public share, the user can go via the Network folder to any network share he wants.

And you can pass a network share in the SelectedPath property to start of there.

But I agree, it is not the best dialog to let a user select any folder.


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

0 Kudos
emcclure
Enthusiast
Enthusiast

Ok, so I have it working to import the OVF files by the name that they already have.  However in my script I want to move them to a different folder, but I don't know what name to use for the VM.  Here's the code that I have:

Get-ChildItem -Path $folderpath -Directory | ForEach-Object -Process {
  Get-ChildItem -Path $_.FullName -File -Filter *.ovf | ForEach-Object -Process {
  Import-vApp -Source $_.FullName -VMHost $vmHost.Name -Location $myCluster -DiskStorageFormat $diskStorage -Datastore $mydatastore
}
  }

Sleep 10
$folder = Get-Datacenter -Name $dc.Name | Get-Folder -Type VM | sort Name | Out-GridView -Title 'Select a VM folder to move to' -OutPutMode Single
$VMmove = $folder.Name
Move-VM -VM $vmName -Destination $VMmove #Moves VM to the appropriate folder#

if($myCluster){
    $vlans = Get-Cluster -Name $mycluster.Name | Get-VMHost -Name $vmHost.Name | Foreach-Object {Get-VirtualPortGroup $_ | Select-Object @{N="Cluster";E={$cluster.Name}},Name,VLanId} | Out-GridView -Title 'Select a VLAN' -OutputMode Single #Gets available VLANs from the host#
}
else{
    $vlans = Get-VMHost -Name $vmHost.Name | Foreach-Object {Get-VirtualPortGroup $_ | Select-Object @{N="Cluster";E={$cluster.Name}},Name,VLanId} | Out-GridView -Title 'Select a VLAN' -OutputMode Single #Gets available VLANs from the host#
}

$vlan = $vlans
$NIC = Get-NetworkAdapter -VM $folderPath.Name
Set-NetworkAdapter -NetworkAdapter $NIC -NetworkName $vlan.Name -Confirm:$false #Applies selected VLAN to VM#

So basically after the import I want to get a folder to move them to and then move them there.  However I haven't specified a VM name anywhere.  I did originally in the script after I had selected the file to import, but since I'm doing multiple OVF's I just want to use the name associated with them.  Would the $_.FullName work somehow or is there a better way to get the multiple VM names?

0 Kudos
LucD
Leadership
Leadership

I suspect that $_.Name (from Get-ChildItem) might be a better choice for the VM's DisplayName.


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

0 Kudos
emcclure
Enthusiast
Enthusiast

So when I do a command like this:

$vmName = Get-ChildItem -Path $folderpath -Directory | ForEach-Object -Process {

     Get-ChildItem -Path $_.Name }

It treats the $folderpath as the path that I'm running the script from instead of the path that it actually has for the OVF folder location.  If I just run the Get-ChildItem -Path $folderpath it shows me the correct path, but whenever I put anything after that it fails, even though it lists the folder names of the OVFs, but it just can't find them in the directory I'm running the script from.  Not sure why that's happening.

0 Kudos
LucD
Leadership
Leadership

I would need to see the complete script, as you are currently using it, to determine what the issue could be.


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

0 Kudos
emcclure
Enthusiast
Enthusiast

So this is where I'm at:

Get-ChildItem -Path $folderPath -Directory | ForEach-Object -Process {
  Get-ChildItem -Path $_.FullName -File -Filter *.ovf | ForEach-Object -Process {
  Import-vApp -Source $_.FullName -VMHost $vmHost.Name -Location $myCluster -DiskStorageFormat $diskStorage -Datastore $mydatastore
}
  }
$vmName = Get-ChildItem -Path $folderPath -Directory | ForEach-Object -Process { Get-ChildItem -Path $_.Name}

Sleep 10
$folder = Get-Datacenter -Name $dc.Name | Get-Folder -Type VM | sort Name | Out-GridView -Title 'Select a VM folder to move to' -OutPutMode Single
$VMmove = $folder.Name
Move-VM -VM $vmName -Destination $VMmove #Moves VM to the appropriate folder#

I've posted everything previous in the thread prior to this code.  Somewhere before that Sleep 10 I want to get the VM names.  I though basically reusing some of the Get-ChildItem part would work with the $_.Name, but it's been a no go so far.

0 Kudos
LucD
Leadership
Leadership

You should be using BaseName.
And you can combine that in the same loop as the Import-VApp.

Something like this

$vmName = @()

Get-ChildItem -Path $folderPath -Directory | ForEach-Object -Process {

   Get-ChildItem -Path $_.FullName -File -Filter *.ovf | ForEach-Object -Process {

   $vmName += $_.BaseName

   Import-vApp -Source $_.FullName -VMHost $vmHost.Name -Location $myCluster -DiskStorageFormat $diskStorage -Datastore $mydatastore

   }

}


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

0 Kudos
emcclure
Enthusiast
Enthusiast

Seems to be an improvement, but I'm running into another issue.  After it imports the OVF's I select the folder to move to.  It then moves them, however it throws an error:

Move-VM : 1/17/2019 1:28:05 PM  Move-VM         The operation for the entity "ImportTest" failed with the following message:

"The name 'EN-2K12SR2' already exists."

At C:\Users\username\Desktop\GenScripts\SharedScripts\GenImportOVFMultiple.ps1:92 char:1

+ Move-VM -VM $vmName -Destination $VMmove #Moves VM to the appropriate ...

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [Move-VM], DuplicateName

    + FullyQualifiedErrorId : Client20_TaskServiceImpl_CheckServerSideTaskUpdates_OperationFailed,VMware.VimAutomation

   .ViCore.Cmdlets.Commands.MoveVM

Line 92 is this:Move-VM -VM $vmName -Destination $VMmove

The lines right above that are:

$folder = Get-Datacenter -Name $dc | Get-Folder -Type VM | sort Name | Out-GridView -Title 'Select a VM folder to move to' -OutPutMode Single

$VMmove = $folder.Name

The folder is empty when I move them and it craps out the rest of the script so I can't change the VLAN to what I want or change an advanced setting either.

0 Kudos