PowerShell

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.Core snap-in (the script calls Add-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

NameTypeRequiredDescription
vcServer4stringYesThe vCenter 4.1 server to connect to.
vcServer5stringYesThe 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:

VariableValue in the scriptDescription
$outvmxdirC:\scripts\vmxfilesLocal folder that receives the downloaded VMX files. Must exist.
$outvmxnewdirC:\scripts\vmxfiles-newLocal folder for the modified VMX files. Must exist.
$vClusterNA ClusterCluster in vCenter 4.1 whose powered-on VMs are processed.
$VMFolderNameImportVM 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.
$ResPoolInfrastructureResource 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.

powershell
.\VM-GatherVMX.ps1 -vcServer4 "<vcenter-4-server>" -vcServer5 "<vcenter-5-server>"

Script

powershell
<#
.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 -Location received a path string instead of the folder). VMX file names that contain ., v, m or x are also no longer truncated, and the fromds: drive is removed at the end.