While Windows does an OK job of preventing low disk space, you can use Group Policy or SCCM to vastly improve those tasks and prevent low disk space problems on your clients.
In my environment, we had a few dozen machines with less than 10% free disk space. By using this setup, we now have zero and a few dozen less problems to worry about! When we purchase new computers, we are saving $100 per machine because we know that we can use a smaller SSD.
Setting up an automatic disk cleanup requires two components. First, you need a way to filter out machines that have low disk space from normal machines. Group Policy or SCCM can do this for you. Even though I personally use SCCM to do this, I’ll outline how to use both tools.
Second, you need a way to tighten the existing cleanup tasks while expanding the low disk cleanup to other common space hogs. SCCM can do this but Group Policy is my preferred solution for most of the settings.[note]Sometimes, low disk space can prevent the SCCM client from behaving normally but Group Policy will still apply normally.[/note]
Finding Machines with Low Disk Space Using Group Policy
To find these computers with Group Policy, we can use a WMI Filter. If you have access to SCCM, I would recommend using SCCM to find computers instead – skip to the next heading.
If you do not have SCCM, Group Policy will work just fine. You won’t have access to reporting and processing will be a fraction slower. WMI filters do have to process every time and slow down Group Policy processing. This WMI filter is fast (.50ms on my test machine), so it is not a huge deal.
If you are using Group Policy, open the Group Policy Management Console and head down to the WMI Filters section. Create a new WMI filter and paste in the following query:
Select * from Win32_LogicalDisk where VolumeName = "OSDisk" AND FreeSpace < 10000000000
This query will equal TRUE on any computer when the OSDisk volume has less than 10GBs of free space. If you wish to apply the disk cleanup to all drives (including external/USB drives) in the machine, remove the VolumeName section. You can also adjust the FreeSpace size or reverse the less than symbol to a greater than symbol for testing.
If you do not have access SCCM, skip to the Cleaning Up Disk Space with Group Policy section – at the bottom of this article.
Find Machines with Low Disk Space Using SCCM
The Hardware Inventory in SCCM can be used to find computers with low disk space. From that, you can build out a Low Disk Space collection and target those devices with additional cleanup tasks.
First, make sure that your clients are reporting their Logical Disk Free Space in the hardware inventory. Under Administration/Client Settings, open your default client settings and select Hardware Inventory. Next, select Set Classes. Finally, expand Logical Disk (SMS_LogicalDisk) and ensure that Free Space (MB) is checked). If is not checked, check it and continue on – clients just won’t populate into your Low Disk Space collection until their next Hardware Inventory cycle.
Now, create a new Device Collection and add in the following query statement:
select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_LOGICAL_DISK on SMS_G_System_LOGICAL_DISK.ResourceID = SMS_R_System.ResourceId where SMS_G_System_LOGICAL_DISK.FreeSpace <= 10000 and SMS_G_System_LOGICAL_DISK.VolumeName = "OSDisk"
Notice that is query is virtually the same as the Group Policy WMIFilter. The FreeSpace is measured in megabytes for SCCM instead of just bytes for Group Policy.
Cleaning Up Disk Space with SCCM
If you have SCCM, you can technically use it for all of the cleanup settings that are set using Group Policy in the section below. I find these settings easier and more reliable to implement with Group Policy.
For SCCM, we are only concerned with the client cache size. In the Configuration Manager console, create a new Client Device Settings. Under Client Cache Settings, set the Maximum cache size (either percentage of disk or MB) to a very low value.
Deploy these settings to just your Low Disk Space device collection.
Finally, create a new group in Active Directory that has the same name as your Low Disk Space device collection. Use these steps to sync your SCCM collection to that AD group. We will use this group to apply the Group Policy cleanup tasks.
Cleaning Up Disk Space with Group Policy
In the Group Policy Management Console, create a new GPO named something like “Cleanup Computers with Low Disk Space”.
If you are using the WMI filter to target your computers, leave the Security Filtering to Authenticated Users and set the WMI filter from None to your Low Disk filter.
If you are syncing a SCCM device collection to an AD security group, remove Authenticated Users and add your group to the Security Filtering section – don’t set a WMI filter.
Edit your GPO and add the following PowerShell shutdown script:
# Remove all items in these directories
Remove-Item -recurse -force "C:\Windows\Prefetch\*"
Remove-Item -recurse -force "C:\Windows\Temp\*"
Remove-Item -recurse -force "C:\Windows\Logs\CBS\*"
#Offline Files
Remove-Item -recurse -force "C:\Windows\CSC\v2.0.6\namespace\*"
Remove-Item -recurse -force "C:\Users\*\AppData\Roaming\Microsoft\Windows\Recent Items\*"
# Remove items with exclusions
Remove-Item -recurse -force c:\Users\*\AppData\Local\* -exclude "Microsoft","Google"
Most of the files removed should be temp files. The exception is the \namespace\ line as it removes Offline files synced from a DFS namespace and the final line (exclusions line). When testing this, you may want to remove the Offline Files line and check the local user appdata folder for additional items to exclude.
Under Computer Configuration, set the following Administrative Templates – as pictured below:
In our environment, we had one final culprit that ate up disk space. Folder Redirection with Offline Files on shared desktops. To fix this, we can create a Registry Group Policy Preference to nuke the entire offline file cache. To do this, create a new computer side Registry preference:
- Hive: HKLM
- Key Path: SYSTEM\CurrentControlSet\Services\CSC\Parameters
- Value Name: FormatDatabase
- Value Type: Dword
- Value Data: 1
Most of these settings only apply on a shutdown so it might take a reboot or two to see them in action.
With all of this setup, you should no longer see computers with low disk space. This will prevent helpdesk calls and might even let you purchase smaller drives for your computers. If you have any suggestions or improvements, I would love to hear about them – just leave me a comment!
Here is a part off what I use in SCCM / PS based on the free disk space (less then 10%, I write the logs to the local drive for history purposes. Be aware, first time run can take quite some time :-). There is no impact what so ever on machine performance.
# Begin Script
$FreeSpace=Get-WmiObject win32_logicaldisk -Filter “Drivetype=3” -ErrorAction SilentlyContinue | Where-Object {($_.freespace/$_.size) -le ‘0.1’}
$View=($FreeSpace.DeviceID -join “,”).Replace(“:”,””)
##
### Check that the logs directory exists
$LogDir = “C:\Source\Logs”
If(!(test-path $LogDir))
{
New-Item -ItemType Directory -Path $LogDir -force
}
# start the cleanup process if needed
If ($FreeSpace)
{
# Log the currect % free so we can refrence back why we cleaned.
#####
$disks = gwmi win32_logicaldisk -namespace “root\CIMV2″|
Where-Object {$_.DriveType -eq 3} | Format-Table DeviceId, VolumeName,
@{n=”Size”;e={“{0} GB” -f ([math]::Round($_.Size/1GB))};a=’center’},
@{n=”FreeSpace”;e={“{0} GB” -f ([math]::Round($_.FreeSpace/1GB))};a=’center’},
@{n=”% Free”;e={“{0} %” -f ([math]::Round($_.FreeSpace/$_.Size,2)*100)};a=’center’}
$disks | Out-File “C:\source\Logs\Disk Cleanup Required.txt”
$logPath = “C:\source\Logs\Disk Cleanup Required.txt” #Path to log file; e.g. C:\source\Logs
#####
$ErrorActionPreference = ‘silentlyContinue’
SCCM client agent Section Only
End SCCM client agent Section
#Capture current time free disk space on Drive C
#$StartDate=(GET-DATE) | Out-File “C:\source\Logs\Space Before and After.txt” -Force
$logPath = “C:\source\Logs\Space Before and After.txt” #Path to log file; e.g. C:\source\Logs
$FreespaceBefore = (Get-WmiObject win32_logicaldisk -filter “DeviceID=’C:'” | select Freespace).FreeSpace/1GB
#
#Set StateFlags0012 setting for each item in Windows 10 disk cleanup utility so cleanup utility “knows” what to clean
# For full expalanation check http://support.microsoft.com/kb/253597
# Original Script, Action-CleanupBeforeSysprep.wsf used in MDT post image build, written by Mikael Nystrom & Johan Arwidmark. Converted from WSF to PS with the help of Greg Ramsey
#
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\Content Indexer Cleaner’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Device Driver Packages’ -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
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Internet Cache Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Memory Dump Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Old ChkDsk Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Previous Installations’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Recycle Bin’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Service Pack Cleanup’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Setup Log Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\System error memory dump files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\System error minidump files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Setup Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Sync Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Thumbnail Cache’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Upgrade Discarded Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\User file versions’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Defender’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Reporting Archive Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Reporting Queue Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Reporting System Archive Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Error Reporting System Queue Files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows ESD installation files’ -name StateFlags0012 -type DWORD -Value 2
set-itemproperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Windows Upgrade Log Files’ -name StateFlags0012 -type DWORD -Value 2
#
}
cleanmgr /sagerun:12
#
# When running interactive show the progress (every hour)
do {
“waiting for cleanmgr to complete. . .”
start-sleep 3600
} while ((get-wmiobject win32_process | where-object {$_.processname -eq ‘cleanmgr.exe’} | measure).count)
$FreespaceAfter = (Get-WmiObject win32_logicaldisk -filter “DeviceID=’C:'” | select Freespace).FreeSpace/1GB
# Space summary
$logPath = “C:\source\Logs\Space Before and After.txt” #Path to log file; e.g. C:\source\Logs
Logger “Free Space Before: {0} $FreespaceBefore” $false
Logger “Free Space After: {0} $FreespaceAfter” $false
#”Free Space Before: {0}” -f $FreespaceBefore | Out-File “C:\source\Logs\Space Before and After.txt” -Append
#”Free Space After: {0}” -f $FreespaceAfter | Out-File “C:\source\Logs\space Before and After.txt” -Append
# time it took to clean
$EndDate=(GET-DATE)
$logPath = “C:\source\Logs\space.txt” #Path to log file; e.g. C:\source\Logs
Logger $EndDate $false
$span = NEW-TIMESPAN –Start $StartDate –End $EndDate
$logPath = “C:\source\Logs\Space Before and After.txt” #Path to log file; e.g. C:\source\Logs
Logger $span $false
#$EndDate=(GET-DATE) | Out-File “C:\source\Logs\space.txt” -Append
#NEW-TIMESPAN –Start $StartDate –End $EndDate | Out-File “C:\source\Logs\Space Before and After.txt” -Append
}
# If no cleanup required write a log with disk summary
Else{
$disks = gwmi win32_logicaldisk -namespace “root\CIMV2″| Where-Object {$_.DriveType -eq 3} | Format-Table DeviceId, VolumeName,
@{n=”Size”;e={“{0} GB” -f ([math]::Round($_.Size/1GB))};a=’center’},
@{n=”FreeSpace”;e={“{0} GB” -f ([math]::Round($_.FreeSpace/1GB))};a=’center’},
@{n=”% Free”;e={“{0} %” -f ([math]::Round($_.FreeSpace/$_.Size,2)*100)};a=’center’}
}
$disks = $disks | Out-String #Convert to string to logging function will work
#$disks | Out-File “C:\source\Logs\Disk Space OK.txt”
$logPath = “C:\source\Logs\Disk Space OK.txt” #Path to log file; e.g. C:\source\Logs
## end of script
nice writeup thanks! any guidance on how to delete old user profiles, for shared computers (the GPO that’s supposed to do this doesn’t seem to work for us anymore, any advice or alternative?
i use delprof2 in a batch file
When the default service is not working, it is often due to something writing to those profiles. Like Jason said, delprof2 is a good solution!
I believe there is a GPO which auto deletes profiles.
Computer configuration – administrative templates – system – user profiles – “Delete user profiles older than a specified number of days on a system restart.