Disk Usage Report Task

I’d like to have a disk report available as a task or diagnostic task that will show what folders / files on the alerting volume are using the most amount of space. I did see the below question that was similar and have seen the folder usage script but was curious if a “ready to go” script was available already before I try to create it myself with the PowerShell MP.

Community Answers
https://github.com/squaredup/Community.PowerShellMonitoring.MP/blob/master/Samples/Diagnostics/Get-SubFolderUsage.ps1

Similar Question (wouldn’t show up so put in quotes): “https://community.squaredup.com/answers/question/powershell-script-for-large-files-and-folders/”

I think the challenge is going to be finding a script which can return the data you want in a timely fashion. Anything which is called on-demand is going to take quite some time to execute as it has to traverse the entire disk and all of the subfolders.

I’d probably be more inclined to go the route of using a scheduled task that runs periodically and creates such a report; you could then have an on-demand task which just pulls the data from the report.

Hi,

I made a small script that runs as a diagnostic task whenever a disk full alert is raised. It sends a e-mail report to our admin-team.

param([string]$Arguments)

$startDirectory = $Arguments + ‘’

$numberOfTopDirectories = 5
$numberOfTopFiles = 5
$emailTo = ‘adminteam@contoso.msft’
$smtpSrv = ‘mailer.contsole.msft’
$emailFrom = ‘diskSpaceDetail@scom.contoso.msft’

#region Get-Metadata

$timeZone = ([TimeZoneInfo]::Local).Id
$scanDate = Get-Date -Format ‘yyyy-MM-dd hh:MM:ss’
$WindowsVersion = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption

try {
$computerDescription = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\services\LanmanServer\Parameters | Select-Object -ExpandProperty srvcomment
} catch {
$computerDescription = ‘’
}

try {
$adSearcher = New-Object System.DirectoryServices.DirectorySearcher
$adSearcher.Filter = “(&(objectCategory=computer)(cn=$env:computername))”
$adComputer = $adSearcher.FindOne()
$adComputerProperties = $adComputer | Select-Object -ExpandProperty Properties
$adComputerLdapPath = $adComputerProperties.distinguishedname
} catch {
$adComputerLdapPath = ‘Failed to extract AD Information.’ + $_.StackTrace
}

$diskDetails = Get-WMIObject -Namespace root/cimv2 -Class Win32_LogicalDisk | Where-Object {$_.DeviceID -match “$($Arguments)” } | Select-Object -Property Size, FreeSpace, VolumeName, DeviceID
$DriveLetter = $diskDetails.DeviceID
$DriveName = $diskDetails.VolumeName
$SizeInGB = “{0:0}” -f ($diskDetails.Size/1GB)
$FreeSpaceInGB = “{0:0}” -f ($diskDetails.FreeSpace/1GB)
$PercentFree = “{0:0}” -f (($diskDetails.FreeSpace / $diskDetails.Size) * 100)

$diskMessage = “<table><tr><td>$($DriveLetter)</td><td> ($($DriveName))</td></tr>”
$diskMessage += “<tr><td>Total: $($SizeInGB) GB |</td><td>Free: $($FreeSpaceInGB) GB ($($PercentFree) %)</td></tr></table>”

#endregion Get-Metadata

Function Get-BigDirectories {

param(
	[string]$startDirectory,
	[ref]$sortedDirectories
)

$bigDirList = New-Object -TypeName System.Collections.ArrayList

if (Test-Path -Path $startDirectory) {

	&amp; "$env:ComSpec" /c dir $startDirectory /-c /s | ForEach-Object {

			$null      = $_ -match 'Directory\s{1}of\s{1}(?&lt;dirName&gt;[\w:\\\s\.\-\(\)_#{}\$\%\+\[\]]{1,})'
			$dirName   = $Matches.dirName

			$null      = $_ -match '\s{1,}\d{1,}\sFile\(s\)\s{1,}(?&lt;lengh&gt;\d{1,})'
			$dirLength = $Matches.lengh

			if ($dirName -and $dirLength) {

				$dirLength = [float]::Parse($dirLength)
				$myFileHsh = @{'Name'=([string]$dirName)}
				$myFileHsh.Add('Length',$dirLength)
				$myFileObj = New-Object -TypeName PSObject -Property $myFileHsh
				$null = $bigDirList.Add($myFileObj)

			}

			$dirName = ''
			$dirLength = 0

	} #END cmd /c dir C:\windows /-c /s | ForEach-Object

} else {

	if ($startDirectory) {
		$dirName   = 'Error'
		$dirLength = 'No directory passed in.'
	} else  {
		$dirName   = $startDirectory
		$dirLength = $error.Message.ToString()
	}

	$dirLength = [float]::Parse($dirLength)
	$myFileHsh = @{'Name'=([string]$dirName)}
	$myFileHsh.Add('Length',$dirLength)
	$myFileObj = New-Object -TypeName PSObject -Property $myFileHsh

	$null = $bigDirList.Add($myFileObj)

} #END if (Test-Path -Path $startDirectory)

$sortedDirectories.Value = $bigDirList

} #End Function Get-BigDirectories

Function Convert-LengthToReadable {

param(
	[System.Collections.ArrayList]$lengthList,
	[ref]$readableList
)

$allFiles = New-Object -TypeName System.Collections.ArrayList

$lengthList | ForEach-Object {

	$sizeRaw  = $_.Length
	$sizeUnit = 'KB'
	$fileSize = 0

	if ($sizeRaw -gt 1kb -and $sizeRaw -lt 1mb ) {
		$fileSize = $sizeRaw / 1mb
		$sizeUnit = 'MB'
	} elseif ($sizeRaw -gt 1mb -and $sizeRaw -lt 1gb ) {
		$fileSize = $sizeRaw / 1mb
		$sizeUnit = 'MB'
	} elseif ($sizeRaw -gt 1gb -and $sizeRaw -lt 1tb ) {
		$fileSize = $sizeRaw / 1gb
		$sizeUnit = 'GB'
	} elseif ($sizeRaw -gt 1tb ) {
		$fileSize = $sizeRaw / 1tb
		$sizeUnit = 'TB'
	} else {
		$fileSize = $sizeRaw
		$sizeUnit = 'KB?'
	}

	$fileSize = [Math]::Round($fileSize,2)
	$myFileHsh = @{'Name'=([string]$_.Name)}
	$myFileHsh.Add('fileSize',([float]$fileSize))
	$myFileHsh.Add('sizeUnit',([string]$sizeUnit))
	$myFileHsh.Add('Length',([float]$sizeRaw))
	$myFileHsh.Add('LastWriteTime',$_.LastWriteTime)
	$myFileObj = New-Object -TypeName PSObject -Property $myFileHsh
	$null = $allFiles.Add($myFileObj)

}

$readableList.Value = $allFiles

} #End Function Convert-LengthToReadable

Function Send-TopDirectoryMailReport {

param(
	[string]$diskMetaData,
	[string]$runInfo,
	[System.Array]$tDirectories,
	[System.Collections.Hashtable]$tDirsAndFiles,
	[System.Collections.Hashtable]$nDirsAndFiles
)

$directoryDetails   = ''
$dirAndFilesDetails = ''
$dirAndNewFilesDetails = ''

foreach ($dirItem in $tDirectories) {

	$dirAndFilesDetails    += "&lt;tr&gt;&lt;td style=`"font-weight: bold; text-align:center`"&gt;&lt;br /&gt;&amp;nbsp;$($dirItem.Name)&lt;/td&gt;&lt;/tr&gt;"
	$dirAndNewFilesDetails += "&lt;tr&gt;&lt;td style=`"font-weight: bold; text-align:center`"&gt;&lt;br /&gt;&amp;nbsp;$($dirItem.Name)&lt;/td&gt;&lt;/tr&gt;"
	$directoryDetails      += "&lt;tr&gt;&lt;td&gt;$($dirItem.Name)&lt;/td&gt;&lt;td&gt;$($dirItem.fileSize)&lt;/td&gt;&lt;td&gt;$($dirItem.sizeUnit)&lt;/td&gt;&lt;tr&gt;"

	$matchEntry = $tDirsAndFiles.($dirItem.Name)
	$matchEntry | ForEach-Object {
		$dirAndFilesDetails += "&lt;tr&gt;&lt;td&gt;$($_.Name)&lt;/td&gt;&lt;td&gt;$($_.fileSize)&lt;/td&gt;&lt;td&gt;$($_.sizeUnit)&lt;/td&gt;&lt;td&gt;$($_.LastWriteTime)&lt;/td&gt;&lt;tr&gt;"
	}

	$matchEntry = $nDirsAndFiles.($dirItem.Name)
	$matchEntry | ForEach-Object {
		$dirAndNewFilesDetails += "&lt;tr&gt;&lt;td&gt;$($_.Name)&lt;/td&gt;&lt;td&gt;$($_.fileSize)&lt;/td&gt;&lt;td&gt;$($_.sizeUnit)&lt;/td&gt;&lt;td&gt;$($_.LastWriteTime)&lt;/td&gt;&lt;tr&gt;"
	}

} #End 	foreach ($dirItem in $tDirectories)

$htmlBegin   = "&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;title&gt;DISK FULL - Troubleshooting Assistance on $($env:Computername) -  $($computerDescription)&lt;/title&gt;"
$htmlBegin  += "&lt;h1&gt;&lt;span style=`"background-color:#D3D3D3`"&gt;DISK FULL - Troubleshooting Assistance on $($env:Computername)&lt;/span&gt;&lt;/h1&gt;&lt;/head&gt;"
$htmlBegin  += "&lt;h3&gt;&lt;span style=`"background-color:#D3D3D3`"&gt; $($computerDescription) &lt;/span&gt;&lt;/h3&gt;&lt;/head&gt;"

$htmlMiddle  = '&lt;body style="color:#000000; font-size:12pt;"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;span style="color:#B22222; font-weight: bold; background-color:#D3D3D3; font-size:14pt;"&gt;Disk details:&lt;/span&gt;&lt;br /&gt;' + $diskMetaData + '&lt;p&gt;&amp;nbsp;&lt;/p&gt;'
$htmlMiddle += '&lt;span style="color:#000080; font-weight: bold; background-color:#D3D3D3; font-size:14pt;"&gt;Largest Directories:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;table&gt;' + $directoryDetails + '&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;'
$htmlMiddle += '&lt;span style="color:#000080; font-weight: bold; background-color:#D3D3D3; font-size:14pt;"&gt;Newest files:&lt;/span&gt;&lt;br /&gt;&lt;table&gt;' + $dirAndNewFilesDetails + '&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;'
$htmlMiddle += '&lt;span style="color:#000080; font-weight: bold; background-color:#D3D3D3; font-size:14pt;"&gt;Largest files:&lt;/span&gt;&lt;br /&gt;&lt;table&gt;' + $dirAndFilesDetails + '&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;'
$htmlMiddle += '&lt;span style="color:#FF8C00; font-weight: bold; background-color:#D3D3D3; font-size:14pt;"&gt;Meta Information:&lt;br /&gt;&lt;/span&gt;' + $runInfo
$htmlEnd     = '&lt;/body&gt;&lt;/html&gt;'

$htmlCode = $htmlBegin + $htmlMiddle + $htmlEnd

$mailMessageParms = @{
	To          = $emailTo
	From        = $emailFrom
	Subject     = "DISK Full - Troubleshooting Assistance on $($env:computername) . $($computerDescription)"
	Body        = $htmlCode
	Smtpserver  = $smtpSrv
	ErrorAction = "SilentlyContinue"
	BodyAsHTML  = $true
}

Send-MailMessage<a class="ap-mention-link" href="https://community.squaredup.com/user/mailMessageParms">@mailMessageParms</a> 

} #End Send-TopDirectoryMailReport

$startTime = Get-Date

$bigDirList = New-Object -TypeName System.Collections.ArrayList
Get-BigDirectories -startDirectory $startDirectory -sortedDirectories ([ref]$bigDirList)

$bigDirListReadable = New-Object -TypeName System.Collections.ArrayList
Convert-LengthToReadable -lengthList $bigDirList -readableList ([ref]$bigDirListReadable)

$topDirectories = $bigDirListReadable | Sort-Object -Property Length -Descending | Select-Object -First $numberOfTopDirectories
$topDirsAndFiles = New-Object -TypeName System.Collections.Hashtable

$topDirsAndNewestFiles = New-Object -TypeName System.Collections.Hashtable

foreach ($tDirectory in $topDirectories) {

$tDirName       = $tDirectory.Name

$tmpFileList    = New-Object -TypeName System.Collections.ArrayList
$tmpNewFileList = New-Object -TypeName System.Collections.ArrayList
$newFilesInDir  = New-Object -TypeName System.Collections.ArrayList

$filesInTDir    = Get-ChildItem -Path $tDirName | Where-Object { $_.PSIsContainer -eq $false } | Select-Object -Property DirectoryName, Name, LastWriteTime, Length
$filesInTDir	  | ForEach-Object {
	$null = $newFilesInDir.Add($_)
}

$filesInTDir    = $filesInTDir   | Sort-Object -Property Length        -Descending | Select-Object -First $numberOfTopFiles
$newFilesInDir  = $newFilesInDir | Sort-Object -Property LastWriteTime -Descending | Select-Object -First $numberOfTopFiles

$filesInTDir | Select-Object -Property DirectoryName, Name, LastWriteTime, Length | ForEach-Object {
	$null = $tmpFileList.Add($_)
}

$newFilesInDir | Select-Object -Property DirectoryName, Name, LastWriteTime, Length | ForEach-Object {
	$null = $tmpNewFileList.Add($_)
}

$bigFileList = New-Object -TypeName System.Collections.ArrayList
Convert-LengthToReadable -lengthList $tmpFileList -readableList ([ref]$bigFileList)

$newFileList = New-Object -TypeName System.Collections.ArrayList
Convert-LengthToReadable -lengthList $tmpNewFileList -readableList ([ref]$newFileList)

$topDirsAndFiles.Add($tDirName,$bigFileList)
$topDirsAndNewestFiles.Add($tDirName,$newFileList)

} #End foreach ($tDirectory in $topDirectories)

$endTime = Get-Date
$requiredTime = New-TimeSpan -Start $startTime -End $endTime
$reqHours = [Math]::Round($requiredTime.TotalHours,2)
$reqMinutes = [Math]::Round($requiredTime.TotalMinutes,2)
$reqSeconds = [Math]::Round($requiredTime.TotalSeconds,2)

$metaInfo = “<table><tr><td>Gathering details took:</td><td>$($reqHours) Hours / $($reqMinutes) Minutes / $($reqSeconds) Seconds.</td></tr>”
$metaInfo += “<tr><td>Checking time:</td><td>$($scanDate), $($timeZone)</td></tr>”
$metaInfo += “<tr><td>LDAP Path:</td><td>$($adComputerLdapPath)</td></tr>”
$metaInfo += “<tr><td>Operating System:</td><td>$($WindowsVersion)</td></tr></table>”

$sendTopDirectoryMailReport = @{
tDirectories = $topDirectories
tDirsAndFiles = $topDirsAndFiles
nDirsAndFiles = $topDirsAndNewestFiles
diskMetaData = $diskMessage
runInfo = $metaInfo
}

Send-TopDirectoryMailReport@sendTopDirectoryMailReport

It requires the device ID as the script parameter:

I’m going to publish a step by step instruction soon and will publish the link here :slight_smile:

@Pete ,don’t worry. The script runs fast. Averagely it finishes within 30 seconds for 60 GB.

1 Like

Hi,

 

please check the link below for a step - by - step instruction:

https://4sysops.com/archives/send-disk-usage-reports-on-disk-full-alerts-in-scom/

 

If you have further questions or comments, let me know :slight_smile:

1 Like

This is very neat and very helpful. Thanks Reuben.

I ended up creating three tasks. One task for looking 1 level down in a directory, one task for 2 levels down in a directory, and then a task that runs Ruben’s report. The first two tasks I just slightly modified the one that is in the SquaredUp GitHub link I posted initially. The tasks were created for 2008/2012/2016 logical disks.

TaskList.jpg

Here is the output of the first two tasks on the C: of a server:

Disk Usage – 2 Levels

Param ($Arguments = [system.string]::Empty)

#Initialize Global Variables

$folderPath = $Arguments + ‘**’

#Check Folder path is populated and exists
If (($folderPath -ne [System.String]::Empty) -and (Test-Path $folderPath))
{
$subFolders = Get-ChildItem -Path $folderPath | Where-Object {$.PSIsContainer -eq $true}
If ($subFolders -ne $null)
{
$results = @()
ForEach ($folder in $subFolders)
{
$fileCount = Get-ChildItem $folder.FullName -Recurse | Where-Object {$
.PSIsContainer -eq $false} | Measure-Object -Sum Length

        $results += [PSCustomObject]@{
            'Name' = $folder.FullName
            'Files' = $fileCount.Count
            'Size-MB' = [math]::Round(($fileCount.Sum) /1024/1024,2)
           

        }
        
    }
    $results | Sort-Object Size-MB,Files,Name -Descending | select -first 25 | Format-Table Size-MB,Files,Name -AutoSize | Out-String -Width 4096 | Write-Host
}
Else
{
        Write-host "No Subfolders found on that path."
}

}

Disk Usage – 1 Level

Param ($Arguments = [system.string]::Empty)

#Initialize Global Variables

$folderPath = $Arguments + '\*'

#Check Folder path is populated and exists
If (($folderPath -ne [System.String]::Empty) -and (Test-Path $folderPath))
{    
    $subFolders = Get-ChildItem -Path $folderPath | Where-Object {$_.PSIsContainer -eq $true} 
    If ($subFolders -ne $null)
    {
        $results = @()
        ForEach ($folder in $subFolders)
        {
            $fileCount = Get-ChildItem $folder.FullName -Recurse | Where-Object {$_.PSIsContainer -eq $false}  | Measure-Object -Sum Length

            $results += [PSCustomObject]@{
                'Name' = $folder.FullName
                'Files' = $fileCount.Count
                'Size-MB' = [math]::Round(($fileCount.Sum) /1024/1024,2)
               

            }
            
        }
        $results | Sort-Object Size-MB,Files,Name -Descending | select -first 25 | Format-Table Size-MB,Files,Name -AutoSize | Out-String -Width 4096 | Write-Host
    }
    Else
    {
            Write-host "No Subfolders found on that path."
    }
}

Any method is fine. A diagnostic would be ideal but having the capability within SCOM / SquaredUp is the main goal.

Thanks Ruben, I’ll check out your script. Looking forward to the step by step instructions as well!

Yeah, i’d love the step by step too. I’m sure we could make use of this too. Great work.

Ruben, this is awesome - thanks very much for sharing.