Drive full? Find what's taking up the space with one PowerShell script

A free PowerShell script that shows which folders and files are filling up your drive, as a simple list from biggest to smallest.

✓ Safe for work PCs No admin rights needed Windows 10/11 · admin optional
LargeFolderSearch.ps1
⬇ Download
How to run it
  1. 1Download
  2. 2Right-click › Run with PowerShell
  3. 3Results open in Downloads
Didn't work? Do this instead
  1. 1Copy script
  2. 2Open PowerShell from Start
  3. 3Paste and press Enter
Safety & file details

Free to use, provided as-is. It doesn't install anything or send data anywhere, and it runs without admin rights. As with any script, read it before you run it — or ask us.

PowerShell · Version 2.0 · 279 lines

SHA-256 (to check the download wasn't altered: Get-FileHash .\LargeFolderSearch.ps1)
ba6cf5c1c709c043156aa971b9eb3aeaf370f89223101118d3467530f627cb35

View the script
#Requires -Version 5.1
<#
.SYNOPSIS
    Large Folder Search - find what is filling up your drive

.DESCRIPTION
    Scans a drive (C:\ by default) and lists the folders and files taking up
    the most space, as a simple tree. Use it when the drive is full, Windows
    updates fail for lack of space, or the PC has slowed down.

    The script does NOT delete, move or change anything, and sends nothing
    anywhere. The only things it writes are the results folder and zip file.

    Works as a normal user. Run as administrator for complete totals:
    without admin rights a few system folders can't be read, so they show
    smaller than they really are (the results say so when that happens).

.NOTES
    Source   : https://techs4u.com.au/resources
    Version  : 2.0
    Requires : Windows 10/11, Windows PowerShell 5.1 or PowerShell 7+
    Licence  : MIT - free to use and modify. Provided as-is, without warranty.

    HOW TO RUN
      1. Download   - your browser saves it to your Downloads folder.
      2. Run it     - right-click LargeFolderSearch.ps1 > Run with PowerShell
                      (Windows 11: right-click > Show more options first).
      3. Results    - saved to your Downloads folder, which opens for you.

      IF RIGHT-CLICK DOESN'T WORK (the most reliable method):
      1. Copy the whole script (the "Copy script" button on the web page,
         or open the file in Notepad, Ctrl+A, Ctrl+C).
      2. Open Windows PowerShell from the Start menu.
      3. Paste it in (right-click inside the window, or Ctrl+V) and press
         Enter. If Windows warns about pasting multiple lines, choose
         "Paste anyway".

      FOR COMPLETE TOTALS (optional): right-click the Start button >
      "Terminal (Admin)" or "Windows PowerShell (Admin)", then paste the
      script as above.

    A full C: drive can take 5-15 minutes to scan.

    PRIVACY: the results list folder and file names on your PC. Review them
    before sharing.
#>

# ---- Settings: change these before running if you want ----
$ScanPath    = 'C:\'   # Drive or folder to scan
$MinFolderGB = 2       # Only show folders at least this big
$Depth       = 3       # How many folder levels deep to show
$MinFileGB   = 1       # List individual files at least this big
$TopFiles    = 25      # How many of the biggest files to list

# ============================================================================
#  TECHS4U SCRIPT STANDARD - output to Downloads, zip, Explorer, wait for Enter
# ============================================================================
$ScriptTitle = 'Large Folder Search'
$OutputName  = 'LargeFolderSearch'

function Get-DownloadsFolder {
    $path = $null
    try { $path = (New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path } catch {}
    if (-not $path -or -not (Test-Path $path)) {
        try {
            $raw = (Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -ErrorAction Stop).'{374DE290-123F-4565-9164-39C4925E467B}'
            if ($raw) { $path = [Environment]::ExpandEnvironmentVariables($raw) }
        } catch {}
    }
    if (-not $path -or -not (Test-Path $path)) { $path = Join-Path $env:USERPROFILE 'Downloads' }
    if (-not (Test-Path $path)) { $path = $env:TEMP }
    return $path
}

function Wait-BeforeClose {
    Write-Host ""
    Write-Host "Press Enter to close this window..." -ForegroundColor DarkGray
    [void](Read-Host)
}

$IsAdmin = (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

Clear-Host
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host "  $ScriptTitle"                                 -ForegroundColor Cyan
Write-Host "  techs4u.com.au"                               -ForegroundColor Cyan
Write-Host "==============================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "This script only READS folder sizes - it doesn't delete or change anything." -ForegroundColor Gray
if ($IsAdmin) {
    Write-Host "Running as administrator: full totals." -ForegroundColor Gray
} else {
    Write-Host "Running as a normal user: a few system folders may show smaller than they are." -ForegroundColor DarkYellow
}
Write-Host ""
Start-Sleep -Seconds 2

try {
    $Folder = Join-Path (Get-DownloadsFolder) ("{0}_{1}" -f $OutputName, (Get-Date -Format 'yyyyMMdd-HHmm'))
    New-Item -ItemType Directory -Path $Folder -Force -ErrorAction Stop | Out-Null
    $ResultsPath = Join-Path $Folder 'Results.txt'
    $LogPath     = Join-Path $Folder 'Log.txt'
} catch {
    Write-Host "SETUP FAILED - could not create the results folder: $($_.Exception.Message)" -ForegroundColor Red
    Wait-BeforeClose
    exit
}

function Write-Result {
    param([string]$Text, [string]$Color = 'White')
    Write-Host $Text -ForegroundColor $Color
    Add-Content -Path $ResultsPath -Value $Text -Encoding UTF8
}
function Write-Log {
    param([string]$Text)
    Add-Content -Path $LogPath -Value ("{0}  {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Text) -Encoding UTF8
}
function Format-GB {
    param([long]$Bytes)
    "{0,8:N2} GB" -f ($Bytes / 1GB)
}

if (-not (Test-Path -LiteralPath $ScanPath)) {
    Write-Host "Can't find $ScanPath - check the ScanPath setting at the top of the script." -ForegroundColor Red
    Wait-BeforeClose
    exit
}

# ---- Scan: one pass over the drive ----
# Each file is read once and its size added to every parent folder down to
# $Depth levels, so the scan doesn't re-read the drive for each level.
# Junctions/links are skipped (they point at folders already counted), and
# OneDrive "online-only" files are skipped (they don't use disk space).
$root      = (Get-Item -LiteralPath $ScanPath -Force).FullName
$rootInfo  = New-Object System.IO.DirectoryInfo $root
$sizes     = @{}
$children  = @{}
$sizes[$root]    = [long]0
$children[$root] = New-Object System.Collections.Generic.List[string]
$bigFiles  = New-Object System.Collections.Generic.List[object]
$denied    = New-Object System.Collections.Generic.List[string]
$minFolderBytes = [long]($MinFolderGB * 1GB)
$minFileBytes   = [long]($MinFileGB * 1GB)
# Offline (0x1000), RecallOnOpen (0x40000) and RecallOnDataAccess (0x400000) mark
# OneDrive online-only files. Plain numbers: the last two aren't named in the
# .NET FileAttributes enum, so casting them to that type throws.
$cloudMask      = 0x1000 -bor 0x40000 -bor 0x400000
$fileCount  = [long]0
$totalBytes = [long]0
$cloudBytes = [long]0
$start        = Get-Date
$lastProgress = Get-Date

Write-Log "Scan started: $root (admin: $IsAdmin, depth: $Depth, min folder: $MinFolderGB GB)"
Write-Host "Scanning $root - a full drive can take 5-15 minutes..." -ForegroundColor Cyan

$stack = New-Object 'System.Collections.Generic.Stack[object]'
$stack.Push([pscustomobject]@{ Dir = $rootInfo; Depth = 0; Keys = [string[]]@($root) })

while ($stack.Count -gt 0) {
    $node = $stack.Pop()
    $dir  = $node.Dir

    try {
        foreach ($f in $dir.EnumerateFiles()) {
            $fileCount++
            $len = $f.Length
            if (([int]$f.Attributes) -band $cloudMask) { $cloudBytes += $len; continue }
            $totalBytes += $len
            foreach ($k in $node.Keys) { $sizes[$k] += $len }
            if ($len -ge $minFileBytes) { $bigFiles.Add($f) }
        }
    } catch {
        $denied.Add($dir.FullName)
        Write-Log "Could not read files in '$($dir.FullName)': $($_.Exception.Message)"
    }

    try {
        foreach ($d in $dir.EnumerateDirectories()) {
            if ($d.Attributes -band [IO.FileAttributes]::ReparsePoint) { continue }
            $childDepth = $node.Depth + 1
            if ($childDepth -le $Depth) {
                $sizes[$d.FullName]    = [long]0
                $children[$d.FullName] = New-Object System.Collections.Generic.List[string]
                $children[$dir.FullName].Add($d.FullName)
                $keys = [string[]]($node.Keys + $d.FullName)
            } else {
                $keys = $node.Keys
            }
            $stack.Push([pscustomobject]@{ Dir = $d; Depth = $childDepth; Keys = $keys })
        }
    } catch {
        if (-not $denied.Contains($dir.FullName)) { $denied.Add($dir.FullName) }
        Write-Log "Could not list folders in '$($dir.FullName)': $($_.Exception.Message)"
    }

    if (((Get-Date) - $lastProgress).TotalMilliseconds -ge 500) {
        Write-Progress -Activity "Scanning $root" -Status ("{0:N0} files checked ({1:N1} GB) - {2}" -f $fileCount, ($totalBytes / 1GB), $dir.FullName)
        $lastProgress = Get-Date
    }
}
Write-Progress -Activity "Scanning $root" -Completed
$elapsed = (Get-Date) - $start
Write-Log ("Scan finished in {0:N1} minutes: {1:N0} files, {2} folders unreadable" -f $elapsed.TotalMinutes, $fileCount, $denied.Count)

# ---- Results ----
Write-Host ""
Write-Result "LARGE FOLDER SEARCH  (techs4u.com.au)" 'Cyan'
Write-Result ("Scanned {0} on {1}  |  {2:N0} files  |  took {3:N1} minutes" -f $root, (Get-Date -Format 'dd MMM yyyy HH:mm'), $fileCount, $elapsed.TotalMinutes) 'DarkGray'
try {
    $di = New-Object System.IO.DriveInfo $root
    if ($di.IsReady) {
        $usedPct = [math]::Round((1 - $di.AvailableFreeSpace / $di.TotalSize) * 100)
        Write-Result ("Drive {0}  size {1:N1} GB  |  free {2:N1} GB  |  {3}% used{4}" -f $di.Name, ($di.TotalSize / 1GB), ($di.AvailableFreeSpace / 1GB), $usedPct, $(if ($usedPct -ge 90) { "  <-- VERY LOW SPACE" } else { "" })) 'DarkGray'
    }
} catch {}
if ($cloudBytes -gt 0) {
    Write-Result ("Skipped {0:N1} GB of OneDrive online-only files (they don't use disk space)." -f ($cloudBytes / 1GB)) 'DarkGray'
}
if (-not $IsAdmin -and $denied.Count -gt 0) {
    Write-Result ("{0} folders couldn't be read without admin rights, so some totals are lower than real." -f $denied.Count) 'Yellow'
    Write-Result "For full totals: right-click Start > Terminal (Admin), then paste the script again." 'Yellow'
}
Write-Result ""
Write-Result ("FOLDERS OF {0} GB OR MORE (up to {1} levels deep)" -f $MinFolderGB, $Depth) 'Green'
Write-Result ("=" * 64) 'Green'

function Write-Tree {
    param([string]$Parent, [string]$Indent, [int]$Level)
    if (-not $children.ContainsKey($Parent)) { return }
    $kids = $children[$Parent] | Where-Object { $sizes[$_] -ge $minFolderBytes } | Sort-Object { $sizes[$_] } -Descending
    foreach ($k in $kids) {
        $color = if ($Level -eq 1) { 'White' } else { 'Cyan' }
        Write-Result ("{0}[{1}] {2}" -f $Indent, (Format-GB $sizes[$k]), (Split-Path $k -Leaf)) $color
        Write-Tree -Parent $k -Indent ($Indent + "    ") -Level ($Level + 1)
        if ($Level -eq 1) { Write-Result "" }
    }
}
Write-Tree -Parent $root -Indent "" -Level 1

Write-Result ""
Write-Result ("LARGEST FILES ({0} GB OR MORE)" -f $MinFileGB) 'Green'
Write-Result ("=" * 64) 'Green'
if ($bigFiles.Count -gt 0) {
    $bigFiles | Sort-Object Length -Descending | Select-Object -First $TopFiles | ForEach-Object {
        Write-Result ("[{0}] {1}" -f (Format-GB $_.Length), $_.FullName) 'Cyan'
    }
} else {
    Write-Result "None found." 'DarkGray'
}

Write-Result ""
Write-Result "WHERE TO START" 'Green'
Write-Result ("=" * 64) 'Green'
Write-Result "- Usually safe to clear yourself: your Downloads folder, the Recycle Bin, and"
Write-Result "  Settings > System > Storage > Temporary files."
Write-Result "- C:\Windows\WinSxS often looks bigger than it really is. Don't delete it by hand."
Write-Result "- Don't delete anything inside C:\Windows or C:\Program Files manually."
Write-Result "- Not sure? Send this file to your IT support (or techs4u.com.au/contact)."

# ============================================================================
#  FINISH - zip, show the result, wait for Enter
# ============================================================================
Write-Host ""
$ZipPath = "$Folder.zip"
try { Compress-Archive -Path (Join-Path $Folder '*') -DestinationPath $ZipPath -Force -ErrorAction Stop }
catch { $ZipPath = $null }

Write-Host "========================================" -ForegroundColor Green
Write-Host "DONE. Results are in your Downloads folder:" -ForegroundColor Green
Write-Host "  $ResultsPath" -ForegroundColor Green
if ($ZipPath) {
    Write-Host "To send them to someone, use this one file:" -ForegroundColor Green
    Write-Host "  $ZipPath" -ForegroundColor Green
}
Write-Host "========================================" -ForegroundColor Green
Start-Process explorer.exe -ArgumentList "/select,`"$(if ($ZipPath) { $ZipPath } else { $ResultsPath })`""
# Kept on one line so a pasted run pauses here correctly.
Wait-BeforeClose; exit

When the drive fills up, Windows slows down and updates start failing. This script scans the drive and lists the biggest folders and files, so you can see straight away where the space has gone.

Tip: a full C: drive can take 5–15 minutes to scan. Leave the window open until it says DONE.

When to use it

  • Windows says you're running out of space
  • Updates fail with "not enough disk space"
  • The PC has slowed down and the drive is nearly full

What you get

  • The biggest folders, up to 3 levels deep, largest first
  • The 25 biggest files (1 GB or more)
  • How full the drive is

It doesn't delete or move anything. You decide what to clean up.

For complete totals (optional)

It works as a normal user, but a few system folders can't be read without admin rights, so they show smaller than they are. The results tell you if that happened. For full totals, right-click the Start button, choose Terminal (Admin) or Windows PowerShell (Admin), and paste the script there.

Before you delete anything

  • Usually safe: your Downloads folder, the Recycle Bin, and Settings → System → Storage → Temporary files.
  • Don't delete anything inside C:\Windows or C:\Program Files by hand.
  • C:\Windows\WinSxS often looks bigger than it really is. Leave it alone.

Not sure what's safe to remove? Send us the results and we'll tell you.

Privacy

The results list folder and file names on your PC. Check them before sharing.

Was this helpful?
🚩 Report a problem with this script
0 / 2000

Still stuck? We can fix it for you remotely, usually the same day. See our Remote computer support service.

Get help