Find orphaned VMs in vCenter with PowerCLI
An orphaned VM is usually a virtual machine configuration file left on a datastore after its vCenter inventory entry has been removed. A .vmx file alone is not proof: it may belong to a powered-off machine, a template, a backup, or a VM registered under another account. The safest workflow compares datastore contents with inventory and reports candidates without changing anything.
The example below uses PowerCLI for VM inventory and the vCenter REST datastore-file endpoint for discovery. It targets PowerShell 7, PowerCLI 13, and vCenter 7.0 Update 2 or later. Test it on a non-production datastore first using an account with read access to both inventory and datastore files.
Understanding orphaned VM candidates
The term orphan is used inconsistently. Here, an unregistered VM candidate has a .vmx file but no matching registered configuration path. An orphaned disk may have only .vmdk, .nvram, snapshot, or log files, so this method cannot reconstruct a VM whose .vmx has already been removed.
| Evidence | What it indicates | Sensible next step |
|---|---|---|
Matching .vmx path |
The file belongs to a registered VM. | Exclude it from cleanup. |
| VMX path absent from inventory | Possible unregistered VM. | Verify host, backup, and disk ownership. |
| VMDK without a matching VMX | Possibly orphaned storage rather than an orphaned VM. | Review it as a separate recovery case. |
Cleanup opportunities also arise from Australian operating patterns. A Sydney team may inherit a datastore from a Melbourne migration, while a Brisbane office can retain a copy after a Perth consolidation. Easter and Christmas shutdowns can provide maintenance windows, but they still require documented change approval.
Comparing registered VMs with datastore files
Registered VMs expose their configuration-file path through the vSphere API. The script normalises those paths, retrieves datastore files, and compares full paths rather than display names, because a folder called payroll-prod may contain a VM named finance-01.
param(
[Parameter(Mandatory = $true)][string]$Vcenter,
[string[]]$Datastore,
[string]$Output = 'vm-orphan-candidates.csv'
)
$ErrorActionPreference = 'Stop'
$cred = Get-Credential -Message 'Enter your vCenter account'
Connect-VIServer -Server $Vcenter -Credential $cred | Out-Null
$api = "https://$Vcenter/api"
$token = $null
try {
$token = Invoke-RestMethod -Method Post -Uri "$api/session" `
-ContentType 'application/json' `
-Body (@{
username = $cred.UserName
password = $cred.GetNetworkCredential().Password
} | ConvertTo-Json)
$headers = @{ Authorization = "Bearer $token" }
$registered = Get-VM -Server $Vcenter | ForEach-Object {
$file = $_.ExtensionData.Config.Files |
Where-Object { $_.Key -eq 'virtualMachineConfigFile' } |
Select-Object -First 1
if ($file -and $file.Value -match '^\[([^\]]+)\]\s+(.+)$') {
[pscustomobject]@{
Datastore = $Matches[1]
Path = $Matches[2].Replace('\', '/').TrimStart('/')
}
}
}
$stores = if ($Datastore) {
Get-Datastore -Server $Vcenter -Name $Datastore
} else {
Get-Datastore -Server $Vcenter
}
$candidates = foreach ($ds in $stores) {
$files = Invoke-RestMethod -Method Get `
-Uri "$api/vcenter/datastore/$([uri]::EscapeDataString($ds.Name))/files" `
-Headers $headers
foreach ($file in @($files.value ?? $files)) {
$path = (($file.path ?? $file.name) -replace '\\', '/').TrimStart('/')
if ($path -notlike '*.vmx') { continue }
$known = $registered | Where-Object {
$_.Datastore -ceq $ds.Name -and $_.Path -ceq $path
} | Select-Object -First 1
if (-not $known) {
[pscustomobject]@{
Datastore = $ds.Name
VmxPath = $path
Reason = 'No matching registered VM configuration path'
}
}
}
}
$candidates | Sort-Object Datastore, VmxPath |
Export-Csv -Path $Output -NoTypeInformation -Encoding utf8
if ($candidates) {
$candidates | Sort-Object Datastore, VmxPath | Format-Table -AutoSize
} else {
Write-Output 'No unmatched .vmx files were found in the selected scope.'
}
}
finally {
if ($token) {
Invoke-RestMethod -Method Delete -Uri "$api/session" `
-Headers $headers -ErrorAction SilentlyContinue
}
}
Run the script with -Vcenter vcenter.example.com and optionally -Datastore 'datastore-name'. An empty result covers only accessible datastores selected in that run. If an endpoint request fails, resolve the permission or connectivity error before treating the scan as complete.
Storage changes can have wider effects than a single folder, especially with stretched clusters. Use the resilient vSAN design guide as background when checking whether a candidate datastore belongs to an active VMware vSAN configuration.
Interpreting the PowerCLI output
Treat every unmatched .vmx as a lead, not a verdict. Check ESXi host inventory, backup catalogues, templates, and the ownership of accompanying disks. Another registered VM can point to existing virtual disks while its own configuration file is missing, producing a misleading result.
In the Australian market, many organisations run VMware estates alongside AWS and Azure. Imported artefacts or cloud disk images can therefore resemble local virtual hardware. Data-residency and retention expectations also make a dated, read-only audit trail valuable before records or files are removed.
Recommended audit and remediation workflow
Use the first run to validate visibility, permissions, and path matching rather than to clean up a production estate.
- Start with one non-critical datastore and confirm that known registered VMs do not appear as candidates.
- Export the results, then check each path against ESXi host inventory, templates, backups, and recent change records.
- Keep the original
.vmx, virtual disks, snapshots, and NVRAM files until ownership and recoverability are confirmed. - Use the free PowerCLI tools collection to standardise your reporting workflow across sites.
Where cleanup is justified, schedule it through the normal change process, test the selected folder or datastore in a maintenance window, and record who approved each removal.
Turn findings into a safe cleanup plan
Start by running the audit against a test datastore, saving the CSV, and comparing every candidate with your backup catalogue and ESXi records. A clean result should mean verified coverage, not merely an empty output file.
Then extend the same check to the remaining datastores, preserve the evidence, and remove only confirmed artefacts under an approved change. That turns a directory cleanup into a controlled recovery decision rather than a risky guess.