Deleting Recycle Bin Items Deleted Over 28 Days Ago For All Users

I have a few different scripts that I have been using to remove items older than 28 days. However, I cannot find a way to do this for all users. I can use “last write time” in PowerShell for all users, but last write time is not the same as the deleted time. I found a script on http://baldwin-ps.blogspot.com/2013/07/empty-recycle-bin-with-retention-time.html that uses the “deleted time”, but it only deletes the items for the current user (not all users on a machine). Does anyone know how I can achieve this? Script by D. Baldwin:

# ----------------------------------------------------------------------- 
#
#       Author    :   Baldwin D.
#       Description : Empty Recycle Bin with Retention (Logoff Script)
#     
# -----------------------------------------------------------------------

$Global:Collection = @()

$Shell = New-Object -ComObject Shell.Application
$Global:Recycler = $Shell.NameSpace(0xa)

$csvfile = "\\YourNetworkShare\RecycleBin.txt"
$LogFailed = "\\YourNetworkShare\RecycleBinFailed.txt"


function Get-recyclebin
{ 
    [CmdletBinding()]
    Param
    (
        $RetentionTime = "28",
        [Switch]$DeleteItems
    )

    $User = $env:USERNAME
    $Computer = $env:COMPUTERNAME
    $DateRun = Get-Date

    foreach($item in $Recycler.Items())
        {
        $DeletedDate = $Recycler.GetDetailsOf($item,2) -replace "\u200f|\u200e","" #Invisible Unicode Characters
        $DeletedDate_datetime = get-date $DeletedDate   
        [Int]$DeletedDays = (New-TimeSpan -Start $DeletedDate_datetime -End $(Get-Date)).Days
      
        If($DeletedDays -ge $RetentionTime)
            {
            $Size = $Recycler.GetDetailsOf($item,3)
          
            $SizeArray = $Size -split " "
            $Decimal = $SizeArray[0] -replace ",","."
            If ($SizeArray[1] -contains "bytes") { $Size = [int]$Decimal /1024 }
            If ($SizeArray[1] -contains "KB") { $Size = [int]$Decimal }
            If ($SizeArray[1] -contains "MB") { $Size = [int]$Decimal * 1024 }
            If ($SizeArray[1] -contains "GB") { $Size = [int]$Decimal *1024 *1024 }
            
       $Object = New-Object Psobject -Property @{
                Computer = $computer
                User = $User
                DateRun = $DateRun
                Name = $item.Name
                Type = $item.Type
                SizeKb = $Size
                Path = $item.path
                "Deleted Date" = $DeletedDate_datetime
                "Deleted Days" = $DeletedDays }
            
            $Object

                If ($DeleteItems)
                {
                    Remove-Item -Path $item.Path -Confirm:$false -Force -Recurse
              
                    if ($?)
                    {
                        $Global:Collection += @($object)
                    }
                    else
                    {
                        Add-Content -Path $LogFailed -Value $error[0]
                    }
                }#EndIf $DeleteItems
            }#EndIf($DeletedDays -ge $RetentionTime)
}#EndForeach item
}#EndFunction

Get-recyclebin -DeleteItems


if (@($collection).count -gt "0")
{
$Collection = $Collection | Select-Object "Computer","User","DateRun","Name","Type","Path","SizeKb","Deleted Days","Deleted Date"
$CsvData = $Collection | ConvertTo-Csv -NoTypeInformation
$Null, $Data = $CsvData

Add-Content -Path $csvfile -Value $Data
}

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)

#ScriptEnd

Never done it but I would think gci/remove-item would be much shorter than this script.

Homework: Get-ChildItem "C:`$Recycle.bin" -Recurse -Force #apply a filter
#pipe into remove-item

I mentioned there are no properties to determine what the “deleted time” was for items in the Recycle Bin. I could only find “Last Write Time.” I cannot use “last write time” because the requirement is to delete items that have been in the recycle bin for over 28 days (items deleted over 28 days ago). That is why I had to use the script above. However, that only deletes items for the current user.

Looking into it further I’d probably translate the sids into user accounts prior to deletion for safety…It looks like there is a lot of program stuff in there. Also the names change, so the log isn’t going to be useful.

I don’t know about your windows os but my lastwrite and deleted time match exactly.

upload pics

To get you started.

gci "C:\`$Recycle.bin\" -Force | select mode,lastwritetime,name,@{n='aduser';e={(get-aduser $_.name).samaccountname}} |ft


Unfortunately, the deleted date information is not easily accessible. It is stored in info files next to the actual files.

Get-ChildItem -Path ‘C:$Recycle.Bin*$I*’ -Force

I have yet to find a way to retrieve the information from these files without using the COM object which only works for the current user.

The following PDF has an explanation of the $I file format on page 8 (right column).

The deleted date is the lastwrite in the recycle bin. see above.

Your observation is correct for the info files called $I(random).(original ext), but not for the original files which get moved and renamed to $R(same as info file).(original ext).

There is one R for every I right? If so, simple logic to tell when the item was deleted and remove both.

gci -Path "C:\`$Recycle.bin\" -Recurse -Force
should be enough to find all files that need to be deleted for all users, right? That whole $I and $R files are interesting, but hoping I don't need to get that involved. If so, I can look further into that PDF. One thing that may be an issue is a machine with a secondary drive. If a file is deleted on that drive (e.g. E: drive), the recycle bin will be under E:\`$Recycle.bin\. I tried using a wildcard with the recycle bin, but that doesn't work.

I have just realized that the code below will not work since $I files and $R files do not match up. I will just end up deleting basically everything in the recycle bin.

gci -Path "C:\`$Recycle.bin\" -Recurse -Force 

The previous person mentioned “There is one R for every I right? If so, simple logic to tell when the item was deleted and remove both.” This makes sense. However, I cannot figure out how to delete both the $I and $R files if the $I file is older than 28 days. The following code gives me the results I am searching for. however, I need to delete the corresponding $R files that have the same name as these $I files as well. Does anyone know how I can filter to both the $R and $I files when only the $I files have the correct date (last write time) that I’m looking for?

Get-ChildItem -Path 'C:\`$Recycle.bin\' -Recurse -Force | ? { $_.Name -like '$I*' -and $_.LastWriteTime -lt (Get-Date).AddDays(-28) }

This works as well:

Get-ChildItem -Path 'C:\$Recycle.Bin\*\$I*' -Recurse -Force | ? { $_.LastWriteTime -lt (Get-Date).AddDays(-28) }

I’m guessing I will need a foreach loop and some if statements. But I’m not sure how to find when the $I files match the $R file names. Any help would be appreciated.

I think I finally figured it out

$RetentionPeriod = (Get-Date).AddDays(-28)
$Path = 'C:\$Recycle.Bin\*\$I*'

# Find Recycle Bin items older than the $RetentionPeriod.
$TargetItems = gci -Path $Path -Recurse -Force | ? { $_.LastWriteTime -lt $RetentionPeriod  }

# Delete Recycle Bin items older than the $RetentionPeriod.
Foreach ($Item in $TargetItems) {
    $ItemName = $Item.Name.Substring(2)
    Remove-Item "C:\`$Recycle.Bin\*\`$R$ItemName" -Force -Confirm:$false
    Remove-Item $Item.FullName -Force -Confirm:$false
}

I am basically storing all files in scope in a variable that start with $I and am then deleting their matching $R files after cutting off the first two characters. Appears to be working. If anyone can think of a better way to do this, please feel free to chime in.

How does this delete for all users? I don’t see where you enumerate through
a) the c:\users\ folders, or
b) scan all folders for $recycle.bin, in the case of folders redirected to a server.

 

This script will delete all recycle bins for one user, not all recycle bins for all users.

$Recycle.Bin is sort of the “master” recycle bin for the OS. It contains each user SID below it.