# Version date: 2026-08-04 13:53 (CEST) # ------------------------------------------------------------ # Shutdown cleanup script - intended to be registered as a Windows Group # Policy SHUTDOWN script (gpedit.msc -> Computer Configuration -> Windows # Settings -> Scripts (Startup/Shutdown) -> Shutdown), so it runs # automatically and silently every time this computer is shut down, as # SYSTEM, before the shutdown completes. # # For every real local user profile on this machine, this script: # - Clears Firefox, Edge, and Chrome cache folders (never data like # bookmarks, passwords, or history - only the disposable cache). # - Clears the user's own Windows Temp folder (AppData\Local\Temp). # # It also empties the Recycle Bin for ALL users (not just whoever is # currently logged on), since C:\$Recycle.Bin is a single shared folder # with one subfolder per user, not something reachable per-profile. # # This script only touches genuinely disposable cache/temp data, safe to # delete automatically without anyone reviewing it first. Deleting Downloads # and Leica (.lif/.lifext) files is handled separately, by hand, using # FreeSpaceOnC.ps1 instead - those are real user files, not cache, so they # get a human review and confirmation step rather than running unattended. # # If a file is locked (e.g. a browser not fully closed yet), it is simply # skipped - it will get caught on the next run instead, so this is not # treated as a problem worth reporting in detail. # # A log is written next to this script, so it can be reviewed later - # there is no console to watch, since this runs unattended at shutdown. # ------------------------------------------------------------ $LogFile = Join-Path $PSScriptRoot "ShutdownCleanup.log" $AdminGroupName = ([System.Security.Principal.SecurityIdentifier]"S-1-5-32-544").Translate([System.Security.Principal.NTAccount]).Value # takeown's /D confirmation switch expects a localized letter - "Y" (Yes) on # English Windows, but "J" (Ja) on German Windows. This detects the current # UI language and picks the right one; if this system uses a different # language where neither applies, this may need to be edited by hand. $TakeownConfirm = if ((Get-UICulture).TwoLetterISOLanguageName -ieq "de") { "J" } else { "Y" } try { Stop-Transcript -ErrorAction Stop | Out-Null } catch { } Start-Transcript -Path $LogFile -Append Write-Host "" Write-Host "========================================" Write-Host "Shutdown cleanup started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" Write-Host "========================================" $FilesDeleted = 0 $Errors = 0 # Deletes every top-level item (file or folder) directly under $Path - # used to empty a cache/temp folder completely, while leaving the folder # itself in place. # # Running as SYSTEM (the normal case, via the shutdown trigger) generally # has enough access already. But this script can also be run manually, as # an Administrator, for testing - and a plain Administrator token does not # always guarantee access the way SYSTEM does, on a folder with unusual # ownership/permissions. Unlocking access unconditionally here, before # touching anything, means this behaves reliably either way rather than # only working correctly under SYSTEM. function Clear-Folder { param([string]$Path) if (-not (Test-Path $Path)) { return } takeown /F $Path /R /D $TakeownConfirm | Out-Null icacls $Path /grant "${AdminGroupName}:(OI)(CI)M" /T /C | Out-Null Get-ChildItem -Path $Path -Force -ErrorAction SilentlyContinue | ForEach-Object { try { Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction Stop $script:FilesDeleted++ } catch { $script:Errors++ } } } # Accounts to skip - not real user profiles, or profiles that shouldn't be # touched by an automated cleanup. Add more names here (e.g. local admin # accounts) as needed, separated by commas. Each name must match the # actual folder name under C:\Users, not necessarily the display name # shown at the login screen - these are usually the same, but can differ. # Matching is case-insensitive, so exact capitalization doesn't matter. $ExcludedUsers = @( "Administrator", "Guest", "Gast", "DefaultAccount", "WDAGUtilityAccount", "defaultuser0", "Public" ) $UserFolders = Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notin $ExcludedUsers } foreach ($UserFolder in $UserFolders) { Write-Host "" Write-Host "----------------------------------------" Write-Host "User: $($UserFolder.Name)" $LocalAppData = Join-Path $UserFolder.FullName "AppData\Local" # --- Firefox cache --- $FirefoxProfilesPath = Join-Path $LocalAppData "Mozilla\Firefox\Profiles" if (Test-Path $FirefoxProfilesPath) { Get-ChildItem $FirefoxProfilesPath -Directory -ErrorAction SilentlyContinue | ForEach-Object { Clear-Folder (Join-Path $_.FullName "cache2") } } # --- Chrome / Edge cache (all profiles: Default, Profile 1, ...) --- foreach ($Browser in @("Google\Chrome", "Microsoft\Edge")) { $UserDataPath = Join-Path $LocalAppData "$Browser\User Data" if (Test-Path $UserDataPath) { Get-ChildItem $UserDataPath -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq "Default" -or $_.Name -like "Profile *" } | ForEach-Object { Clear-Folder (Join-Path $_.FullName "Cache") Clear-Folder (Join-Path $_.FullName "Code Cache") Clear-Folder (Join-Path $_.FullName "GPUCache") } } } # --- Windows Temp folder: cleared entirely --- Clear-Folder (Join-Path $LocalAppData "Temp") } # --- Recycle Bin (all users): cleared entirely. This isn't inside the # per-user loop above, since it isn't stored under each user's own # profile folder - it's one shared top-level folder (C:\$Recycle.Bin) # containing one subfolder per user (named by their SID). Clear-Folder # already empties every top-level item under a given path, which is # exactly right here too - reused as-is, including its permission fix. # The path is single-quoted so PowerShell doesn't try to treat "$Recycle" # as a variable. Clear-Folder is a function defined further up in this # script, not a built-in PowerShell command. Write-Host "" Write-Host "----------------------------------------" Write-Host "Recycle Bin (all users)" Clear-Folder 'C:\$Recycle.Bin' Write-Host "" Write-Host "========================================" Write-Host "Cleanup summary" Write-Host "========================================" Write-Host "Files deleted : $FilesDeleted" Write-Host "Errors : $Errors" Write-Host "========================================" Stop-Transcript