Hi Guys,
Someone recently asked about the script which can detect the orphaned files in a datastore and can remove it with full control and condition was, it should not show the files which are not modified since last x number of days rather it should show the files which are not connected with any VM.
I did little effort and could find that such task is already done brilliantly by someone on Internet.
What I tested -
I removed one vmdk file from VM and then ran the script it exactly shows the vmdk which I removed and then run the command to delete it but it tried to delete entire folder. Go through below line, it will be clear.
Below is the script-
#Start here
function Remove-OrphanedData {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[PSObject[]]$Datastore,
[switch]$Delete
)
begin{
$fldList = @{}
$hdList = @{}
$fileMgr = Get-View FileManager
}
process{
foreach($ds in $Datastore){
if($ds.GetType().Name -eq "String"){
$ds = Get-Datastore -Name $ds
}
if($ds.Type -eq "VMFS" -and $ds.ExtensionData.Summary.MultipleHostAccess){
Get-VM -Datastore $ds | %{
$_.Extensiondata.LayoutEx.File | where{"diskDescriptor","diskExtent" -contains $_.Type} | %{
$fldList[$_.Name.Split('/')[0]] = $_.Name
$hdList[$_.Name] = $_.Name
}
}
Get-Template | where {$_.DatastoreIdList -contains $ds.Id} | %{
$_.Extensiondata.LayoutEx.File | where{"diskDescriptor","diskExtent" -contains $_.Type} | %{
$fldList[$_.Name.Split('/')[0]] = $_.Name
$hdList[$_.Name] = $_.Name
}
}
$dc = $ds.Datacenter.Extensiondata
$flags = New-Object VMware.Vim.FileQueryFlags
$flags.FileSize = $true
$flags.FileType = $true
$disk = New-Object VMware.Vim.VmDiskFileQuery
$disk.details = New-Object VMware.Vim.VmDiskFileQueryFlags
$disk.details.capacityKb = $true
$disk.details.diskExtents = $true
$disk.details.diskType = $true
$disk.details.thin = $true
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = $flags
$searchSpec.Query += $disk
$searchSpec.sortFoldersFirst = $true
$dsBrowser = Get-View $ds.ExtensionData.browser
$rootPath = "[" + $ds.Name + "]"
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
foreach($folder in $searchResult){
if($fldList.ContainsKey($folder.FolderPath.TrimEnd('/'))){
foreach ($file in $folder.File){
if(!$hdList.ContainsKey($folder.FolderPath + $file.Path)){
New-Object PSObject -Property @{
Folder = $folder.FolderPath
Name = $file.Path
Size = $file.FileSize
CapacityKB = $file.CapacityKb
Thin = $file.Thin
Extents = [string]::Join(',',($file.DiskExtents))
}
if($Delete){
If ($PSCmdlet.ShouldProcess(($folder.FolderPath + " " + $file.Path),"Remove VMDK")){
$dsBrowser.DeleteFile($folder.FolderPath + $file.Path)
}
}
}
}
}
elseif($folder.File | where {"cos.vmdk","esxconsole.vmdk" -notcontains $_.Path}){
$folder.File | %{
New-Object PSObject -Property @{
Folder = $folder.FolderPath
Name = $_.Path
Size = $_.FileSize
CapacityKB = $_.CapacityKB
Thin = $_.Thin
Extents = [String]::Join(',',($_.DiskExtents))
}
}
if($Delete){
if($folder.FolderPath -eq $rootPath){
$folder.File | %{
If ($PSCmdlet.ShouldProcess(($folder.FolderPath + " " + $_.Path),"Remove VMDK")){
$dsBrowser.DeleteFile($folder.FolderPath + $_.Path)
}
}
}
else{
If ($PSCmdlet.ShouldProcess($folder.FolderPath,"Remove Folder")){
$fileMgr.DeleteDatastoreFile($folder.FolderPath,$dc.MoRef)
}
}
}
}
}
}
}
}
}
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[PSObject[]]$Datastore,
[switch]$Delete
)
begin{
$fldList = @{}
$hdList = @{}
$fileMgr = Get-View FileManager
}
process{
foreach($ds in $Datastore){
if($ds.GetType().Name -eq "String"){
$ds = Get-Datastore -Name $ds
}
if($ds.Type -eq "VMFS" -and $ds.ExtensionData.Summary.MultipleHostAccess){
Get-VM -Datastore $ds | %{
$_.Extensiondata.LayoutEx.File | where{"diskDescriptor","diskExtent" -contains $_.Type} | %{
$fldList[$_.Name.Split('/')[0]] = $_.Name
$hdList[$_.Name] = $_.Name
}
}
Get-Template | where {$_.DatastoreIdList -contains $ds.Id} | %{
$_.Extensiondata.LayoutEx.File | where{"diskDescriptor","diskExtent" -contains $_.Type} | %{
$fldList[$_.Name.Split('/')[0]] = $_.Name
$hdList[$_.Name] = $_.Name
}
}
$dc = $ds.Datacenter.Extensiondata
$flags = New-Object VMware.Vim.FileQueryFlags
$flags.FileSize = $true
$flags.FileType = $true
$disk = New-Object VMware.Vim.VmDiskFileQuery
$disk.details = New-Object VMware.Vim.VmDiskFileQueryFlags
$disk.details.capacityKb = $true
$disk.details.diskExtents = $true
$disk.details.diskType = $true
$disk.details.thin = $true
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = $flags
$searchSpec.Query += $disk
$searchSpec.sortFoldersFirst = $true
$dsBrowser = Get-View $ds.ExtensionData.browser
$rootPath = "[" + $ds.Name + "]"
$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
foreach($folder in $searchResult){
if($fldList.ContainsKey($folder.FolderPath.TrimEnd('/'))){
foreach ($file in $folder.File){
if(!$hdList.ContainsKey($folder.FolderPath + $file.Path)){
New-Object PSObject -Property @{
Folder = $folder.FolderPath
Name = $file.Path
Size = $file.FileSize
CapacityKB = $file.CapacityKb
Thin = $file.Thin
Extents = [string]::Join(',',($file.DiskExtents))
}
if($Delete){
If ($PSCmdlet.ShouldProcess(($folder.FolderPath + " " + $file.Path),"Remove VMDK")){
$dsBrowser.DeleteFile($folder.FolderPath + $file.Path)
}
}
}
}
}
elseif($folder.File | where {"cos.vmdk","esxconsole.vmdk" -notcontains $_.Path}){
$folder.File | %{
New-Object PSObject -Property @{
Folder = $folder.FolderPath
Name = $_.Path
Size = $_.FileSize
CapacityKB = $_.CapacityKB
Thin = $_.Thin
Extents = [String]::Join(',',($_.DiskExtents))
}
}
if($Delete){
if($folder.FolderPath -eq $rootPath){
$folder.File | %{
If ($PSCmdlet.ShouldProcess(($folder.FolderPath + " " + $_.Path),"Remove VMDK")){
$dsBrowser.DeleteFile($folder.FolderPath + $_.Path)
}
}
}
else{
If ($PSCmdlet.ShouldProcess($folder.FolderPath,"Remove Folder")){
$fileMgr.DeleteDatastoreFile($folder.FolderPath,$dc.MoRef)
}
}
}
}
}
}
}
}
}
Remove-OrphanedData -datastore Name_of_datastore
#It will give you below kind of output
Now, if you want to delete this then use below command
Remove-OrphanedData -datastore Name_of_datastore -delete
But before deleting it, let's see what it will be delete. See below pic. I have added a switch -whatif in last after -delete switch.
It will delete entire folder not only single file.
Now, It is up to you that whether you want to remove the entire folder by adding only -delete switch or if you want to delete only vmdk file not entire folder and that will be manual action.
In my case, deletion gave error while deleting the entire folder, might be due to in-use vmx file or so...
Any doubt, let's discuss.
That's it for now. Enjoy the learning!
Thank you,
vCloudNotes
I actually use this script myself. The only issue I have with it is that it doesn't detect if the file is in use by a template published in a content library. Trying to figure out how to add this capability or find an upgraded version with it already.
ReplyDeleteI believe this script will not show the file which is being used by template. Can you share with the example and with snippet like, this file is in use by this template but still this script detects that it is not in use.
DeleteThank you for your comment.