25

I am developing a powershell script file which shall execute some disk cleanup without user intervention. The user shall not be able to configure anything.

When I run cleanmgr.exe /d c: sageset:1 a popup window appears to select files/folders to be cleaned(cleanup options).

This will create a registry entry containing the settings with the cleanup options and after this, you can run cleanmgr.exe /sagerun:1 which will actually execute the cleanup.

Is there a way to specify the cleanup options directly with powerhell/command line(without the need to manually select things to be deleted)?

Pinte Dani
  • 1,989
  • 3
  • 28
  • 35

9 Answers9

12

The following Powershell script automates CleanMgr.exe. In this case, it removes temporary files and runs the Update Cleanup extension to purge superseded Service Pack Backup files (Windows 10 now does this automatically via a scheduled task). To automate other extensions, create a "StateFlags0001" property in the corresponding Registry key, as done in the New-ItemProperty lines. You will find the Registry key names in the "VolumeCaches" branch.

As far as being silent, this script attempts to start CleanMgr.exe in a hidden window. However, at some point CleanMgr spawns new processes which are visible and must be waited on separately.

Write-Host 'Clearing CleanMgr.exe automation settings.'
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*' -Name StateFlags0001 -ErrorAction SilentlyContinue | Remove-ItemProperty -Name StateFlags0001 -ErrorAction SilentlyContinue

Write-Host 'Enabling Update Cleanup. This is done automatically in Windows 10 via a scheduled task.'
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup' -Name StateFlags0001 -Value 2 -PropertyType DWord

Write-Host 'Enabling Temporary Files Cleanup.'
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Files' -Name StateFlags0001 -Value 2 -PropertyType DWord

Write-Host 'Starting CleanMgr.exe...'
Start-Process -FilePath CleanMgr.exe -ArgumentList '/sagerun:1' -WindowStyle Hidden -Wait

Write-Host 'Waiting for CleanMgr and DismHost processes. Second wait neccesary as CleanMgr.exe spins off separate processes.'
Get-Process -Name cleanmgr,dismhost -ErrorAction SilentlyContinue | Wait-Process

$UpdateCleanupSuccessful = $false
if (Test-Path $env:SystemRoot\Logs\CBS\DeepClean.log) {
    $UpdateCleanupSuccessful = Select-String -Path $env:SystemRoot\Logs\CBS\DeepClean.log -Pattern 'Total size of superseded packages:' -Quiet
}

if ($UpdateCleanupSuccessful) {
    Write-Host 'Rebooting to complete CleanMgr.exe Update Cleanup....'
    SHUTDOWN.EXE /r /f /t 0 /c 'Rebooting to complete CleanMgr.exe Update Cleanup....'
}
Nathan Hartley
  • 4,005
  • 2
  • 43
  • 46
  • 5
    for ps novices like me who sat there going... is it working??? make sure to add $VerbosePreference="Continue" if you want to see what it's doing. – JDPeckham Jan 27 '17 at 00:05
  • 2
    That was code was copy-n-pasted from an advanced function, which took a a -Verbose parameter. I'll change Write-Verbose to Write-Host, despite Don Jones saying that each use of Write-Host will kill a puppy. – Nathan Hartley Jan 27 '17 at 13:46
6

The PowerShell logic provided below is dynamic and ready for use or automation with the sageset options all being selected and no user interaction being required. This was inspired by multiple answers and comments from this post.

Note: I've adjusted for my needs and used successfully without any issues on multiple remote and local Windows 10 systems in particular.

Run on Local System

Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*' | % {
    New-ItemProperty -Path $_.PSPath -Name StateFlags0001 -Value 2 -PropertyType DWord -Force
   };
Start-Process -FilePath CleanMgr.exe -ArgumentList '/sagerun:1' ##-WindowStyle Hidden

Run on Remote System

$cred = Get-Credential "domain\administrator";
Invoke-Command -ComputerName "computer004" {
    Process {
        Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*' | % {
            New-ItemProperty -Path $_.PSPath -Name StateFlags0001 -Value 2 -PropertyType DWord -Force
           };
        Start-Process -FilePath CleanMgr.exe -ArgumentList '/sagerun:1' -WindowStyle Hidden
    }
} -AsJob -Credential $cred 

Supporting Resources

Bitcoin Murderous Maniac
  • 1,209
  • 1
  • 14
  • 27
5

You can use cleanmgr /verylowdisk to silently automate all the cleanup steps.

Ryan Turcotte
  • 59
  • 1
  • 1
4

The only solution I found is to manually set the registry values like this:

...

#Set StateFlags0012 setting for each item in Windows 8.1 disk cleanup utility
if (-not (get-itemproperty -path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Active Setup Temp Folders' -name StateFlags0012 -ErrorAction SilentlyContinue)) {
set-itemproperty -path 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Active Setup Temp Folders' -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\BranchCache' -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Downloaded Program Files' -name StateFlags0012 -type DWORD -Value 2

...

see full example

Daniel Fisher lennybacon
  • 3,865
  • 1
  • 30
  • 38
Pinte Dani
  • 1,989
  • 3
  • 28
  • 35
2

I ran into the same issue. Researching the possible ways, I have found the following: http://stealthpuppy.com/cleaning-up-and-reducing-the-size-of-your-master-image/

It shows how to create the sageset registry settings via cmd. You can then use the sagerun:# cmd. I have not tried it via script yet, but have validated that it works...

Kim
  • 21
  • 2
1

I have tried using this on Windows 10 and 11 test VMs, both from my RMM tool (which runs the script locally on the system) but also running as a .PS1 locally from an Administrative Powershell session on both of them with ExecutionPolicy set to Bypass. (Note: My RMM tool runs the script as SYSTEM; when I use a PS1, I'm running as the system's local administrator).

On pretty much every system I've tried,cleanmgr just hangs the first time and never completes. I've tried cleanmgr both with a /sageset /sagerun setup, or just with /verylowdisk. I've used the secondary wait command, or gone without it. And when I do it as a .PS1, Cleanmgr, even with -WindowStyle Hidden still shows a pop-up, which at some point disappears, but the script stays stuck, and sits there for an hour or more (test systems have SSDs, so not slow), whereupon I give up. When I do it through the RMM tool, it either hangs just like the .PS1, or once in awhile I get an output of "Object reference not set to an instance of an object", or another error. On some systems, if I end the script, it will then run on successive tries. But I can't get it to go consistently. So frustrating.

I'm nearly at the point of just using a bunch of scripted Remove-Item commands and skipping CLEANMGR.EXE completely, because at least those work. I'm unsure where to go otherwise.

EDIT: And the answer is it only works with -PassThru. Otherwise, it's a complete tossup. So I've added that. I'm concerned it doesn't finish before the next part of my script however (and adding "-Wait" brings the issue back), so I added "Start-Sleep -Seconds 300" to ensure I give everything time before going on to calculate how much disk space was freed during the cleanup.

FrostWolf
  • 11
  • 3
0

This script will get all the Volume Caches from the Registry, enable them to be cleaned and run the CLEANMGR.EXE for all caches.

$VolumeCachesRegDir = "hklm:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
$CacheDirItemNames = Get-ItemProperty "$VolumeCachesRegDir\*" | select -ExpandProperty PSChildName

$CacheDirItemNames | 
    %{
        $exists = Get-ItemProperty -Path "$VolumeCachesRegDir\$_" -Name "StateFlags6553" -ErrorAction SilentlyContinue
        If (($exists -ne $null) -and ($exists.Length -ne 0))
            {
                Set-ItemProperty -Path "$VolumeCachesRegDir\$_" -Name StateFlags6553 -Value 2
            }
        else
            {
                New-ItemProperty -Path "$VolumeCachesRegDir\$_" -Name StateFlags6553 -Value 0 -PropertyType DWord
            }
     }
Start-Sleep -Seconds 3


Write-Host 'Running CleanMgr.exe...'
Start-Process -FilePath CleanMgr.exe -ArgumentList '/sagerun:65535' -WindowStyle Hidden -PassThru
cls
MT-FreeHK
  • 2,462
  • 1
  • 13
  • 29
Orlando
  • 11
  • 2
0

Running CleanMgr.exe in a powershell script or by itself seems to work fine as long as you run it locally with an account that has local admin rights. But try running it remotely via any remote management tool or remote scripting command (Invoke-Command) and it does not run. You might see the process running on the remote system but it doesn't seem to cleanup anything and the process never ends. I would be interested if anyone has been able to get cleanmgr.exe to run remotely without any user interaction. E.G. ConfigMgr Right Click Tools, ConfigMgr App or PKG, Task Scheduler.

ouflak
  • 2,458
  • 10
  • 44
  • 49
  • Yes, I run it remotely all the time successfully 100% no problem with the below commands and confirm it works only any hundreds of machines using `invoke-command` using a domain admin credential per my answer on this thread. I have noticed it seems to freeze in RAM before too. I usually use a mix of DISM and then the cleanmgr.exe when I run it remotely though. On machines with slows HDD physical disks and not faster SSD, it can take an extra long time to run depending on how much it needs to cleanup. I think I know what you're saying, but being patient helps me there. Maybe permission issue? – Bitcoin Murderous Maniac Aug 31 '22 at 16:31
0

I did a couple of tests with all parameters in latest Win11. Whenever I add a parameter '/sagerun:...' the process is NOT hidden anymore. The only working solution is the exact call as from the task scheduler with parameter '/autocleanstoragesense /d C:'.

If I want to add a sagerun-parameter I had to start the process in a different user-context (here local system):

$reg = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches'
$null = Get-ItemProperty -Path "$reg\*" -Name StateFlags0001 -ea 0 | Remove-ItemProperty -Name StateFlags0001 -ea 0
$null = New-ItemProperty -Path "$reg\Update Cleanup" -Name StateFlags0001 -Value 2 -PropertyType DWord
$null = New-ItemProperty -Path "$reg\Temporary Files" -Name StateFlags0001 -Value 2 -PropertyType DWord
$null = New-ItemProperty -Path "$reg\Previous Installations" -Name StateFlags0001 -Value 2 -PropertyType DWord
$null = New-ItemProperty -Path "$reg\Recycle Bin" -Name StateFlags0001 -Value 2 -PropertyType DWord

# start cleanup as local system (requires admin-rights):
$task      = "Start Disk Cleanup"
$trigger   = New-ScheduledTaskTrigger -Once -At (get-date)
$action    = New-scheduledTaskAction -Execute "C:\Windows\System32\cleanmgr.exe" -Argument "/sagerun:1"
$settings  = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 20) -StartWhenAvailable -Hidden
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest
$task1     = Register-ScheduledTask -trigger $trigger -Action $action -TaskName $task -Settings $settings -Principal $principal -TaskPath "CustomTasks" -Force 0 -ea 0
$result    = Start-ScheduledTask -TaskName $task -TaskPath "CustomTasks"
$result    = Unregister-ScheduledTask -TaskName $task -Confirm:0

# we can now remove the scheduled task folder (if empty):
$scheduler = New-Object -ComObject Schedule.Service
$scheduler.connect()
$rootDir = $scheduler.GetFolder('\')
try {$rootDir.DeleteFolder("CustomTasks", $null)} catch {}

Get-Process -Name cleanmgr,dismhost -ea 0 | Wait-Process
'done.'
Carsten
  • 1,612
  • 14
  • 21