Some thing every VMWare admin will end up doing at some point is orpahen file hunting. That fun task of hunting down unused VM disks hopefully before your datastore is out of space.
Working for a cloud provider, wasted space is always a problem and RVTools has been my tool of choice for years. RVTools does not give me file size or last modification time. Two very important bits of information in the “can I delete this?” decision. But RVTools is quick, very quick (and free).
Here is a handy bit of Powershell that is not quite as quick as RVTools but is not that much slower, and will give me Last modified date and the file size.
$vCenterServer = "myvCenterServer.domain"
connect-viserver $vCenterServer
$OrphanedFiles = @()
$OrphanedDisks = @()
$AllKnownPaths = @()
$AllFoundPaths = @()
$DataStores = get-datastore
foreach ($DataStore in $DataStores)
{
$DiskKnownPaths = @()
$DiskFoundPaths = @()
"Scanning $DataStore"
# get paths of VMs known in vCenter
$VMsinDS = get-vm -Datastore $Datastore
foreach ($VM in $VMsinDS)
{
$AllKnownPaths += $vm.ExtensionData.Layoutex.file.name
$DiskKnownPaths += $vm.ExtensionData.Layoutex.file.name
}
# get paths of templates known in vCenter
$TemplatessinDS = get-template -Datastore $Datastore
foreach ($Template in $TemplatessinDS)
{
$AllKnownPaths += $Template.ExtensionData.Layoutex.file.name
$DiskKnownPaths += $Template.ExtensionData.Layoutex.file.name
}
# Search the Datastore for files
# this is some code I found by LucD
#
$DatastoreView = Get-View $Datastore.id # Create new search specification for SearchDatastoreSubFolders method
$SearchSpecification = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec # Create the file query flags to return Size, type and last modified
$fileQueryFlags = New-Object VMware.Vim.FileQueryFlags
$fileQueryFlags.FileSize = $true
#$fileQueryFlags.FileType = $true
$fileQueryFlags.Modification = $true # Set the flags on the search specification
$SearchSpecification.Details = $fileQueryFlags # Set the browser
$DatastoreBrowser = Get-View $DataStoreView.browser # Set root path to search from
$RootPath = (“[” + $Datastore.Name + “]”) # Do the search
$SearchResults = $DatastoreBrowser.SearchDatastoreSubFolders($RootPath, $SearchSpecification)
# format the list of fond files in to a more useable array
foreach ($SearchResult in $SearchResults)
{
$DSName = ((($SearchResult.folderpath).split('] '))[0]).replace('[','')
$FilePath = (($SearchResult.folderpath).replace(('[' +$DSName + ']'),'')).trim()
foreach ($File in $SearchResult.file)
{
$tmpFoundFile = '' | select DS,Path,Name,FullName,ModDate,FileSize
$tmpFoundFile.DS = $DSName
$tmpFoundFile.Path = $FilePath
$tmpFoundFile.Name = $File.Path
$tmpFoundFile.FullName = $SearchResult.folderpath + $File.Path
$tmpFoundFile.ModDate = $File.Modification
$tmpFoundFile.FileSize = $File.FileSize
$AllFoundPaths += $tmpFoundFile
$DiskFoundPaths += $tmpFoundFile
}
}
# get the files that exist but are not known to vCenter
# loop the list of found files on the datastore and compere with the known files
foreach ($DiskFoundPath in $DiskFoundPaths)
{
$Foundit = $false
if ($DiskKnownPaths -contains $DiskFoundPath.fullname){$Foundit = $true} # if this matches then the file is known to vCenter
if ($DiskKnownPaths -contains (($DiskFoundPath.fullname).replace("-flat.vmdk",".vmdk"))){$Foundit = $true} # if the file Im looking at is -flat.vdk rename it to .vmdk (otherwise all -flat.vmdks would be orphaned)
if ($DiskFoundPath.path -match "contentlib-"){$Foundit = $true} # ignore anything with in a content library
if ($DiskFoundPath.path -match ".dvsData"){$Foundit = $true} # ignore anything with .dvsData
if ($DiskFoundPath.name -match ".vmx~"){$Foundit = $true} # ignore anything ending in .vmx~
if ($DiskFoundPath.Path -eq ""){$Foundit = $true} # ignore anything in the root (cant tell the difference between a file and a directory)
# if we have not matched the found file to a known file then its most likely orphaned
if ($Foundit -eq $false)
{
# Add the foundfile object to the output array
$OrphanedFiles += $DiskFoundPath
# if the found file is a -flat.vmdk, rename it to .vmdk (so I can find it) and save to its own disk output array
if ($DiskFoundPath.name -match "-flat.vmdk")
{
$VMDKOnly = $DiskFoundPath
$VMDKOnly.name = $VMDKOnly.name.replace("-flat.vmdk",".vmdk")
$VMDKOnly.FullName = $VMDKOnly.FullName.replace("-flat.vmdk",".vmdk")
$VMDKOnly.FileSize = [long]$VMDKOnly.FileSize / 1Mb
$OrphanedDisks += $VMDKOnly
}
}
}
}
# $AllKnownPaths # an array of every known file
# $AllFoundPaths # an array of every fine in the datastores
$OrphanedFiles | export-csv "orphs.csv" -NoTypeInformation
$OrphanedDisks | export-csv "orph-disks.csv" -NoTypeInformation
You will how have 2 CSV files.
Orphs.cav will list every file on your datastore that is not part of a VM or template.
Orph-disks.csv will list only the VMDKs
Note: when scanning the datastore at the file level the result can not distinguish between a file and a directory. So in the full report there will be odd results more then once directory level deep.
The is a lot of system stuff saved on DataStores. Cluster HA, Host scratch etc. all this will show up in the full report.
Disks are what you are looking for, Use the disk report.