New Tool – Disk Hogs

Edit: Heavily modified script for speed. Bulk of script is now running Compiled C# Code.

Been resolving some problems at work lately with respect to full disks. One of our charters is to manage the ConfigMgr cache sizes on each machine to ensure that the packages we need to get replicated, actually get replicated out to the right machines at the right time.

But we’ve been getting some feedback about one 3rd party SCCM caching tool failing in some scenarios. Was it really the 3rd party tool failing, or some other factor?

Well we looked at the problem and found:

  • Machines with a modest 120GB SSD Drive (most machines have a more robust 250GB SSD)
  • Configuration Manager Application Install packages that are around 10-5GB (yowza!)
  • Users who leave too much… crap laying around their desktop.
  • And several other factors that have contributed to disks getting full.

Golly, when I try to install an application package that requires 12GB to install, and there is only 10GB free, it fails.

Um… yea…

I wanted to get some data for machines that are full: What is using up the disk space? But it’s a little painful searching around a disk for directories that are larger than they should be.

Options

One of my favorite tools is “WinDirStat” which produces a great graphical representation of a disk, allowing you to visualize what directories are taking up the most space, and which files are the largest.  http://windirstat.net

Additionally I also like the “du.exe” tool from SysInternals.  https://live.sysinternals.com/du.exe

I wrap it up in a custom batch script file

@%~dps0du.exe -l 1 -q -accepteula %*

and it produces output that looks like:

PS C:\Users> dudir
    263,122 C:\Users\Administrator
      1,541 C:\Users\Default
  7,473,508 C:\Users\keith
      4,173 C:\Users\Public
  7,742,345 C:\Users
Files: 27330
Directories: 5703
Size: 7,928,161,747 bytes
Size on disk: 7,913,269,465 bytes

Cool, however, I wanted something that I could run remotely, and that would give me just the most interesting directories, say everything over 1GB, or something configurable like that.

So a tool was born.

Tool

The script will enumerate through all files on a local machine and return the totals. Along the way we can add in rules to “Group” interesting directories and output the results.

So, say we want to know if there are any folders under “c:\program files (x86)\Adobe\*” that are larger than 1GB. For the most part, we don’t care about Adobe Reader, since it’s under 1GB, but everything else would be interesting. Stuff like that.

We have a default set of rules built into the script, but you can pass a new set of rules into the script using a *.csv file ( I use excel )

Folder SizeMB
c:\* 500
C:\$Recycle.Bin 100
c:\Program Files 0
C:\Program Files\* 1000
C:\Program Files (x86) 0
C:\Program Files (x86)\Adobe\* 1000
C:\Program Files (x86)\* 1000
C:\ProgramData\* 1000
C:\ProgramData 0
C:\Windows 0
C:\Windows\* 1000
c:\users 0
C:\Users\* 100
C:\Users\*\* 500
C:\Users\*\AppData\Local\Microsoft\* 1000
C:\Users\*\AppData\Local\* 400

Example output:

The machine isn’t too interesting (it’s my home machine not work machine)

I’m still looking into tweaks and other things to modify in the rules to make the output more interesting.

  • Should I exclude \windows\System32 directories under X size?
  • etc…

If you have feedback, let me know

Script


<#
.SYNOPSIS
Report on Disk Hogs
.DESCRIPTION
Returns a list of the largest directories in use on the local machine
.NOTES
Copyright Keith Garner, All rights reserved.
Really Updated for Windows 7 and Optimized for !!!SPEED!!!
.PARAMETER Path
Start of the search, usually c:\
.PARAMETER IncludeManifest
Include basic info about the memory, OS, and Disk in the manifest
.PARAMETER OutFile
CLIXML file used to store results
Location of a custom rules *.csv file, otherwise use the default table
.LINK
https://keithga.wordpress.com
#>
[cmdletbinding()]
param(
$path = 'c:\',
[switch] $IncludeManifest,
$OutFile
)
###########################################################
$WatchList = @(
@{ Folder = 'c:\'; SizeMB = '0' }
@{ Folder = 'c:\*'; SizeMB = '500' }
@{ Folder = 'C:\$Recycle.Bin'; SizeMB = '100' }
@{ Folder = 'c:\Program Files'; SizeMB = '0' }
@{ Folder = 'C:\Program Files\*'; SizeMB = '1000' }
@{ Folder = 'C:\Program Files (x86)'; SizeMB = '0' }
@{ Folder = 'C:\Program Files (x86)\Adobe\*'; SizeMB = '1000' }
@{ Folder = 'C:\Program Files (x86)\*'; SizeMB = '1000' }
@{ Folder = 'C:\ProgramData\*'; SizeMB = '1000' }
@{ Folder = 'C:\ProgramData'; SizeMB = '0' }
@{ Folder = 'C:\Windows'; SizeMB = '0' }
@{ Folder = 'C:\Windows\*'; SizeMB = '1000' }
@{ Folder = 'c:\users'; SizeMB = '0' }
@{ Folder = 'C:\Users\*'; SizeMB = '100' }
@{ Folder = 'C:\Users\*\*'; SizeMB = '500' }
@{ Folder = 'C:\Users\*\AppData\Local\Microsoft\*'; SizeMB = '1000' }
@{ Folder = 'C:\Users\*\AppData\Local\*'; SizeMB = '400' }
)
###########################################################
Add-Type TypeDefinition @"
public class EnumFolder
{
public static System.Collections.Generic.Dictionary<string, long> ListDir(string Path, System.Collections.Generic.Dictionary<string, long> ControlList)
{
System.Collections.Generic.Dictionary<string, long> Results = new System.Collections.Generic.Dictionary<string, long>();
System.IO.DirectoryInfo Root = new System.IO.DirectoryInfo(Path);
ListDirRecursive(Root, Results, ControlList);
return Results;
}
private static long ListDirRecursive
(
System.IO.DirectoryInfo Path,
System.Collections.Generic.Dictionary<string, long> Results,
System.Collections.Generic.Dictionary<string, long> ControlList
)
{
try
{
long Total = 0;
foreach (System.IO.DirectoryInfo Directory in Path.GetDirectories())
if ((Directory.Attributes & System.IO.FileAttributes.ReparsePoint) == 0)
Total += ListDirRecursive(Directory, Results, ControlList);
foreach (System.IO.FileInfo file in Path.GetFiles())
{
if ((file.Attributes & System.IO.FileAttributes.ReparsePoint) == 0)
{
if (ControlList.ContainsKey(file.FullName))
{
if ((ControlList[file.FullName] * 1024 * 1024) < file.Length)
{
Results.Add(file.FullName, file.Length);
}
else
{
Total += file.Length;
}
}
else
{
Total += file.Length;
}
}
}
if (ControlList.ContainsKey(Path.FullName))
{
if ((ControlList[Path.FullName] * 1024 * 1024) < Total)
{
Results.Add(Path.FullName, Total);
Total = 0;
}
}
return Total;
}
catch
{
return 0;
}
}
}
"@
###########################################################
$start = [datetime]::Now
$ControlList = new-object TypeName 'System.Collections.Generic.Dictionary[String,int64]'
foreach ( $Item in $WatchList ) {
if ( $item.Folder.EndsWith('*') ) {
get-childitem $Item.Folder.TrimEnd('*') force ErrorAction SilentlyContinue |
ForEach-Object {
$_.FullName.Substring(0,1).ToLower() + $_.FullName.Substring(1)
} |
Where-Object { -not $ControlList.ContainsKey( $_ ) } |
foreach-object { $ControlList.Add($_,0 + $Item.SizeMB) }
}
else {
get-item $Item.Folder force ErrorAction SilentlyContinue |
ForEach-Object {
$_.FullName.Substring(0,1).ToLower() + $_.FullName.Substring(1)
} |
Where-Object { -not $ControlList.ContainsKey( $_ ) } |
foreach-object { $ControlList.Add($_,0 + $Item.SizeMB) }
}
}
$ControlList.Keys | write-verbose
###################
$Results = [EnumFolder]::ListDir($Path.ToLower(), $ControlList )
$Results | write-output
([datetime]::now $Start).TotalSeconds | Write-verbose
###################
if ( $OutFile ) {
new-item ItemType Directory Path ( split-path $OutFile ) ErrorAction SilentlyContinue | Out-Null
if ( $IncludeManifest ) {
@{
OS = GWMI Win32_OPeratingSystem | Select OSarchitecture,OSLanguage,InstallDate,Version
Mem = GWMI Win32_PhysicalMemory | Select Capacity
Vol = GWMI Win32_LogicalDisk Filter "DeviceID='$($path.Substring(0,1))`:'" | Select Size,FreeSpace,VolumeName
Data = $Results
} | Export-Clixml Path $OutFile
}
else {
$Results | Export-Clixml Path $OutFile
}
}

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s