In my Orphaned files and folders – Spring cleaning post from way back, I provided a script to find orphaned VMDKs. This week there was a post in the VMTN PowerCLI Community that had a request to find all orphaned files. Time for a revisit of my old post!
I took my old script, massaged it a bit and gave it a more contemporary look and feel.
Just for info, the SearchDatastoreSubFolders method is relatively slow. So scanning a couple of datastores for orphaned files might take a bit of time. Be patient 🙂
The Script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
function Get-VmwOrphan{ <# .SYNOPSIS Find orphaned files on a datastore .DESCRIPTION This function will scan the complete content of a datastore. It will then verify all registered VMs and Templates on that datastore, and compare those files with the datastore list. Files that are not present in a VM or Template are considered orphaned .NOTES Author: Luc Dekens .PARAMETER Datastore The datastore that needs to be scanned .EXAMPLE PS> Get-VmwOrphan -Datastore DS1 .EXAMPLE PS> Get-Datastore -Name DS* | Get-VmwOrphan #> [CmdletBinding()] param( [parameter(Mandatory=$true,ValueFromPipeline=$true)] [PSObject[]]$Datastore ) Begin{ $flags = New-Object VMware.Vim.FileQueryFlags $flags.FileOwner = $true $flags.FileSize = $true $flags.FileType = $true $flags.Modification = $true $qFloppy = New-Object VMware.Vim.FloppyImageFileQuery $qFolder = New-Object VMware.Vim.FolderFileQuery $qISO = New-Object VMware.Vim.IsoImageFileQuery $qConfig = New-Object VMware.Vim.VmConfigFileQuery $qConfig.Details = New-Object VMware.Vim.VmConfigFileQueryFlags $qConfig.Details.ConfigVersion = $true $qTemplate = New-Object VMware.Vim.TemplateConfigFileQuery $qTemplate.Details = New-Object VMware.Vim.VmConfigFileQueryFlags $qTemplate.Details.ConfigVersion = $true $qDisk = New-Object VMware.Vim.VmDiskFileQuery $qDisk.Details = New-Object VMware.Vim.VmDiskFileQueryFlags $qDisk.Details.CapacityKB = $true $qDisk.Details.DiskExtents = $true $qDisk.Details.DiskType = $true $qDisk.Details.HardwareVersion = $true $qDisk.Details.Thin = $true $qLog = New-Object VMware.Vim.VmLogFileQuery $qRAM = New-Object VMware.Vim.VmNvramFileQuery $qSnap = New-Object VMware.Vim.VmSnapshotFileQuery $searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec $searchSpec.details = $flags $searchSpec.Query = $qFloppy,$qFolder,$qISO,$qConfig,$qTemplate,$qDisk,$qLog,$qRAM,$qSnap $searchSpec.sortFoldersFirst = $true } Process{ foreach($ds in $Datastore){ if($ds.GetType().Name -eq "String"){ $ds = Get-Datastore -Name $ds } # Only shared VMFS datastore if($ds.Type -eq "VMFS" -and $ds.ExtensionData.Summary.MultipleHostAccess -and $ds.State -eq 'Available'){ Write-Verbose -Message "$(Get-Date)`t$((Get-PSCallStack)[0].Command)`tLooking at $($ds.Name)" # Define file DB $fileTab = @{} # Get datastore files $dsBrowser = Get-View -Id $ds.ExtensionData.browser $rootPath = "[" + $ds.Name + "]" $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec) | Sort-Object -Property {$_.FolderPath.Length} foreach($folder in $searchResult){ foreach ($file in $folder.File){ $key = "$($folder.FolderPath)$(if($folder.FolderPath[-1] -eq ']'){' '})$($file.Path)" $fileTab.Add($key,$file) $folderKey = "$($folder.FolderPath.TrimEnd('/'))" if($fileTab.ContainsKey($folderKey)){ $fileTab.Remove($folderKey) } } } # Get VM inventory Get-VM -Datastore $ds | %{ $_.ExtensionData.LayoutEx.File | %{ if($fileTab.ContainsKey($_.Name)){ $fileTab.Remove($_.Name) } } } # Get Template inventory Get-Template | where {$_.DatastoreIdList -contains $ds.Id} | %{ $_.ExtensionData.LayoutEx.File | %{ if($fileTab.ContainsKey($_.Name)){ $fileTab.Remove($_.Name) } } } # Remove system files & folders from list $systemFiles = $fileTab.Keys | where{$_ -match "] \.|vmkdump"} $systemFiles | %{ $fileTab.Remove($_) } # Organise remaining files if($fileTab.Count){ $fileTab.GetEnumerator() | %{ $obj = [ordered]@{ Name = $_.Value.Path Folder = $_.Name Size = $_.Value.FileSize CapacityKB = $_.Value.CapacityKb Modification = $_.Value.Modification Owner = $_.Value.Owner Thin = $_.Value.Thin Extents = $_.Value.DiskExtents -join ',' DiskType = $_.Value.DiskType HWVersion = $_.Value.HardwareVersion } New-Object PSObject -Property $obj } Write-Verbose -Message "$(Get-Date)`t$((Get-PSCallStack)[0].Command)`tFound orphaned files on $($ds.Name)!" } else{ Write-Verbose -Message "$(Get-Date)`t$((Get-PSCallStack)[0].Command)`tNo orphaned files found on $($ds.Name)." } } } } } |
Annotations
Line 28-57: The script constructs the SearchDatastoreSubFolders parameter in the Begin block. This parameter will be the same for all datastores.
Line 56: To get maximum detail for the returned files, the script uses all available FileQuery variations
Line 62-64: Cheap OBN implementation
Line 67: The script only looks at shared VMFS datastores
Line 71: This hash table will be used to determine which file/folder is orphaned or not. First all the files the method fins are placed in the hash table. Then the files that belong to VMs and Templates are removed. Finally some system files are removed. What is left are orphaned files.
Line 76: Get all the files on the datastore with the SearchDatastoreSubFolders method
Line 77-87: Enter the files in the hash table
Line 83-85: Take care of the folder entries. If a file inside a folder is encountered, the script also removes the folder itself from the hash table
Line 90-96: Remove all files that belong to registered VMs from the hash table
Line 99-105: Remove all files that belong to registered Templates from the hash table
Line 108-111: Remove system files. For now these all files that are located in folders that start with a dot, or in a folder named vmkdump.
Line 114-128: If there are entries left in the hash table, collect more information and create an ordered object containing that information.
Sample Usage
The use of this function is rather straightforward, call the function with a datastorename or a datastore object.
1 |
Get-VmwOrphan -Datastore MyDS1 |
or
1 2 3 |
$ds = Get-Datastore -Name MyDS1 Get-VmwOrphan -Datastore $ds |
The function also accepts the datastore objects via the pipeline. You can do
1 |
Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan |
The objects that are returned by the function, depending on the type of file, contain information about the file. The following sample output shows how some properties are specific to certain file types.
If you want to create an orphaned files report for multiple datastores, it is handy to capture the results for each datastore in a separate worksheet in an Excel spreadsheet.
The following sample code uses Doug Finke‘s ImportExcel module to accomplish that.
1 2 3 4 5 |
$reportName = 'C:\orphan-report.xlsx' foreach($ds in (Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan | Group-Object -Property {$_.Folder.Split(']')[0].TrimStart('[')})){ $ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow } |
The resulting spreadsheet has all the orphaned files, with a separate worksheet for each datastore that has orphaned files.
Enjoy!
Marco Hofmann
Hi! Really would like to use that script, but I only get zero results. Even with -Verbose.
This should work against VSAN 7, should it?
LucD
The version of the script in the post does not work with VSAN datatsores.
But on VMTN I got a similar question, and I posted an updated version of the script there.
See https://communities.vmware.com/t5/VMware-PowerCLI-Discussions/get-orphaned-vmdk-information-from-vSAN/m-p/2814645/highlight/true#M98536
Can you try that version and let me know if it returns correct results for you?
Shibin
Looking at this in 2023 on latest version of vCenter, ESXi and DS.
I tried to use this script. Since we have restricted environment, I used the export CSV method mentioned in one of the comment section. Regardless of both options ( without export and with export ) I couldn’t get any results neither on screen nor on files.
What I did, first, generated list of DS in a text file, then used that text file lines one by one to scan it ( as shown below ):
Script reading each line and scanning, but not seeing any results. Is that due to any version changes ? But in my RV Tools report, there are plenty of zombie / orphaned files listed.
(Get-Datastore).Name > C:\Temp\AP2_DSName.txt
Write-Host “List of data store in AP2 vCenter has been created” -ForegroundColor Green
$Reader = New-Object System.IO.StreamReader(“C:\Temp\AP2_DSName.txt”)
while($Line = $Reader.ReadLine()) {
# Validate the orphane / zombie files
Get-Datastore -Name $Line | Get-VmwOrphan
Write-Host “Searching for Zombie files has been completed on $Line” -ForegroundColor Magenta
}
$Reader.Close()
LucD
The script works, for me, with the latest vSphere and ESXi versions.
Are your datastores VMFS datastores?
You can also activate Verbose logging, which might give some clues what happens.
Set the $verbosePreference variable to ‘Continue’ at the start of your code.
$VerbosePreference = 'Continue'
Shibin
Yes, all are VMFS datastores.
Enabled verbose mode and run. I could see many has returned orphaned files found. But the exported data has only one item, which is part of an existing VM, but moved to different datastore. No other information I could see. Below are the verbose command output.
VERBOSE: 09/13/2023 14:41:13 Get-VmwOrphan Looking at AP2_HDS_NMA_TP0_ESX_0023
VERBOSE: 13-Sep-23 2:41:14 PM Get-View Finished execution
VERBOSE: 13-Sep-23 2:42:56 PM Get-VM Finished execution
VERBOSE: 13-Sep-23 2:43:08 PM Get-Template Finished execution
VERBOSE: 09/13/2023 14:43:08 Get-VmwOrphan No orphaned files found on AP2_HDS_NMA_TP0_ESX_0023.
VERBOSE: 13-Sep-23 2:43:08 PM Get-Datastore Finished execution
Searching for Zombie files has been completed on AP2_HDS_NMA_TP0_ESX_0023
VERBOSE: 09/13/2023 14:43:11 Get-VmwOrphan Looking at AP2_HDS_NMA_TP0_ESX_0022
VERBOSE: 13-Sep-23 2:43:11 PM Get-View Finished execution
VERBOSE: 13-Sep-23 2:43:44 PM Get-VM Finished execution
VERBOSE: 13-Sep-23 2:44:00 PM Get-Template Finished execution
VERBOSE: 09/13/2023 14:44:00 Get-VmwOrphan Found orphaned files on AP2_HDS_NMA_TP0_ESX_0022!
VERBOSE: 13-Sep-23 2:44:00 PM Get-Datastore Finished execution
Searching for Zombie files has been completed on AP2_HDS_NMA_TP0_ESX_0022
LucD
Could you try capturing the output in a variable instead of showing it in the console?
Something like this for example
$report = @()
(Get-Datastore).Name > C:\Temp\AP2_DSName.txt
Write-Host “List of data store in AP2 vCenter has been created” -ForegroundColor Green
$Reader = New-Object System.IO.StreamReader(“C:\Temp\AP2_DSName.txt”)
while($Line = $Reader.ReadLine()) {
# Validate the orphane / zombie files
$report += Get-Datastore -Name $Line | Get-VmwOrphan
Write-Host “Searching for Zombie files has been completed on $Line” -ForegroundColor Magenta
}
$Reader.Close()
$report
Shibin
I followed Pablo’s method to capture it in CSV file
”
Pablo
DECEMBER 20, 2022 at 11:28″
I will re-attempt as you mentioned, will let you know the results when it is ready.
Shibin
No luck with that option as well. Same result like previous.
Shibin
Ok. I found the problem.
The function Get-VmwOrphan generate information, but it stores into $obj at line number 128. Hence it will not show any result on screen. That’s the reason the $report array has not showing any result. Since I have 200 Data store and looped to call, each time when function gets called, the $obj will be cleared and will not update my CSV file as well. So the result is, last DS with orphaned file shows. Rest of them will be overwritten.
I used return $obj to dump it on screen, so when function called using Get-VmwOrphan -Datastore $DSName it showed on the screen.
Since the $obj inside the function and clears everytime, I need to find a way to dump it into the the CSV file which calls from my PS script and append it.
LucD
The content of $obj is send to the console in line 128.
Your observation is not correct I’m afraid.
Shibin
You are right. When I integrated Pablo’s method that went wrong. After removal of that and run the script with my original way, it worked and got results.
Thank you for the great script !!!
mmccar
Hey LucD
Great script, thanks for sharing.
I ran the script against our datastores but found some orphaned files remained (e.g. .hlog, .vmem, .vmsd, .vmxf, .xml). I couldn’t find file query objects for these file types to add, so i decided to capture all file types by commenting out $searchSpec.Query.
However, this picks up some files belonging to live VMs on the datastore (e.g. .scoreboard, -ctk.vmdk, .vmx~, .vmx.lck), i am guessing as these are not returned by $_.ExtensionData.LayoutEx.File, hence are not removed from $fileTab. I could filter these with a Where-Object but of course that would also exclude orphans of the same type.
Just wondering if you came across similar challenges and have any advice?
LucD
The current version of the script only looks at VMDK files I’m afraid.
The $fileTab table is filled with all VMDK found on the datastore.
Then the VMDK files that are used by VMs, Templates, and some system files are removed from that table.
To handle all the files you encountered would require a serious rewrite of the script.
Although the basic concept should be the same:
Eric
Hello, I can’t dot source the function, it returns a ton of errors, starting at line 20:
At C:\scripts\orphaned.ps1:21 char:20
+ Â Â [CmdletBinding()]
+ ~
An expression was expected after ‘(‘.
At C:\scripts\orphaned.ps1:23 char:35
+ Â Â Â Â [parameter(Mandatory=$true,ValueFromPipeline=$true)]
+ ~
Missing argument in parameter list.
At C:\scripts\orphaned.ps1:23 char:61
+ Â Â Â Â [parameter(Mandatory=$true,ValueFromPipeline=$true)]
+ ~
Missing closing ‘)’ in expression.
At C:\scripts\orphaned.ps1:1 char:23
+ function Get-VmwOrphan{
+ ~
Missing closing ‘}’ in statement block or type definition.
At C:\scripts\orphaned.ps1:25 char:5
+ Â Â )
+ ~
LucD
Looks like something went wrong with your copy/paste by the looks of it.
These strange characters (+ Â Â) at the beginning of each line seem to indicate that.
Did you use the Copy option?
Pablo
Hi there,
first and foremost, thank you su much for this script, it’s superb!! 🙂
I have made some minimal changes on your script, to be able to generate a CSV output with the orphaned files:
In line 61:
$FileObjectArray = @()
In lines 128 & 129:
$FileObject = New-Object PSObject -Property $obj
$FileObjectArray += $FileObject
Finally, in line 136:
If ($FileObjectArray) {$FileObjectArray | Export-csv -NoTypeInformation vMwareOrphanedFiles.csv -Force}
Hope my amendments, helps someone .
KR,
Pablo
LucD
Nice one, thanks Pablo
Rani
Hi LucD,
I added the below to the end of the function in the script
Get-VmwOrphan -Datastore RegionA01-LOCAL-ESX01A
$ds = Get-Datastore -Name nimblea-vvol01
Get-VmwOrphan -Datastore $ds
Get-Cluster -Name RegionA01-COMP01 | Get-Datastore | Get-VmwOrphan
but none of your 3 sample usage returns anything, I created a dummy vm removed it from vc it does not return anything. what am i missing any help pls
LucD
Where did you add those lines?
After the last curly brace (line 138)?
Rani
yep right after the line 138 end of the curly brace, I tried one sample at a time but none helped. I will try again on my lab machines and repost if no success
Rani
Hi LucD,
Seems I had missed copying entirely, copied again created new scripts with 3 above samples and all of them give the similar output and I was able to spot the orphaned vm but I could not export to excel
Export-Excel : The term ‘Export-Excel’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\Prince Joshua\Downloads\Orphan VMDK.ps1:144 char:17
+ $ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Nam …
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Export-Excel:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Export-Excel : The term ‘Export-Excel’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\Prince Joshua\Downloads\Orphan VMDK.ps1:144 char:17
+ $ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Nam …
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Export-Excel:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Realized lab machines dont have excel would that be a problem?
Thanks help when u can
LucD
No, you don’t need to have Excel installed, but it looks like the ImportExcel module is not installed.
Rani
Perfect, worked on all 3 samples as you noted / directed thanks a lot for your help again and your scripts are very useful even this https://communities.vmware.com/t5/VMware-PowerCLI-Discussions/Script-to-export-Alarms-from-VCenter-to-analyze/m-p/2681811 🙂
Ricardo Jose
Regards,
how to use on infraestructure with mixed datastore VMFS6 + VSan + NFS 3
does it works with Vsan ??
My Best Regards
LucD
Hi,
Due to the internal format of the file structure on VSAN, the current script doesn’t work as-is.
For NFS you will have to remove the test condition for VMFS at the beginning, but other than that it should work on NFS.
Regards,
Luc
Joy
How do I alter the function to print the name of the datastore / folder that threw an error?
PS C:\WINDOWS\system32> Get-Datastore | Where-Object {$_.Name -notmatch “^st074no_templates_ds01$|^st0004no_templates_ds1-q1$|local”} | Get-VmwOrphan | Export-Csv c:\users\jm\downloads\vc007no-oct28-orphaned.csv -NoTypeInformation
Exception calling “SearchDatastoreSubFolders” with “2” argument(s): “An error occurred while communicating with the
remote host.”
At line:77 char:9
+ $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPat …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException
I’ve got a much better understanding how all works and get random errors depending on vcenter I run it on. Now I want to drill down to see what datastore wasn’t happy.
LucD
In line 68 there is a Write-Verbose that shows which datastore is being looked at.
Just call the function with the Verbose switch.
Get-Datastore |
Where-Object {$_.Name -notmatch "^st074no_templates_ds01$|^st0004no_templates_ds1-q1$|local"} |
Get-VmwOrphan -Verbose
Joy
Get-VM : 10/26/2021 1:59:16 PM Get-VM Exception has been thrown by the target of an invocation.
At C:\Users\jm\VMware_scripts\SpringCleaning\Orphaned files\SpringCleaning-LocateOrphaned.ps1:96 char:9
+ Get-VM -Datastore $ds | %{
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-VM], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM
Get-VM : 10/26/2021 2:00:22 PM Get-VM Exception has been thrown by the target of an invocation.
At C:\Users\jm\VMware_scripts\SpringCleaning\Orphaned files\SpringCleaning-LocateOrphaned.ps1:96 char:9
+ Get-VM -Datastore $ds | %{
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-VM], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM
Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: ‘[st251104_pre_ds19] previpwebw02-1.sjrb.ad/PREVIPWEBW02-1.SJRB.AD.vmdk’ Key being added: ‘[st251104_pre_ds19]
previpwebw02-1.sjrb.ad/previpwebw02-1.sjrb.ad.vmdk'”
At C:\Users\jm\VMware_scripts\SpringCleaning\Orphaned files\SpringCleaning-LocateOrphaned.ps1:86 char:13
+ $fileTab.Add($key,$file)
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentException
Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: ‘[st251104_pre_ds19] .snapshot/SCV_Daily_10-26-2021_02.25.01.0574/previpwebw02-1.sjrb.ad/PREVIPWEBW02-1.SJRB.AD.vmdk’ Key being added:
‘[st251104_pre_ds19] .snapshot/SCV_Daily_10-26-2021_02.25.01.0574/previpwebw02-1.sjrb.ad/previpwebw02-1.sjrb.ad.vmdk'”
At C:\Users\jm\VMware_scripts\SpringCleaning\Orphaned files\SpringCleaning-LocateOrphaned.ps1:86 char:13
+ $fileTab.Add($key,$file)
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentException
Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: ‘[st251104_pre_ds19] .snapshot/SCV_Daily_10-17-2021_02.25.01.0088/previpwebw02-1.sjrb.ad/PREVIPWEBW02-1.SJRB.AD.vmdk’ Key being added:
‘[st251104_pre_ds19] .snapshot/SCV_Daily_10-17-2021_02.25.01.0088/previpwebw02-1.sjrb.ad/previpwebw02-1.sjrb.ad.vmdk'”
At C:\Users\jm\VMware_scripts\SpringCleaning\Orphaned files\SpringCleaning-LocateOrphaned.ps1:86 char:13
+ $fileTab.Add($key,$file)
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentException
Hi Luc,
I’m seeing a couple of issues, see above. The “Exception has been thrown….” message research I did indicated to uninstall and reinstall powercli. I have not done this yet. What is the impact, other than the message being thrown?
Ultimately, I’d like to exclude ./snapshot/SCV* on all my datastores. This is a backup path.
I’d also like to exclude the following datastores:
st074no_templates_ds01
st0004no_templates_ds1-q1
*local*
LucD
Hi Joy,
For the exceptions, I would indeed advise completely removing PowerCLI and reinstalling with Install-Module.
The removal can be done by just deleting all the folders related to PowerCLI.
You can find those folders by running
Get-Module -Name VMware*
These exceptions skip a number of VMs, meaning that the VMDK from those VMs will be listed as orphaned.
To exclude specific datastores, you can use a Where-clause after the Get-Datastore cmdlet.
The easiest way is to use a RegEx.
Get-Datastore |
Where-Object {$_.Name -notmatch "^st074no_templates_ds01$|^st0004no_templates_ds1-q1$|local"} |
Get-VmwOrphan
The function should exclude system folders (like ./snapshot/SCV*), see line 108.
Should that not be the case, the RegEx in said line 108 can be adapted.
I hope that helps,
Luc
Joy
Hi Luc,
Before I get into what didn’t work I’d like to ask for a modification so that column B was the datastore name and column C was the datastore folder and filename.
Problems I encountered:
1. Running VM files identified as orphaned.
Folder Datastore Folder/File
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/aaa1dev.core.wifi.inet.nvram
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/aaa1dev.core.wifi.inet.vmdk
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/aaa1dev.core.wifi.inet.vmx
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware.log
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware-0.log
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware-4.log
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware-5.log
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware-6.log
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware-7.log
[st912cl_pre_ds05 aaa1dev.core.wifi.inet/vmware-8.log
2. VMs running off ISO files were identified. In hindsight, this is likely good as I can approach the user group asking why they are running VMs off ISOs vs the booted OS.
I have not made the changes you suggested yet, nor have I uninstalled and reinstalled powercli. I am a wee bit perplexed at what you mean by this:
Get-Datastore |
Where-Object {$_.Name -notmatch “^st074no_templates_ds01$|^st0004no_templates_ds1-q1$|local”} |
Get-VmwOrphan
–> do you want me to just run that on the powercli command line or did you want me to make the change at a particular place in the script? I understand what we’re doing here, just not sure where I’m to put this.
I’m not sure if you recall, I’m using NFS so removed “$ds.Type -eq “VMFS” -and ” from line 67. I’m mentioning this as a reminder of previous conversations. I know you’re a busy person.
function Get-VmwOrphan{
Get-VmwOrphan -Datastore DS1
.EXAMPLE
PS> Get-Datastore -Name DS* | Get-VmwOrphan
#>
[CmdletBinding()]
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[PSObject[]]$Datastore
)
Begin{
$flags = New-Object VMware.Vim.FileQueryFlags
$flags.FileOwner = $true
$flags.FileSize = $true
$flags.FileType = $true
$flags.Modification = $true
$qFloppy = New-Object VMware.Vim.FloppyImageFileQuery
$qFolder = New-Object VMware.Vim.FolderFileQuery
$qISO = New-Object VMware.Vim.IsoImageFileQuery
$qConfig = New-Object VMware.Vim.VmConfigFileQuery
$qConfig.Details = New-Object VMware.Vim.VmConfigFileQueryFlags
$qConfig.Details.ConfigVersion = $true
$qTemplate = New-Object VMware.Vim.TemplateConfigFileQuery
$qTemplate.Details = New-Object VMware.Vim.VmConfigFileQueryFlags
$qTemplate.Details.ConfigVersion = $true
$qDisk = New-Object VMware.Vim.VmDiskFileQuery
$qDisk.Details = New-Object VMware.Vim.VmDiskFileQueryFlags
$qDisk.Details.CapacityKB = $true
$qDisk.Details.DiskExtents = $true
$qDisk.Details.DiskType = $true
$qDisk.Details.HardwareVersion = $true
$qDisk.Details.Thin = $true
$qLog = New-Object VMware.Vim.VmLogFileQuery
$qRAM = New-Object VMware.Vim.VmNvramFileQuery
$qSnap = New-Object VMware.Vim.VmSnapshotFileQuery
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = $flags
$searchSpec.Query = $qFloppy,$qFolder,$qISO,$qConfig,$qTemplate,$qDisk,$qLog,$qRAM,$qSnap
$searchSpec.sortFoldersFirst = $true
}
Process{
foreach($ds in $Datastore){
if($ds.GetType().Name -eq “String”){
$ds = Get-Datastore | where{$_.Name -notmatch “^st074no_templates_ds01$|^st004no_templates_ds1-q1$|local”}
}
# Only shared NFS datastore
if($ds.ExtensionData.Summary.MultipleHostAccess -and $ds.State -eq ‘Available’){
Write-Verbose -Message “$(Get-Date)
t$((Get-PSCallStack)[0].Command)
tLooking at $($ds.Name)”# Define file DB
$fileTab = @{}
# Get datastore files
$dsBrowser = Get-View -Id $ds.ExtensionData.browser
$rootPath = “[” + $ds.Name + “]”
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec) | Sort-Object -Property {$_.FolderPath.Length}
foreach($folder in $searchResult){
foreach ($file in $folder.File){
$key = “$($folder.FolderPath)$(if($folder.FolderPath[-1] -eq ‘]’){‘ ‘})$($file.Path)”
$fileTab.Add($key,$file)
$folderKey = “$($folder.FolderPath.TrimEnd(‘/’))”
if($fileTab.ContainsKey($folderKey)){
$fileTab.Remove($folderKey)
}
}
}
# Get VM inventory
Get-VM -Datastore $ds | %{
$_.ExtensionData.LayoutEx.File | %{
if($fileTab.ContainsKey($_.Name)){
$fileTab.Remove($_.Name)
}
}
}
# Get Template inventory
Get-Template | where {$_.DatastoreIdList -contains $ds.Id} | %{
$_.ExtensionData.LayoutEx.File | %{
if($fileTab.ContainsKey($_.Name)){
$fileTab.Remove($_.Name)
}
}
}
# Remove system files & folders from list
$systemFiles = $fileTab.Keys | where{$_ -match “] \.|vmkdump”}
$systemFiles | %{
$fileTab.Remove($_)
}
# Organise remaining files
if($fileTab.Count){
$fileTab.GetEnumerator() | %{
$obj = [ordered]@{
Name = $_.Value.Path
Folder = $_.Name
Size = $_.Value.FileSize
CapacityKB = $_.Value.CapacityKb
Modification = $_.Value.Modification
Owner = $_.Value.Owner
Thin = $_.Value.Thin
Extents = $_.Value.DiskExtents -join ‘,’
DiskType = $_.Value.DiskType
HWVersion = $_.Value.HardwareVersion
}
New-Object PSObject -Property $obj
}
Write-Verbose -Message “$(Get-Date)
t$((Get-PSCallStack)[0].Command)
tFound orphaned files on $($ds.Name)!”}
else{
Write-Verbose -Message “$(Get-Date)
t$((Get-PSCallStack)[0].Command)
tNo orphaned files found on $($ds.Name).”}
}
}
}
}
$reportName = ‘C:\Users\Jm\Downloads\orphan-report-vc007no-all-datacenters-no-local-no-templates.xlsx’
foreach($ds in (Get-Datacenter | Get-Datastore | Get-VmwOrphan | Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘)})){
$ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow
}
LucD
This is the code that calls the Get-VmwOrphan function for each datastore that passes the Where-clause.
That Whereclause uses a RegEx to filter out the datastores you mentioned earlier.
If you place the function Get-VmwOrphan in a .ps1 file, then add those lines to the end, they will call the function.
Get-Datastore |
Where-Object {$_.Name -notmatch “^st074no_templates_ds01$|^st0004no_templates_ds1-q1$|local”} |
Get-VmwOrphan
And yes, that is the correct change to use the function for NFS datastores.
Joy
Luc,
Who knew that the newer versions of PowerCLI were integrated into PowerShell? That threw me. Just a wee panic. 🙂 I was on a really old one that was installed from a .msi file.
All seems to be working just fine now. Amazing script. Thank you so much.
Joy
LucD
You’re welcome 🙂
Martin
Hi Luc,
Anyway to make it work with GUID names?
I really don’t want to have to clean orphaned vmdk file by hand 🙁
Thanks
LucD
What exactly do you mean by “GUID names”?
Martin
Hi Luc,
I was replying to an older post of yours, looks like it didn’t attach to it.
LucD
SEPTEMBER 13, 2019 at 18:44
The function does test for a VMFS datastore, but you should be able to leave that Where-clause out.
With VSAN there is an issue, since the path the VM uses internally is not with the user-friendly datastorename, but with a king of GUID.
The function will not work with that.
Basically, because our volumes are named as UUID (I think thats the official name) I believe its why your script may not be working for my environment. I was looking to know if you know a way around removing orphaned/zombie vmdk files, eith with your script or another way.
Chris
Hey LucD,
when running your Script and exporting to CSV (cmd called: Get-Cluster -Name clustername | Get-Datastore | Get-VmwOrphan | Export-Csv c:\orphans.csv) I get values out however I get an error thrown at the end:
Exception calling “ContainsKey” with “1” argument(s): “Key cannot be null.
Parameter name: key”
At C:\Users\Administrator\Desktop\New.ps1:92 char:16
+ if($fileTab.ContainsKey($_.Name)){
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentNullException
any ideas on this? it’s relating to the following section:
# Get VM inventory
Get-VM -Datastore $ds | %{
$_.ExtensionData.LayoutEx.File | %{
if($fileTab.ContainsKey($_.Name)){
$fileTab.Remove($_.Name)
}
}
}
Have had a look to see if we have a datastore within this cluster with no name but nothing I could see.
Any thoughts on this would be useful as I am a bit stumped.
LucD
Hi Chris,
Judging from that snippet I would suspect a VM without files.
A kind of “ghost” VM.
Do you know for which VM the error is produced?
Luc
Chris
Hello LucD,
Yes we actually have a VM that’s listed in inventory but the HDD’s currently been taken out of it.
I would never have guessed at that from the output.. however my PS is somewhat lacking, so it’s probably obvious.
Thank you
Danny Z
Hi LucD,
I tried using the script but I keep getting the following message.
Here is code to call the function:
$cred = Get-Credential
$vcNames = Get-Content “C:\Temp\p2298.txt”
Connect-VIServer -Server $vcNames -Credential $cred
Get-datastore -name DS* | Get-VmwOrphan
Get-VmwOrphan : The term ‘Get-VmwOrphan’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At line:1 char:47
+ Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan
+ ~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-VmwOrphan:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Can you please help?
LucD
You will have to include the function.
One way of doing that is to copy/paste the function to your .ps1 file.
Then, from the same .ps1 file, you can call the function.
function Get-VmwOrphan{
...
}
Get-VmwOrphan -Datastore MyDS1
Michael Anselmi
Hello LucD,
I’ve been using your script for quite a while now. Thank you. I see that the version I have is an old one. Looked at your changelog but didn’t see if you ever added support for the new content libraries. Would you be interested in adding support?
Kind regards,
Michael.
LucD
Thanks Michael.
I’ll have a look if that is possible
Johnn Wong
getting this message in PowerCLI
WARNING: The ‘Accessible’ property of Datastore type is deprecated. Use the ‘State’ property instead.
LucD
Thanks for reporting that John.
I updated the code to
if($ds.Type -eq "VMFS" -and $ds.ExtensionData.Summary.MultipleHostAccess -and $ds.State -eq 'Available'){
LB-ATL
I think I must’ve had a stroke or something because I cannot, for the LIFE of me, get this to return ANY result.
I tried the unregistered VM trick… nothing.
I tried adding the code after the script that “calls the function”… nothing.
I don’t know what I’m doing wrong.
I copy/pasted the code into a PS1 file named “Get-VmwOrphan.ps1”
From PowerCLI (latest)
I connected to my vCenter…
I then typed “Get-Datastore | Get_VmwOrphan”
I got a prompt – no output.
I tried using “.\Get-VmwOrphan.ps1”
I got a prompt – no output.
ARGH.
LB-ATL
Also, this is on a vSAN, so it doesn’t register as VMFS5 (If that matters)
LucD
If you stored the function in that .ps1 file, you will have to dot-source that file.
That way the PS engine knows the function.
. .\file.ps1
Notice the blank between the 2 dots.
The example assumes the .ps1 file is located in your current directory, but it could be anywhere.
Just adapt the path.
. C:\Scripts\file.ps1
LucD
The function does test for a VMFS datastore, but you should be able to leave that Where-clause out.
With VSAN there is an issue, since the path the VM uses internally is not with the user-friendly datastorename, but with a king of GUID.
The function will not work with that.
Nathan C
I have been using your script in a large environemnt, I have nearly 3000 volumes. I was tasked with finding orphans and this script came in very handy. I have discovered 2 issues. One I was able to fix, the other still eludes me. Both give the exact same error(almsost).
The Errors…
1. The Volume name being passed 2 times with a space in between….
Exception calling “SearchDatastoreSubFolders” with “2” argument(s): “Invalid datastore path ‘[LOC-91253-BIZU-VMC000-Temp-DS01 LOC-91253-BIZU-VMC000-Temp-DS01]’.”
At C:\VMWareScripts\LucD\UpdatedOrphansFunction\NewOrphans.ps1:68 char:1
+ $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec) | S …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException
2. The Volume being called only once no spaces, but a “-” sign inside the Volume name…
Exception calling “SearchDatastoreSubFolders” with “2” argument(s): “Invalid datastore path ‘[97354_BIZU-CL01_DS03]’.”
At C:\users\nathan_choate\GIT\HCLS-VM-Toolkit\NATHAN\LucD\UpdatedOrphansFunction\NewOrphans.ps1:68 char:1
+ $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec) | S …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException
====
On item #1, with the volume listed twice with a space in between, this ended up being from having multiple vcenters with cross shared volumes, I solved this in the code…
Your line 74 >> $dsBrowser = Get-View -Id $ds.ExtensionData.browser
Your line 75 >> $rootPath = “[” + $ds.Name + “]”
My New Line added >> $dsBrowser = $dsBrowser[0] > $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec) | Sort-Object -Property {$_.FolderPath.Length}
====
As for item #2 im still stumped, I figured its got to be the ‘minus’ sign in the volume name…..
PowerCLI C:\VMWareScripts\LucD\UpdatedOrphansFunction\082819run> $rootpath
[97354_BIZU-CL01_DS03]
PowerCLI C:\VMWareScripts\LucD\UpdatedOrphansFunction\082819run> $rootPath = “[“”97354_BIZU-CL01_DS03″”]”
PowerCLI C:\VMWareScripts\LucD\UpdatedOrphansFunction\082819run> $rootPath
[“97354_BIZU-CL01_DS03”]
PowerCLI C:\VMWareScripts\LucD\UpdatedOrphansFunction\082819run> $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
Exception calling “SearchDatastoreSubFolders” with “2” argument(s): “Invalid datastore path ‘[“97354_BIZU-CL01_DS03″]’.”
At line:1 char:1
+ $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException
I have tried to force quotes around the $rootpath variable, but I cant seem to get the method to accept a volume with a ‘dash’ inside it. I have a number of these, its a large environment with many people and so its hard to just ‘fix’ the names, I was hoping to get some fresh ideas on how to deal with this. Thanks.
Otis Wheeler
I don’t know if you got an answer but I had issues with the Exception calling “SearchDatastoreSubFolders” with “2” argument(s): “Invalid..
You will need to specify the vcenter you are wanting to get the data from. this is easy with just a couple of edits.
after the line
if($ds.extensiondata.summary.MaintenanceMode -eq “normal”){
add this line
$Vcenter = [regex]::Split($ds.Uid,’.*@(.*):’)[1]
and then edit the $dsBrowser line and add the -server
$dsBrowser = Get-View -Id $ds.ExtensionData.browser -server $Vcenter
now when you call the function you will have to do the same.
$MyCluster = get-cluster somecluster
$Vcenter = [regex]::Split($MyCluster.Uid,’.*@(.*):’)[1]
$ClusterObj | Get-Datastore -server $Vcenter | Get-VmwOrphan
the reason I have to do this is because I am connected to 8 vcenters when I run this function so it see’s multiple datastores with the same ID
LucD
Thanks for posting that fix.
Travis
You can also use $ds.GetClient().Config.Server to get the server of the datastore rather than trimming the UID.
Thomas
First off, thanks for this function – will edefinately come in handy.
I do however experience a slight issue, in that I’m getting no output.
PowerShel: 5.1.18362.145
VMware PowerCLI 11.2.0 build 12483598
When running the function:
Get-VmwOrphan -datastore fast03
VERBOSE: 6/12/2019 8:36:42 AM Get-Datastore Finished executio
And that’s it. What am I missing?
Running Get-Datastore -name “fast03” shows expected output, so I know I’m connected and using a valid datastore.
Thanks
LucD
Hi Thomas,
Could it be that there are no orphaned files on that datastore?
As a test, can you create a dummy VM on that datastore and then unregister it.
When you then call the function, the files of that dummy VM should be shown.
Thomas
Hi
Still doesn’t show anything. Wouldn’t the script output “”No orphaned files found on $($ds.Name).” if nothing had been found? After hitting “Enter”, i only the above verbose output, and the command finished instantly.
It’s like it never really initiates or starts to run.
LucD
Hi Thomas,
Sorry to hear that.
Could it be that this datastore does not pass the test in line 67 (VMFS and shared and accessible)?
Thomas
Hi
You hit the nail! It’s not a VMFS datastore at all (NFS 3).
Thanks for the assistance.
LucD
You can still use the same logic, just remove the test for the VMFS type and the shared storage.
Prashant
Thanks for the excellent script. The sample code with ImportExcel function is missing operators. Please modify the sample code with ImportExcel function as:
====
$reportName = ‘C:\temp\VMW-orphan-report-MYCluster.xlsx’
foreach($ds in (Get-Cluster -Name MYCluster | Get-Datastore | Get-VmwOrphan |
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘)})){
$ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow
}
====
The output is listing Templates as being orphaned and is also picking up VMware-*.log files from actual VMs that exist. A sample output is attached:
Name Folder Size
vmware.log [MY_VMDATASTORE_05] MY-VMW-123-02/vmware.log 308926
UBUNTU-18.vmtx [MY_VMDATASTORE_05] UBUNTU-18/UBUNTU-18.vmtx 2686
vmware-7.log [MY_VMDATASTORE_05] MY-VMW-123_restored/vmware-7.log 1161458
vmware-1.log [MY_VMDATASTORE_05] VCSA01/vmware-1.log 267696
vmware-6.log [MY_VMDATASTORE_05] MY-vm-1234_restored/vmware-6.log 724546
windows-2012-r2.vmtx [MY_VMDATASTORE_05] windows-2012-r2/windows-2012-r2.vmtx 2542
vmware-1.log [MY_VMDATASTORE_05] MY-Pilot-02/vmware-1.log 397817
LucD
Thanks for the feedback.
I’m not sure what you mean with “missing operators”?
That sample seems to work for me, and the code you included looks exactly as what I have, except for the Path of the file.
The script indeed also lists templates, but only when they are orphaned. In other words not registered in the vCenter.
The vmware.log files are not automatically removed when the logging cycles. So you could have for example 5 vmware.log type files attached to the VM, while there can be other vmware.log type files that are not connected to the VM anymore. In other words orphaned.
Shaneb
howdy
the code from your block above is missing a } as the person above pointed out
The sample above reads
foreach($ds in (Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan |
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘))){
it should read
foreach($ds in (Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan |
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘)}))
with a extra } after trimstart(‘{‘)
LucD
Thanks for noticing.
I corrected the sample.
Scott
For some reason mine is listing Templates as being orphaned even though they aren’t. It is also picking up VMware-*.log files from actual VMs that exist. Does it not filter out Templates? Not sure on why the log files are showing up either.
LucD
Normally it checks if the files it finds belong to a registered VM. If they don’t, it reports the file as orphaned.
Can you send me an example (you can use the Contact form)?
Peter
Hi Luc,
I have placed your code – from 1st row to 138th – and then we pasted the following from 139th:
$reportName = ‘C:\orphan-report.xlsx’
foreach($ds in (Get-Cluster -Name LABMHM | Get-Datastore | Get-VmwOrphan |
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘)})){
$ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow
The whole code(from st row to 144) was saved as Get-VmwOrphan.ps1
There is an error message when it runs
PS C:\> .\Get-VmwOrphan.ps1
WARNING: The ‘Accessible’ property of Datastore type is deprecated. Use the ‘State’ property instead.
Exception calling “Add” with “2” argument(s): “Az elemet korábban már felvették. Kulcs a szótárban: „[CX4_Medium] log” ; Felvétel alatt álló kulcs: „[CX4_Medium] Log”.”
At C:\Get-VmwOrphan.ps1:80 char:13
+ $fileTab.Add($key,$file)
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentException
In english: Exception calling “Add” with “2” argument(s): “Item has already been added. Key in dictionary: „[CX4_Medium] log” ; Key being added: „[CX4_Medium] Log”.”
PowerCLI Version
—————-
VMware PowerCLI 11.0.0 build 10380590
—————
Component Versions
—————
VMware Cis Core PowerCLI Component PowerCLI Component 11.0 build 10335701
VMware VimAutomation VICore Commands PowerCLI Component PowerCLI Component 11.0 build 10336080
Your assistant and cooperation is highly appreciacted in this matter!
Sincerely,
Peter
LucD
Hi Peter,
Can you give it a try with
$reportName = 'C:\orphan-report.xlsx'
Get-Cluster -Name LABMHM | Get-Datastore -PipelineVariable ds |
ForEach-Object -Process {
Get-VmwOrphan -Datastore $_
} |
Group-Object -Property {$_.Folder.Split(']')[0].TrimStart('[')} |
Select -ExpandProperty Group |
Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow
You can ignore the Warning.
Mariano
Hello, please help as I get the following error:
PowerCLI C:\> Import-Module C:\Users\user\Desktop\Orphaned_VM_Script.ps1
PowerCLI C:\> Remove-OrphanedData -Datastore wrp-archive
Get-View : 15/02/2019 2:46:50 PM Get-View View with Id ‘FileManager-FileManager’ was not found on the server(s).
At C:\Users\user\Desktop\Orphaned_VM_Script.ps1:30 char:12
+ $fileMgr = Get-View FileManager
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-View], VimException
+ FullyQualifiedErrorId : Core_GetView_WriteNotFoundError,VMware.VimAutomation.ViCore.Cmdlets.Commands.DotNetInter
op.GetVIView
LucD
I’m not sure why you are trying to do an Import-Module of the .ps1 file?
When you want use a function, store the code in a .ps1 file.
At the end of that file, add the line that calls the function.
Now from a PowerShell prompt, invoke the .ps1 file, by typing it’s path
Rajug VCP
Thank you LuCD,
am lil confused. I just used the main script and ran in Powercli its nothing happning.
do I need to copy the other lines also with main script?
foreach($ds in (Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan |
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘))){
$ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow
}
LucD
Hi Rajug,
Yes, the function in itself will not do anything.
You will have to call the function.
You can place that call in the same .ps1 file as the one you placed the function in.
The code you included is such a call to the function.
ANkoji
Hi Lucd,
Where I need to copy below lines in Script for output
$reportName = ‘C:\orphan-report.xlsx’
foreach($ds in (Get-Cluster -Name MyCluster | Get-Datastore | Get-VmwOrphan |
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘))){
$ds.Group | Export-Excel -Path $reportName -WorkSheetname $ds.Name -AutoSize -AutoFilter -FreezeTopRow
}
LucD
The Get-VmwOrphan is a function.
You can copy the complete function to a .ps1 file, and then place your lines of code at the bottom of your .ps1 file (outside the function).
RegReg
very good thank you
Pete
Great script, thanks! I used installed the ImportExcel module and tried your sample code and got an error. It looks like it’s missing a } at the end of line 4 before the final 2 closing parenthesis. Line 4 should be as follows. At least that’s how it worked for me.
Group-Object -Property {$_.Folder.Split(‘]’)[0].TrimStart(‘[‘)})){
Jon
Can folders be excluded? When I modify for including NFS datastores, NetApp snapshots directory (.snapshot) becomes visible too and the scan sorts through all of that as well. Slows down the scan unnecessarily. Thoughts?
LucD
Hi Jon,
The SearchDatastoreSubFolders method doesn’t have any options to exclude specific folders I’m afraid.
The only option I can think of is to call the Get-VmwOrphan function and then filter the results with a Where-clause on the Folder property.
Luc
Dario
Hi Luc,
there seems to be a error when using this script with PowerCLI 6.5:
C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCliEnvironment.ps1
WARNING: The ‘Accessible’ property of Datastore type is deprecated. Use the ‘State’ property instead.
Exception calling “SearchDatastoreSubFolders” with “2” argument(s): “Invalid datastore path ‘[s-infra-00-001]’.”
At D:\sw\Script\PowerShell\VMWare\Get-Orphaned-Files-2.1.ps1:68 char:1
+ $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec) | S …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException
And in your sample code at the bottom of the post there is a “}” missing on line 3.
JD
Hey LucD
Just getting your script to run after hunting around
I get the following error:
PowerCLI C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI> Get-VmwO
rphan -Datastore V-Cluster2-DISKa
WARNING: The ‘Accessible’ property of Datastore type is deprecated. Use the
‘State’ property instead.
Unable to find type [ordered]: make sure that the assembly containing this type
is loaded.
At C:\Users\jdesmarais\Documents\WindowsPowerShell\Modules\MyFunctions\MyFuncti
ons.psm1:103 char:17
+ $obj = [ordered] <<<< @{
+ CategoryInfo : InvalidOperation: (ordered:String) [], RuntimeEx
ception
+ FullyQualifiedErrorId : TypeNotFound
JD
I figured it out, I needed powershell 3 for windows 7.
JerH
Thanks for taking the time and effort to create these scripts. I’m really new to using PS scripts in VMware. I’m assuming somewhere along the way, I need to specify which vCenter to run this on? Can you help with the proper language for that? Also, I get an error with I try to run it with the export-excel function. Do I just paste that code into the end of your script? Maybe I need to download Doug Finkel’s code to make it work? Sorry for the annoying newb questions, but I’m not getting anywhere trying to figure it out on my own…
Dennis Zimmer
Great Script Luc! I would change line 67 to avoid an error for datastores that can’t be accessed.
if($ds.Type -eq “VMFS” -and $ds.ExtensionData.Summary.MultipleHostAccess -and $ds.Accessible){
That script will definitely makes its way into our OpBot repository!
LucD
Great suggestion Dennis.
The code has been updated.
Vladimir
Hi LucD! In order to include NFS shared volumes, I would like to change line like 67 this
if((($ds.Type -eq “VMFS”) -or ($ds.Type -eq “NFS”)) -and $ds.ExtensionData.Summary.MultipleHostAccess -and $ds.State -eq “Available”) {
(already included ds.State instead deprecated ds.Accessible property)
Dennis Zimmer
Amazing script Luc, that will find its way into our OpBot function library. Small improvement, that the script runs through even if some datastores are not accessible:
Line 61
if($ds.Type -eq “VMFS” -and $ds.ExtensionData.Summary.MultipleHostAccess){
I would change to
if($ds.Type -eq “VMFS” -and $ds.ExtensionData.Summary.MultipleHostAccess -and $ds.Accessible){
Todd Scalzott
Thanks so much for this great script, Luc.
Are you missing “$_.Group |” prior to “Export-Excel” in your sample utilizing the ImportExcel module?
Thanks,
Todd
LucD
Thanks Todd, your assumption is correct, well spotted.
I updated the sample code.
Stu Green
Hi Luc,
Thanks for this script 🙂
I added this filter which I found to give the output in Byte\KB\MB\GB\TB\PB format
https://mjolinor.wordpress.com/2012/04/18/powershell-byte-count-formatting-simplified/
Cheers!
Ritam9
Hi Luc,
Thanks again for your wonderful scripts. Is this has to be run from the Powershell only ? I am not able to run from powercli . Can you provide the script which can be run from the powercli. Also does it avoid snapshot files / ctk files ?
PowerCLI D:\> powercliversion
PowerCLI Version
—————-
VMware vSphere PowerCLI 5.1 Release 1 build 793510
—————
Snapin Versions
—————
VMWare AutoDeploy PowerCLI Component 5.1 build 768137
VMWare ImageBuilder PowerCLI Component 5.1 build 768137
VMware License PowerCLI Component 5.1 build 669840
VMware vSphere PowerCLI Component 5.1 build 793489
LucD
Hi Ritam9,
You have to download the function, under The Script, from the post, and store it in a .ps1 file.
Now, at the end of the file, you will have to call the function. I have some examples of that in the Sample Usage.
Once you have all that in the .ps1 file, save the file and from the PowerCLI prompt you can call the .ps1 file.
Note that you will have to connect (Connect-VIServer) first to a vSphere Server (vCenter or ESXi node).
I hope that helps.
Luc
Tony
Thanks for another great script. Note, in your example section you have Get-VmwOrphaned -Datastore DS1, which should be updated to Get-VmwOrphan
LucD
Thanks Tony.
And thanks for spotting that!