
Generate a Disk-Space Report Across Servers with PowerShell Remoting
Before you run this
This guide builds a read-only disk-space report. It uses PowerShell remoting (Invoke-Command over WinRM) to query each server's local fixed disks and returns size, free space, and percent free as one combined table you can export to CSV or HTML. It does not write to, resize, or clean up any disk — it only reads WMI/CIM data.
- Privileges: The report query itself needs an account with local administrator rights on the target servers (querying
Win32_LogicalDiskremotely does). In a domain, that's typically a domain account in each server's local Administrators group. Run your PowerShell session as that account, or pass a credential with-Credential. You do not need to run the console "as Administrator" on your workstation just to query, but the account you connect with must be admin on the targets. - The one configuration change here is enabling WinRM on any target that doesn't already have it (
Enable-PSRemoting). That is a real change to the server — it starts the WinRM service, sets it to automatic, and creates a firewall rule. On domain-joined servers WinRM is very often already on via Group Policy. Only enable it where it's off, and know how to reverse it (shown at the end). - Test first: Read the script before running it. Run it against one non-production server first, confirm the numbers match what you see in that server's Disk Management or
Get-Volume, then widen the scope. Don't point it at your whole fleet on the first run. - Not destructive: Nothing here deletes or edits data. The only thing you may need to undo is having enabled remoting on a host that shouldn't have it.
What I'm assuming
- Windows Server 2019/2022 targets, domain-joined.
- You run this from a domain-joined Windows 10/11 or Server workstation using Windows PowerShell 5.1 (the default
powershell.exe). It works the same in PowerShell 7, but 5.1 is what ships everywhere. - WinRM/PSRemoting is available on the targets (default for domain servers with the standard remote-management GPO). If not, see the prerequisite step.
- You're inside the same AD domain and forest, so Kerberos handles authentication.
If your servers are standalone/workgroup, cross-domain, or you connect by IP, WinRM authentication gets more involved (TrustedHosts, HTTPS listeners). That's a different setup — see Microsoft Learn, "about_Remote_Troubleshooting."
Prerequisite: confirm remoting works
Before the report, prove you can reach a target. From your workstation:
# Replace with a real server name
Test-WSMan -ComputerName server01.example.com
If that returns product/protocol version info, WinRM is answering. Then confirm you can actually run a command:
Invoke-Command -ComputerName server01.example.com -ScriptBlock { $env:COMPUTERNAME }
If WinRM is not enabled on a target, enable it on that server (locally or via a management tool), in a maintenance window:
# Run this ON the target server, in an elevated PowerShell session
Enable-PSRemoting -Force
-Force skips the confirmation prompts; it starts the WinRM service, sets it to automatic start, and adds the inbound firewall rule for the current network profiles. Don't run it blindly across the fleet — enable only where Test-WSMan failed and you've decided that host should accept remoting.
Build your server list
Keep the list in a plain text file, one hostname per line — easy to edit and to reuse in scheduled tasks:
server01.example.com
server02.example.com
server03.example.com
Save it as C:\path\to\servers.txt (substitute your own path). If you'd rather pull live from AD, you can generate the list with the ActiveDirectory module (Get-ADComputer), but a curated text file is the boring, reliable choice and won't accidentally sweep in a decommissioned box.
The report script
Save this as Get-DiskReport.ps1. It reads the server list, queries each host's fixed disks in parallel, calculates free space, and flags anything under a threshold.
# --- Settings you edit ---
$ServerListPath = 'C:\path\to\servers.txt' # your server list
$ReportFolder = 'C:\path\to\reports' # where output lands
$LowFreePercent = 15 # flag disks below this % free
# -------------------------
$servers = Get-Content -Path $ServerListPath | Where-Object { $_ -and $_.Trim() }
# This block runs ON each remote server. DriveType 3 = local fixed disk.
$diskBlock = {
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
Select-Object DeviceID, VolumeName, Size, FreeSpace
}
# Invoke-Command runs the block on all servers at once and tags each
# result with the source computer name in PSComputerName.
$raw = Invoke-Command -ComputerName $servers -ScriptBlock $diskBlock -ErrorAction SilentlyContinue
$report = $raw | ForEach-Object {
$sizeGB = [math]::Round($_.Size / 1GB, 1)
$freeGB = [math]::Round($_.FreeSpace / 1GB, 1)
# Guard against divide-by-zero on odd/empty volumes
$pctFree = if ($_.Size -gt 0) { [math]::Round(($_.FreeSpace / $_.Size) * 100, 1) } else { 0 }
[PSCustomObject]@{
Server = $_.PSComputerName
Drive = $_.DeviceID
Label = $_.VolumeName
SizeGB = $sizeGB
FreeGB = $freeGB
PctFree = $pctFree
LowSpace = ($pctFree -lt $LowFreePercent)
}
} | Sort-Object PctFree
# Show worst-first in the console
$report | Format-Table -AutoSize
# Write timestamped CSV and HTML
if (-not (Test-Path $ReportFolder)) { New-Item -Path $ReportFolder -ItemType Directory | Out-Null }
$stamp = Get-Date -Format 'yyyy-MM-dd_HHmm'
$report | Export-Csv -Path (Join-Path $ReportFolder "disk-report_$stamp.csv") -NoTypeInformation
$report | ConvertTo-Html -Title 'Disk Space Report' |
Out-File -FilePath (Join-Path $ReportFolder "disk-report_$stamp.html") -Encoding utf8
A few notes on the choices:
Win32_LogicalDiskwithDriveType=3returns only local fixed disks, skipping CDs, network drives, and removable media.Get-CimInstanceis the current, WSMan-based way to read WMI — I'm not using the deprecatedGet-WmiObject.-ErrorAction SilentlyContinueonInvoke-Commandmeans one unreachable server won't kill the whole run. That's a trade-off: unreachable hosts silently drop out. To see which failed, keep the error stream — see verification below.- The size math uses PowerShell's built-in
1GBconstant (1073741824 bytes), soSizeGB/FreeGBare binary gigabytes, matching what Explorer shows.
Run it:
# If your logged-in account is admin on the targets:
.\Get-DiskReport.ps1
# If you need a different admin credential, wire -Credential into the
# Invoke-Command call in the script (Get-Credential prompts securely).
Verify it worked
Check the console table printed by
Format-Table— one row per fixed disk per server, sorted with the fullest disks (lowestPctFree) at the top.Confirm the files exist:
Get-ChildItem C:\path\to\reports\disk-report_*.* | Sort-Object LastWriteTime -Descending | Select-Object -First 2Spot-check one server's numbers. On that server directly, run
Get-Volume(or open Disk Management) and confirm the free-space figure matches the report row. If they line up, trust the rest.Find servers that didn't answer. Compare the servers in your list against the
Servercolumn in the report:$reported = $report.Server | Sort-Object -Unique Compare-Object $servers $reportedAnything showing only on the input side didn't respond — investigate WinRM or credentials on those hosts with the
Test-WSMancheck above.
Scheduling it
To run daily, register a scheduled task under a service account that's a local admin on the targets (Task Scheduler, or Register-ScheduledTask). Point the action at:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\path\to\Get-DiskReport.ps1"
Store the service account credential in the task itself so it runs unattended. From there you can add an email step (Send-MailMessage is deprecated — use your mail platform's supported method or an SMTP client) to deliver the HTML report.
Undo
The report itself makes no changes to undo. The only reversible change is having enabled remoting. If you turned on WinRM on a host purely for this and want it off again, run this on that server in an elevated session:
# Stops and disables the WinRM listener created by Enable-PSRemoting
Disable-PSRemoting -Force
Note that Disable-PSRemoting disables the session configurations and remote access but does not stop the WinRM service or remove the firewall rule it created — if you need a full teardown to the prior state, stop/disable the WinRM service and remove the firewall rule you added, and check the "Enable-PSRemoting" and "Disable-PSRemoting" pages on Microsoft Learn for the exact residual steps for your build. On servers where remoting was already enabled by policy, leave it alone.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
