PowerShell: Download VMX File, Rename, Upload, Add to Inventory
Originally published on TechColumnist.
I was tasked with migrating a VMware 4.1 to 5.1 environment. This created a slight issue due to the fact that environment ran vShield. For those of you not familiar, vShield on 4.1 had 2 additional lines in the VMX file that was manually added. These VFILE lines caused the VM not to boot if it was moved to an environment where vShield 1.0 was not present. So to resolve this a script was needed to download all these VMs, remove the lines, then reupload the file with a different name. Well, here you go.
Requirements
- PowerShell 2.0 or later.
- VMware vSphere PowerCLI 5.x, loaded as the
VMware.VimAutomation.Coresnap-in (the script callsAdd-PSSnapin). - Network access from the machine running the script to both the vCenter 4.1 and vCenter 5.1 servers.
- Permission to browse and upload files on the datastores, and to register (
New-VM) virtual machines in vCenter 5.1. - The datastores holding the VMs must be visible to the vCenter 5.1 server and to the ESX host you import to.
- The two local working folders and the import folder in vCenter 5.1 must already exist (see the values to edit below).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
vcServer4 | string | Yes | The vCenter 4.1 server to connect to. |
vcServer5 | string | Yes | The vCenter 5.1 server to connect to. |
The script exits with a message if either server is missing. Edit these variables near the top of the script before running it:
| Variable | Value in the script | Description |
|---|---|---|
$outvmxdir | C:\scripts\vmxfiles | Local folder that receives the downloaded VMX files. Must exist. |
$outvmxnewdir | C:\scripts\vmxfiles-new | Local folder for the modified VMX files. Must exist. |
$vCluster | NA Cluster | Cluster in vCenter 4.1 whose powered-on VMs are processed. |
$VMFolderName | Import | VM folder in vCenter 5.1 the VMs are imported into. Must exist. |
$ESXHost | <esx-host> | ESX host in vCenter 5.1 the VMs are imported to. |
$ResPool | Infrastructure | Resource pool in vCenter 5.1 the VMs are imported into. |
Usage
Pass the vCenter 4.1 server first and the vCenter 5.1 server second.
.\VM-GatherVMX.ps1 -vcServer4 "<vcenter-4-server>" -vcServer5 "<vcenter-5-server>"
Script
<#
.SYNOPSIS
Downloads, updates and uploads VMX files with PowerCLI, then adds the VMs to the vCenter 5.1 inventory.
.DESCRIPTION
Downloads the VMX file of every "PoweredOn" VM in the vCenter 4.1 cluster,
removes the vShield VFILE lines, and uploads the result to the same
datastore folder under a new name (<name>-51.vmx). Once uploaded, each VM is
imported into the new vCenter 5.1 server.
.PARAMETER vcServer4
The vCenter 4.1 server to connect to.
.PARAMETER vcServer5
The vCenter 5.1 server to connect to.
.EXAMPLE
.\VM-GatherVMX.ps1 -vcServer4 "<vcenter-4-server>" -vcServer5 "<vcenter-5-server>"
.NOTES
Author : Thomas Lasswell (https://www.techcolumnist.com)
Version : 1.1 (2026-09-20)
Requires: VMware vSphere PowerCLI 5.x (VMware.VimAutomation.Core snap-in)
History : 1.0 2013-10-25 Initial version.
1.1 2026-09-20 Fixed VM folder lookup (now after connecting to vCenter 5.1)
and a variable clash that overwrote it, VMX names that contain
".", "v", "m" or "x", and the unassigned $VMFullPath output.
#>
param(
[string]$vcServer4,
[string]$vcServer5
)
Clear-Host
if (!$vcServer4) {
Write-Host "No vCenter 4 Server Specified"
exit
}
if (!$vcServer5) {
Write-Host "No vCenter 5 Server Specified"
exit
}
# Load PowerCLI.
Add-PSSnapin VMware.VimAutomation.Core
$host.ui.rawui.WindowTitle = "PowerShell [PowerCLI Snap-in Loaded]"
# Download VMX file location.
$outvmxdir = "C:\scripts\vmxfiles"
# Modified VMX file location.
$outvmxnewdir = "C:\scripts\vmxfiles-new"
# Cluster name in vCenter 4.1 to gather PoweredOn VMs.
$vCluster = "NA Cluster"
# VM folder name in vCenter 5.1. This folder needs to exist.
$VMFolderName = "Import"
# ESX host in vCenter 5.1 to import VM.
$ESXHost = "<esx-host>"
# Resource pool in vCenter 5.1 to import VM.
$ResPool = "Infrastructure"
# Connect to vCenter 4.1 server.
Connect-VIServer $vcServer4
$aVM = Get-VM -Location $vCluster | Where-Object { $_.PowerState -eq "PoweredOn" } | Select-Object Name, PowerState
$aVMInfo = @()
foreach ($vm in $aVM) {
$vname = $vm.name
Write-Host $vname
# Process VMX information.
$VMView = Get-VM $vname | Get-View
$VMPathName = $VMView.Config.Files.VmPathName
Write-Host $VMPathName
# Download VMX file.
$dsname = $VMPathName.Split(" ")[0].TrimStart("[").TrimEnd("]")
$vmRelativePath = $VMPathName.Split(']')[1].TrimStart(' ')
Remove-PSDrive -Name fromds -ErrorAction SilentlyContinue
New-PSDrive -Name fromds -Location (Get-Datastore $dsname) -PSProvider VimDatastore -Root '/' | Out-Null
$dlvmx = $outvmxdir + ("/") + $vmRelativePath.Split('/')[1]
$checkforoutfile = Test-Path $dlvmx
if ($checkforoutfile) {
Remove-Item $dlvmx
}
Copy-DatastoreItem -Item fromds:\$vmRelativePath -Destination $outvmxdir -Force
# Update VMX file to -51 file without vShield lines.
$vmxfile = $VMPathName.Split("/")[1]
$vmxsource = $outvmxdir + "\" + $vmxfile
$vmxdest = $outvmxnewdir + "\" + [System.IO.Path]::GetFileNameWithoutExtension($vmxfile) + "-51.vmx"
$checkforoutfile = Test-Path $vmxdest
if ($checkforoutfile) {
Remove-Item $vmxdest
}
Get-Content $vmxsource | Where-Object { $_ -notmatch 'VFILE.globaloptions' -and $_ -notmatch 'scsi0:0.filters = "VFILE"' } | Set-Content $vmxdest
Add-Content $vmxdest "`n#Modified to remove VFILE Lines"
# Upload new VMX file to datastore.
Write-Host $vmxdest
$vmxupload = ($vmRelativePath -replace '\.vmx$', '') + "-51.vmx"
Write-Host $vmxupload
Copy-DatastoreItem -Item $vmxdest -Destination fromds:\$vmxupload -Force
# Set array for addition of VMs.
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "VMName" -Value $vname
$obj | Add-Member -MemberType NoteProperty -Name "VMDatastore" -Value $dsname
$obj | Add-Member -MemberType NoteProperty -Name "VMPathName" -Value $vmxupload
$aVMInfo += $obj
}
Remove-PSDrive -Name fromds -ErrorAction SilentlyContinue
Disconnect-VIServer $vcServer4 -Force -Confirm:$false
Start-Sleep 5
# Connect to vCenter 5.1 server.
Connect-VIServer $vcServer5
$VMFolder = Get-Folder $VMFolderName
# Import VM to vCenter 5.1.
foreach ($vm in $aVMInfo) {
$vname = $vm.VMName
$datastore = $vm.VMDatastore
$VMPath = $vm.VMPathName
Write-Host $vname
Write-Host $datastore
Write-Host $VMPath
$VMFullPath = "[" + $datastore + "] " + $VMPath
Write-Host $VMFullPath
New-VM -VMFilePath $VMFullPath -VMHost $ESXHost -Location $VMFolder -ResourcePool $ResPool | Out-Null
}
Disconnect-VIServer $vcServer5 -Force -Confirm:$false
Notes
- The original VMX files are left untouched. The modified copy is uploaded next to each original as
<name>-51.vmx, and that copy is what gets registered in vCenter 5.1. - The script expects each VM to live in a single datastore folder of the form
[datastore] <folder>/<name>.vmx. - Update 2026-09-20 (v1.1): the import folder is now looked up after connecting to vCenter 5.1 (it previously ran before any connection, and its variable was overwritten inside the download loop, so
New-VM -Locationreceived a path string instead of the folder). VMX file names that contain.,v,morxare also no longer truncated, and thefromds:drive is removed at the end.