VMware Cloud Community
ganapa2000
Hot Shot
Hot Shot
Jump to solution

Exclude certain VMDKs from deletion

Hi,

Is there a way to provide and exclude certain VMDKs from deletion from the Orphaned VMDK script.

We have more than 10 VMDK that we need to exclude from deletion.

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
.PARAMETER ExcludeFolder
Folders that will not be scanned
.PARAMETER Delete
Remove the orphaned files
.EXAMPLE
PS> Get-VmwOrphan -Datastore DS1
.EXAMPLE
PS> Get-Datastore -Name DS* | Get-VmwOrphan
.EXAMPLE
PS> Get-Datastore -Name DS* | Get-VmwOrphan -ExcludeFolder ISO
.EXAMPLE
PS> Get-Datastore -Name DS* | Get-VmwOrphan -Delete
#>

[CmdletBinding(SupportsShouldProcess=$True)]
param(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[PSObject[]]$Datastore,
[String[]]$ExcludeFolder,
[Switch]$Delete
)

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

$fileMgr = Get-View FileManager
$excludeFilter = $ExcludeFolder -join '|'
}

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.Accessible){
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){
$filler = ''
if($folder.FolderPath[-1] -eq ']'){$filler = ' '}
elseif($folder.FolderPath[-1] -ne '/'){$filler = '/'}
$key = "$($folder.FolderPath)$filler$($file.Path)"
#if($key -notmatch $excludeFilter){
if($excludeFilter -eq '' -or ($excludeFilter -ne '' -and $key -notmatch $excludeFilter)){
$fileTab.Add($key,$file)

$folderKey = "$($folder.FolderPath.TrimEnd('/'))"
if($fileTab.ContainsKey($folderKey)){
$fileTab.Remove($folderKey)
}
}
else{
Write-Verbose -Message "$(Get-Date)`t$((Get-PSCallStack)[0].Command)`tExcluding $key"
}
}
}

# 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)
}
}
if($fileTab.ContainsKey($_.ExtensionData.Config.Files.VmPathName)){
$fileTab.Remove($_.ExtensionData.Config.Files.VmPathName)
}
}

# 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]@{
Datastore = $ds.Name
Name = $_.Value.Path
Path = $_.Name
#VMDK = $_.Name.Split('/')[1].TrimStart('[')
Size = $_.Value.FileSize
CapacityKB = [math]::Round($_.Value.FileSize/1024,0)
CapacityMB = [math]::Round($_.Value.FileSize/1024/1024,0)
CapacityGB = [math]::Round($_.Value.FileSize/1024/1024/1024,0)
Modification = $_.Value.Modification
Owner = $_.Value.Owner
Thin = $_.Value.Thin
Extents = $_.Value.DiskExtents -join ','
DiskType = $_.Value.DiskType
HWVersion = $_.Value.HardwareVersion
}
New-Object PSObject -Property $obj
if ($Delete) {
If ($PSCmdlet.ShouldProcess($_.Name, "Remove Folder/File")) {
$fileMgr.DeleteDatastoreFile($_.Name, $ds.Datacenter.ExtensionData.MoRef)
if ($_.Value.DiskExtents.Count -ne 0) {
$_.Value.DiskExtents | ForEach-Object -Process {
$fileMgr.DeleteDatastoreFile($_, $ds.Datacenter.ExtensionData.MoRef)
}
}
}
}
}
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)."
}
}
}
}
}

#Script
Get-Datastore -Name DS14576 | Get-VmwOrphan -Delete -Verbose

Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

In the function add the new parameter.
Replace

    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSObject[]]$Datastore,
        [String[]]$ExcludeFolder,
        [Switch]$Delete
    )


with

    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSObject[]]$Datastore,
        [String[]]$ExcludeFolder,
        [String[]]$ExcludeVMDK,
        [Switch]$Delete
    )

Then change the following code

                        if ($Delete) {
                                If ($PSCmdlet.ShouldProcess($_.Name, "Remove Folder/File")) {
                                    $fileMgr.DeleteDatastoreFile($_.Name, $ds.Datacenter.ExtensionData.MoRef)
                                    if ($_.Value.DiskExtents.Count -ne 0) {
                                        $_.Value.DiskExtents | ForEach-Object -Process {
                                            $fileMgr.DeleteDatastoreFile($_, $ds.Datacenter.ExtensionData.MoRef)
                                        }
                                    }
                                }
                        }

to

                        if ($Delete) {
                            if ($ExcludeVMDK -notcontains $_.Name) {
                                If ($PSCmdlet.ShouldProcess($_.Name, "Remove Folder/File")) {
                                    $fileMgr.DeleteDatastoreFile($_.Name, $ds.Datacenter.ExtensionData.MoRef)
                                    if ($_.Value.DiskExtents.Count -ne 0) {
                                        $_.Value.DiskExtents | ForEach-Object -Process {
                                            $fileMgr.DeleteDatastoreFile($_, $ds.Datacenter.ExtensionData.MoRef)
                                        }
                                    }
                                }
                            }
                        }


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
4 Replies
LucD
Leadership
Leadership
Jump to solution

In the function add the new parameter.
Replace

    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSObject[]]$Datastore,
        [String[]]$ExcludeFolder,
        [Switch]$Delete
    )


with

    param(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSObject[]]$Datastore,
        [String[]]$ExcludeFolder,
        [String[]]$ExcludeVMDK,
        [Switch]$Delete
    )

Then change the following code

                        if ($Delete) {
                                If ($PSCmdlet.ShouldProcess($_.Name, "Remove Folder/File")) {
                                    $fileMgr.DeleteDatastoreFile($_.Name, $ds.Datacenter.ExtensionData.MoRef)
                                    if ($_.Value.DiskExtents.Count -ne 0) {
                                        $_.Value.DiskExtents | ForEach-Object -Process {
                                            $fileMgr.DeleteDatastoreFile($_, $ds.Datacenter.ExtensionData.MoRef)
                                        }
                                    }
                                }
                        }

to

                        if ($Delete) {
                            if ($ExcludeVMDK -notcontains $_.Name) {
                                If ($PSCmdlet.ShouldProcess($_.Name, "Remove Folder/File")) {
                                    $fileMgr.DeleteDatastoreFile($_.Name, $ds.Datacenter.ExtensionData.MoRef)
                                    if ($_.Value.DiskExtents.Count -ne 0) {
                                        $_.Value.DiskExtents | ForEach-Object -Process {
                                            $fileMgr.DeleteDatastoreFile($_, $ds.Datacenter.ExtensionData.MoRef)
                                        }
                                    }
                                }
                            }
                        }


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
ganapa2000
Hot Shot
Hot Shot
Jump to solution

LucD,

Since I have more than 10 VMDKs to exclude, is there a way to provide .txt or .csv input file for -ExcludeVMDK ?

 

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

You can read in the CSV file, extract the VMDK paths and store them in an array, which you pass to ExcludeVMDK

$exclude = Import-Csv -Path .\vmdk-names.csv -UseCulture | Select -ExpandProperty VMDKPath
Get-Datastore -Name DS14576 | Get-VmwOrphan -ExcludeVMDK $exclude -Delete


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
ganapa2000
Hot Shot
Hot Shot
Jump to solution

Thank you very much...that worked 🙂

Reply
0 Kudos