A drive that runs out of space rarely announces itself politely. One moment everything works, the next an application cannot write its files, a database refuses transactions, updates stall, and on a server the whole machine may grind to a halt. The cruel part is that the warning signs were there for days, written plainly in the steadily shrinking free-space figure that nobody happened to look at. An automated free-space report is the mechanism that turns that ignored number into an alert delivered to a human before the crisis, rather than a postmortem discovered after it.

The reason to automate this rather than glance at drives by hand is the same reason any monitoring gets automated: human attention is unreliable and intermittent, while a scheduled script is patient and exact. Anyone who has run servers for long has watched one crash because its disk quietly filled while no one was watching, unable to create temporary files, store data, or complete maintenance. A report that runs every day and speaks up only when a threshold is crossed converts that recurring risk into a routine notification, giving an administrator the lead time to clear space or expand the volume calmly.

Reading Free Space the Right Way

The foundation of any free-space report is a reliable reading of how much room each volume has left. PowerShell offers several ways to obtain this, and the modern, robust choice queries the logical disks through the system management interface, filtering to fixed local drives so that removable and network drives do not clutter the report. From each disk it pulls the total size and the free bytes, which together yield both an absolute figure and a percentage.

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
    Select-Object DeviceID,
        @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,2)}},
        @{N='FreePercent';E={[math]::Round(($_.FreeSpace/$_.Size)*100,1)}}

Reporting both an absolute and a percentage figure matters more than it first appears. A small system volume at ten percent free might have only a couple of gigabytes left, which is genuinely dangerous, while a large data volume at ten percent free could still hold hundreds of gigabytes and be in no immediate trouble. A report that shows both numbers lets the reader judge severity correctly, and a well-designed alert threshold often combines the two so that it fires on whichever condition is more urgent for a given drive.

It is worth choosing the query command deliberately. The management-interface command is the modern, reliable choice and works cleanly across remote machines, whereas the older equivalent it superseded is legacy and best left out of new scripts. Building the report on the current command keeps it dependable today and ready to extend to a fleet of machines tomorrow without rewriting its core logic.

Deciding When the Report Should Speak

A report that emails its full contents every single day quickly becomes noise that everyone learns to ignore. The far more useful design stays silent when all is well and speaks only when a drive crosses a threshold, so that any message in the inbox is by definition worth reading. The script therefore filters the drives down to those whose free space has fallen below a chosen limit, and only proceeds to notify if that filtered set contains anything at all.

$threshold = 10
$lowDrives = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
    Where-Object { ($_.FreeSpace / $_.Size) * 100 -lt $threshold }

if ($lowDrives) {
    # build and send the alert
}

Choosing the threshold well is part judgment, part experience. Set it too high and the alert fires constantly on drives that are merely comfortably used, training the reader to dismiss it; set it too low and the warning arrives with too little runway to act before the drive fills. A common and sensible starting point is ten percent free, generous enough to leave breathing room yet tight enough to mean something, with the value tuned per environment as real behavior reveals how fast each drive actually fills.

There is a subtle trap worth anticipating. Some drives fill so suddenly, a runaway log or an unexpected dump, that a once-a-day check can miss the window between the last quiet check and the moment the disk is full. Where a drive is known to be volatile, the answer is not a higher threshold, which only produces false alarms on stable drives, but a more frequent check for that machine, so the report runs often enough to catch a fast fill before it completes.

Composing a Clear, Readable Alert

When the report does have something to say, the message should make the problem obvious at a glance rather than burying it in raw output. Formatting the offending drives into a tidy table, with the machine name, the drive letter, the free gigabytes, and the free percentage, turns a data dump into something a busy administrator can absorb in seconds. Including the computer name is especially important when the same report runs on many machines, since the recipient must know instantly which server is in trouble.

$body  = "The following drives on $env:COMPUTERNAME are low on space:`n`n"
$body += $lowDrives |
    Select-Object @{N='Drive';E={$_.DeviceID}},
        @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,2)}},
        @{N='FreePercent';E={[math]::Round(($_.FreeSpace/$_.Size)*100,1)}} |
    Format-Table -AutoSize | Out-String

The subject line carries its own weight, because many people triage by subject alone. A subject that names the machine and states the problem plainly, such as a low-disk warning identifying the server and drive, lets the recipient grasp the situation without even opening the message. For richer presentation the body can be built as an HTML table rather than plain text, which renders as a clean, color-coded layout in most mail clients and makes a multi-drive alert easy to scan.

Whatever the format, the guiding principle is that the alert exists to provoke action, so it should lead with what is wrong, where, and how urgent, and leave any supporting detail below. An alert that requires interpretation before its meaning is clear has partly defeated its own purpose.

Delivering the Notification Where It Will Be Seen

A perfectly composed alert is useless if it lands somewhere no one looks. The classic delivery method is email, sent through the organization's mail server to one or more recipients. PowerShell's mail-sending command makes this straightforward, accepting the sender, recipients, subject, body, and mail server, and it can flag the message as high priority so it stands out among ordinary traffic.

$mail = @{
    SmtpServer = 'smtp.example.com'
    From       = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.'
    To         = @(Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.',Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.')
    Subject    = "[DISK ALERT] $env:COMPUTERNAME low on space"
    Body       = $body
    Priority   = 'High'
}
Send-MailMessage @mail

Sending to several recipients at once, by passing an array of addresses, guards against the alert being missed because the one person who would have read it is away. On networks without a convenient mail relay, the same logic can deliver the warning through other channels instead, writing a prominent entry to the system event log where existing monitoring tools already watch, or pushing a message into whatever chat or ticketing system the team actually uses day to day.

A practical caution applies whichever channel is chosen: the delivery path itself must be tested under realistic conditions. A firewall that blocks the mail server, an expired credential, or a misrouted address can silently swallow every alert, so it is wise to force the script to fire once, by temporarily setting an impossibly generous threshold, and confirm the message truly arrives. An alert mechanism that has never been observed to work is an act of faith, not a safeguard.

Extending the Report Across Many Machines at Once

A report that runs locally on each machine works well, but in a larger environment it is often more practical to gather free space from many machines into a single consolidated view. The same management-interface query accepts a remote computer name, so one script run from a central host can poll a list of servers in turn and assemble their results into one report. This shifts the model from many small local alerts to a single overview that an administrator can scan in one place.

$servers = Get-Content 'C:\Scripts\servers.txt'
$report = foreach ($server in $servers) {
    Get-CimInstance -ComputerName $server -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
        Where-Object { ($_.FreeSpace / $_.Size) * 100 -lt 10 } |
        Select-Object @{N='Server';E={$server}}, DeviceID,
            @{N='FreePercent';E={[math]::Round(($_.FreeSpace/$_.Size)*100,1)}}
}

Reading the server list from an external file rather than hard-coding it keeps the report easy to maintain, since adding or removing a machine becomes a matter of editing a text file rather than the script. In a domain, the list can even be generated dynamically by querying the directory for all server computers, so the report automatically covers new machines as they are added without any manual update at all.

The centralized approach does introduce dependencies worth noting. Remote queries require that the polling account has rights on the target machines and that the network permits the connection, so a server that is unreachable or refuses the query will simply be absent from the report rather than confirmed healthy. A robust consolidated report therefore distinguishes a machine that reported plenty of free space from one that could not be reached at all, since the latter is its own kind of problem that silent omission would hide.

Putting the Report on a Schedule

The final piece is making the report run on its own, since a check that depends on someone remembering to launch it offers little protection. The task scheduler runs the script on a recurring trigger, and registering the task from PowerShell keeps the whole arrangement reproducible across machines. The task combines an action that invokes the script, a daily trigger at a fixed time, and a principal that grants it the privileges it needs.

$action  = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\diskreport.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At 8:00AM

Register-ScheduledTask -TaskName 'DiskSpaceReport' `
    -Action $action -Trigger $trigger -User 'SYSTEM' -RunLevel Highest `
    -Description 'Daily free-space report with low-space alert'

Two choices in that registration deserve attention. Running the task under a system account with elevated rights ensures the script can query every drive without permission errors that would otherwise leave some volumes silently unreported. Scheduling it for early morning means the report has already run and any warning is waiting before the working day begins, so an overnight slide toward a full disk is known the moment it could matter rather than discovered hours later.

Because the script and its scheduled task are both defined in code, the identical setup can be deployed across an entire fleet, bringing every machine under the same daily watch with the same threshold and the same delivery. This uniformity is itself valuable: it removes the drift that creeps in when each server is configured by hand, and it guarantees that no machine is quietly left unmonitored simply because someone forgot to set it up.

The Quiet Payoff of Watching Free Space Automatically

The deeper point of automating free-space reporting is that the cheapest moment to deal with a filling disk is well before it is full, and the only way to reliably catch that moment is to look every day without fail. Free space erodes slowly and then, sometimes, all at once, and a number that no one reads protects no one. A scheduled report reading that number daily and alerting only on trouble converts an invisible, creeping risk into a visible, actionable one.

A well-built report reads free space reliably through the modern interface, judges severity by both absolute and percentage figures, stays silent until a real threshold is crossed, formats its warning to be understood instantly, and delivers it where someone will actually see it. None of these elements is complicated on its own, yet together they form a dependable early-warning system that asks nothing of the administrator from one day to the next.

That is the appeal of handing this dull but vital chore to a script. It does not forget, does not tire, and does not need prompting; it simply checks each morning and stays quiet until it has something worth saying. When that day arrives, the warning comes with enough lead time to act without drama, and a potential outage is reduced to a routine task of clearing space or growing a volume. The modest effort of building and scheduling the report repays itself many times over the first time it catches a drive on its way to full.