Last active
February 17, 2019 23:55
-
-
Save everydayPowerShell/8b9d400ba4c577f742825b7df3fe5965 to your computer and use it in GitHub Desktop.
Function to collect low drive space on list of systems.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FUNCTION Get-LowDriveSpace { | |
<# | |
.SYNOPSIS | |
Collect hard drive stats from list of computers and report findings with rankings | |
of concern. | |
.DESCRIPTION | |
Using provided CSV list of computer names, reaches out and determines available sizes. | |
Highlights concerns through the below listed evaluation process. | |
Findings our prepared and offered either to a specified log file or to the screen, | |
allowing inclusion or exclusion of error'ed attempts. | |
Failure Levels (using failLvl parameter) Will Be Reported As Follows: | |
5 Information: Drive space is satisfactory. | |
4 Information: Drive Information Inaccessible. | |
3 Warning: Low % (less than 5%), not low size (less than 5 GB). | |
2 Severe: Low size (less than 5 GB), not low % (less than 5%). | |
1 Critical: Low % (less than 5%) & low size (less than 5 GB). | |
.PARAMETER CsvPath | |
This should be a file path to the csv that contains computer names to check. It will | |
need a column called cName in order to work properly. | |
The default, if no CsvPath is specified is: | |
"$($env:TEMP)\myComputerList.csv" | |
This should be updated for long-term use. | |
.PARAMETER LogPath | |
LogPath allows the user of the function to specify where logged information should go. | |
There is no need to clarify this if the technician will be using the OutGridview option | |
as all output will be going to the screen rather than to the log file. | |
The default, if no LogPath is provided, is: | |
"$($env:TEMP)\lowDrives.log" | |
This should be changed for long-term use. | |
.PARAMETER OutGridview | |
As an alternative to reporting the information back to the monitored log file, the | |
technician can just output the list to a gridview to review and act on as needed. | |
.PARAMETER ReportFailures | |
This will also be a switch allowing tech to indicate that failures in information | |
gathering should be logged as well. Depending on who's using this function, they | |
may only want info when there's actual drive info to evaluate. Others may want to | |
know... what they don't know. | |
.EXAMPLE | |
Get-LowDriveSpace | |
The most basic usage. | |
The computers will be drawn from: | |
"$($env:TEMP)\myComputerList.csv" | |
The information will be logged to: | |
"$($env:TEMP)\lowDrives.log" | |
.EXAMPLE | |
Get-LowDriveSpace -ReportFailures | |
This will evaluate the drives the same but will include, | |
in the log, where system information could not be retrieved. | |
.EXAMPLE | |
Get-LowDriveSpace -CsvPath '\\myServer\myShare\consolidatedSystemList.csv' | |
Here you will redirection the function's attention to find it's list of | |
target computers by using the specified server path. | |
.EXAMPLE | |
Get-LowDriveSpace -logPath '\\myServer02\mgmtShare\everyoneCanSeeMe.log' | |
With this command, the final output would be saved to a management share | |
rather than to the default temp folder location on the local computer. | |
.EXAMPLE | |
$lowDriveVars = @{ | |
OutGridView = $true | |
ReportFailures = $true | |
} | |
Get-LowDriveSpace @lowDriveVars | |
With this splat/command combo, Get-LowDriveSpace will evaluate the target | |
systems, include catching failures, and report all results to the screen | |
in GridView format. | |
.LINK | |
Test-Connection | |
New-PSSession | |
Invoke-Command | |
Get-CIMInstance | |
Remove-PSSession | |
.NOTES | |
Author: Mark Smith | |
Blog: blog.everydayPowerShell.com | |
Created On: 17Feb19 | |
Last Updated On: 17Feb19 | |
#> | |
[CmdletBinding()] | |
[OutputType([String],[Array])] | |
param( | |
[Parameter(Position=0)] | |
[String]$CsvPath = | |
"$($env:TEMP)\myComputerList.csv", | |
[Parameter(Position=1)] | |
[String]$LogPath = | |
"$($env:TEMP)\lowDrives.log", | |
[Parameter(Position=2)] | |
[Switch]$OutGridView, | |
[Parameter(Position=3)] | |
[Switch]$ReportFailures | |
) | |
#region Parameter Check & Variable Prep | |
Write-Verbose -Message "Verifying Parameter Information." | |
#region Verify file referenced is CSV | |
IF($CsvPath -notlike "*.csv"){ | |
$badParamMsg = -JOIN ( | |
"`n `nYou have not included a path to a CSV providing computers that need ", | |
"to be checked. Please make sure to use the -CsvPath option with this ", | |
"function.`n `nFor additional help, please use `"Get-Help Get-LowDriveSpace ", | |
"-Full`" or `"Get-Help Get-LowDriveSpace -Examples`".`n `nThank you. This ", | |
"script will now halt.`n `n" | |
) | |
Write-Error -Message $badParamMsg -ErrorAction Stop | |
} | |
#endregion | |
#region Verify CSV is accessible | |
IF(!(Test-Path -Path $CsvPath)){ | |
$badParamMsg = -JOIN ( | |
"`n `nThe path provided for the CSV list of computers does not appear to be ", | |
"valid. You provided the following path for the CSV.`n `n$CsvPath`n `nThank ", | |
"you. This script will now halt." | |
) | |
Write-Error -Message $badParamMsg -ErrorAction Stop | |
} | |
#endregion | |
#region Import CSV & verify cNames exist | |
$cList = Import-Csv -Path $CsvPath | |
IF(!($cList.cName.Count -gt 0)){ | |
$badParamMsg = -JOIN ( | |
"`n `nYou provided what appears to be a valid CSV file. However, no ", | |
"computer names could be found when looking for values in a cName column.", | |
"`n `nFor additional help, please use `"Get-Help Get-LowDriveSpace ", | |
"-Full`". Thank you. This script will now halt.`n `n" | |
) | |
Write-Error -Message $badParamMsg -ErrorAction Stop | |
} | |
#endregion | |
#region Hone CSV list down to just cNames | |
$cList = $cList | SELECT-Object -ExpandProperty cName | |
#endregion | |
#region Prepare for OutGridview switch if applicable | |
IF($OutGridView){ | |
$allDriveInfo = @() | |
} | |
#endregion | |
#region Establish stateCount variable | |
$stateCount = "" | Select-Object ComputerCount,Critical,Severe,Warning, | |
Satisfactory,Unavailable | |
$stateCount | Get-Member -MemberType Properties | ForEach-Object { | |
$stateCount.($_.Name) = [int]0 | |
} | |
#endregion | |
#endregion | |
ForEach($cName in $cList){ | |
#region Final Findings Prep | |
($stateCount.ComputerCount)++ | |
Write-Verbose -Message "Preparing result object for computer $cName" | |
TRY{ | |
#region Preparing individual computer result object | |
$baseLowDriveResult = "" | SELECT-Object cName, drvLetter, drvSize, | |
drvFree, failLvl, failedDrvSpaceRule, lowNotes, evalTime | |
$baseLowDriveResult.cName = $cName | |
[string]$baseLowDriveResult.evalTime = (Get-Date -Format g).ToString() | |
$baseLowDriveResult.lowNotes = "Initiating Check" | |
#endregion | |
} | |
CATCH{ | |
#region Prepare for error closure for this one system | |
$msg = "Something went wrong when establishing the result object" | |
$msg = "$msg for $cName." | |
Write-Verbose -Message $msg | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$baseLowDriveResult.lowNotes = -JOIN ( | |
"`nBreak At:`n`#Region End Game Prep`n", | |
"$(($_.ScriptStackTrace -Split("at "))[1])`n", | |
"Error Message:`n$($_.Exception.Message)`n`n", | |
"Error Name:`n$($_.Exception.GetType().FullName)`n" | |
) | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Report error closure for this one system | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
#endregion | |
#region Check cName For Availability | |
$msg = "Testing whether the current computer," | |
$msg = "$msg $cName, is accessible via Test-Connection." | |
Write-Verbose -Message $msg | |
TRY{ | |
#region Checking if system can be pinged | |
$cName = ($cName).Trim() | |
Write-Output "Checking on system `"$cName`"" | |
IF(!(Test-Connection -ComputerName $cName -Count 3 -Quiet)){ | |
#region Prepare for error closure for this one system | |
$msg = "System $cName was not available via Test-Connection." | |
Write-Verbose -Message $msg | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$baseLowDriveResult.lowNotes = | |
"System $cName unavailable using Test-Connection." | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Report error closure for this one system | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
ELSE{ | |
$baseLowDriveResult.lowNotes = "System $cName is online." | |
} | |
#endregion | |
} | |
CATCH{ | |
#region Prepare for error closure for this one system | |
$msg = "Something went wrong when attempting to run Test-Connection" | |
$msg = "$msg against $cName." | |
Write-Verbose -Message $msg | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$baseLowDriveResult.lowNotes = -JOIN ( | |
"`nBreak At:`n`#Region Check cName For Availability`n", | |
"$(($_.ScriptStackTrace -Split("at "))[1])`n", | |
"Error Message:`n$($_.Exception.Message)`n`nError Name:`n", | |
"$($_.Exception.GetType().FullName)`n" | |
) | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Report error closure for this one system | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
#endregion | |
#region Establish Connection | |
Write-Verbose -Message "Attempting to establish connection with $cName." | |
TRY{ | |
<# | |
In our case, below, we'll attempt a PSSession rather than a CIMSession. | |
We'll use the computer's name as the name of the session as well to make | |
it easier to remember later in the script. | |
#> | |
$psSessionVars = @{ | |
ComputerName = $cName | |
Name = $cName | |
WarningAction = "Stop" | |
ErrorAction = "Stop" | |
} | |
New-PSSession @psSessionVars | Out-Null | |
$baseLowDriveResult.lowNotes = | |
"PSSession established with $cName." | |
Write-Verbose ($baseLowDriveResult.lowNotes) | |
} | |
CATCH [System.Management.Automation.Remoting.PSRemotingTransportException] { | |
<# | |
This error means, explicitly, that the attempt to connect to the remote | |
system failed. More often then not, this means there are either firewall | |
issues or PSRemoting has not been enabled on the supported system. | |
#> | |
#region Prepare for error closure for this one system | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$baseLowDriveResult.lowNotes = | |
"Could not establish PSSession with $cName." | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Report error closure for this one system | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
CATCH{ | |
#region Prepare for error closure for this one system | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$errMsg = -JOIN ( | |
"`nBreak At:`n`#Region Establish Connection`n", | |
"$(($_.ScriptStackTrace -Split("at "))[1])`n", | |
"Error Message:`n$($_.Exception.Message)`n`nError Name:`n", | |
"$($_.Exception.GetType().FullName)`n" | |
) | |
$baseLowDriveResult.lowNotes = $errMsg | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Report error closure for this one system | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
#endregion | |
#region Collect Drive Info | |
TRY{ | |
Write-Verbose -Message "Attempting to collect drive information for $cName." | |
<# | |
Invoke-Command will connect to the present remote computer using the | |
connection we established in the last region. The results of whatever | |
is done over there will then be saved to $driveInfo. | |
#> | |
#region Attempt collection of information | |
$driveInfo = $null | |
$invokeCommandVars = @{ | |
Session = (Get-PSSession -Name $cName) | |
WarningAction = "Stop" | |
ErrorAction = "Stop" | |
} | |
$driveInfo = Invoke-Command @invokeCommandVars -ScriptBlock { | |
<# | |
Get-CimInstance draws on the underlying CIM/WMI classes to collect | |
system information. WMI is Microsoft's implementation of the CIM | |
standard. For more information on WMI (which is CIM), try out | |
Get-Help about_WMI. | |
#> | |
Get-CimInstance -ClassName CIM_LogicalDisk | | |
Where-Object {$_.DriveType -eq 3} | |
} | Select-Object DeviceID, Size, FreeSpace | |
#endregion | |
#region Wrap up from successful collection | |
$removePSVars = @{ | |
WarningAction = "SilentlyContinue" | |
ErrorAction = "SilentlyContinue" | |
} | |
Get-PSSession -Name $cName | | |
Remove-PSSession @removePSVars | |
$baseLowDriveResult.lowNotes = | |
"Drive information for $cName collected, pending processing." | |
Write-Verbose ($baseLowDriveResult.lowNotes) | |
#endregion | |
} | |
CATCH [System.Management.Automation.Runspaces.InvalidRunspaceStateException] { | |
<# | |
If a network is unstable, it's possible to have a PSSession change from | |
being "Open" to "Broken". If that's happened in the half second from | |
when we established the connection in Region Establish Connection to now, | |
when we're trying to use it, this will CATCH that specific issue and | |
provide feedback accordingly. | |
#> | |
#region Prepare for error closure for this one system | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$baseLowDriveResult.lowNotes = -JOIN ( | |
"It seems the just established PSSession that we created in order ", | |
"to run commands on $cName is already broken. The script cannot ", | |
"collect information for this machine at this time.`n`n", | |
"Error Information:`n", | |
"$($_.Exception.Message)`n", | |
"$($_.Exception.GetType().FullName)`n" | |
) | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Close PSSession | |
<# | |
If things went wrong prematurely in the TRY section, we still need | |
to make sure to clean up after ourselves so, here, I again implement | |
the step to remove the saved connection to the remote computer. | |
#> | |
$removePSVars = @{ | |
WarningAction = "SilentlyContinue" | |
ErrorAction = "SilentlyContinue" | |
} | |
Get-PSSession -Name $cName | | |
Remove-PSSession @removePSVars | |
#endregion | |
#region Report error closure for this one system | |
Write-Verbose ($baseLowDriveResult.lowNotes) | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
CATCH{ | |
#Generic CATCH for the unforeseen issues. | |
#region Prepare for error closure for this one system | |
$baseLowDriveResult.drvLetter = "NA" | |
$baseLowDriveResult.drvSize = "0" | |
$baseLowDriveResult.drvFree = "0" | |
$baseLowDriveResult.failLvl = 4 | |
$baseLowDriveResult.failedDrvSpaceRule = | |
"Information: Drive Information Inaccessible." | |
$baseLowDriveResult.lowNotes = -JOIN ( | |
"`nBreak At:`n`#Region Establish Connection`n", | |
"$(($_.ScriptStackTrace -Split("at "))[1])`n", | |
"Error Message:`n$($_.Exception.Message)`n`nError Name:`n", | |
"$($_.Exception.GetType().FullName)`n" | |
) | |
($stateCount.Unavailable)++ | |
#endregion | |
#region Close PSSession | |
<# | |
If things went wrong prematurely in the TRY section, we still need | |
to make sure to clean up after ourselves so, here, I again implement | |
the step to remove the saved connection to the remote computer. | |
#> | |
$removePSVars = @{ | |
WarningAction = "SilentlyContinue" | |
ErrorAction = "SilentlyContinue" | |
} | |
Get-PSSession -Name $cName | | |
Remove-PSSession @removePSVars | |
#endregion | |
#region Report error closure for this one system | |
Write-Verbose ($baseLowDriveResult.lowNotes) | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $baseLowDriveResult | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$baseLowDriveResult | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
} | |
#endregion | |
#region Process Drive Info | |
<# | |
Considering there may be more than one drive on this one system being checked, | |
we'll use a ForEach loop to check for whatever $driveInfo results we have. | |
#> | |
ForEach($thisDrive in $driveInfo){ | |
#region Per drive prep | |
<# | |
Creating new object per drive based on baseLowDriveResult object | |
template. Nulling some prep variables per loop. | |
#> | |
$thisDriveInfo = @() | |
$thisDriveInfo = $baseLowDriveResult | SELECT-Object * | |
$spaceLeft=$percentLeft=$null | |
#endregion | |
#region Format & calculate size & percentage | |
$spaceLeft = [Math]::Round(($thisDrive.FreeSpace)/1GB,2) | |
$percentLeft = | |
[Math]::Round((($thisDrive.FreeSpace)/($thisDrive.Size))*100,2) | |
#endregion | |
#region Save drive info to current thisDriveInfo object | |
$thisDriveInfo.drvLetter = $thisDrive.DeviceID | |
$thisDriveInfo.drvSize = [Math]::Round(($thisDrive.Size)/1GB,2) | |
$thisDriveInfo.drvFree = $spaceLeft | |
$thisDriveInfo.lowNotes = "Success: Process completed successfully." | |
#endregion | |
#region Evaluation drive state/category & log accordingly | |
#region Critical | |
IF($spaceLeft -lt 5 -and $percentLeft -lt 5){ | |
($stateCount.Critical)++ | |
$thisDriveInfo.failLvl = 1 | |
$thisDriveInfo.failedDrvSpaceRule = | |
'Critical: Low % (less than 5%) & low space (less than 5 GB).' | |
IF($OutGridView){ | |
$allDriveInfo += $thisDriveInfo | |
continue | |
} | |
ELSE{ | |
$thisDriveInfo | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
} | |
#endregion | |
#region Severe | |
IF($spaceLeft -lt 5){ | |
($stateCount.Severe)++ | |
$thisDriveInfo.failLvl = 2 | |
$thisDriveInfo.failedDrvSpaceRule = | |
'Severe: Low space (less than 5 GB), not low % (less than 5%).' | |
IF($OutGridView){ | |
$allDriveInfo += $thisDriveInfo | |
continue | |
} | |
ELSE{ | |
$thisDriveInfo | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
} | |
#endregion | |
#region Warning | |
IF($percentLeft -lt 5){ | |
($stateCount.Warning)++ | |
$thisDriveInfo.failLvl = 3 | |
$thisDriveInfo.failedDrvSpaceRule = | |
'Warning: Low % (less than 5%), not low space (less than 5 GB).' | |
IF($OutGridView){ | |
$allDriveInfo += $thisDriveInfo | |
continue | |
} | |
ELSE{ | |
$thisDriveInfo | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
} | |
#endregion | |
#region Satisfactory | |
IF($spaceLeft -ge 5 -and $percentLeft -ge 5){ | |
($stateCount.Satisfactory)++ | |
$thisDriveInfo.failLvl = 5 | |
$thisDriveInfo.failedDrvSpaceRule = | |
'Information: Drive space is satisfactory.' | |
IF($OutGridView){ | |
$allDriveInfo += $thisDriveInfo | |
continue | |
} | |
ELSE{ | |
$thisDriveInfo | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
} | |
#endregion | |
#region Unavailable | |
($stateCount.Unavailable)++ | |
$thisDriveInfo.failLvl = 4 | |
$thisDriveInfo.failedDrvSpaceRule = | |
'Information: Drive Information Inaccessible.' | |
$thisDriveInfo.lowNotes = | |
'Failure: Information was not correctly gathered.' | |
IF($OutGridView -and $ReportFailures){ | |
$allDriveInfo += $thisDriveInfo | |
continue | |
} | |
ELSEIF($ReportFailures){ | |
$thisDriveInfo | | |
Export-Csv $logPath -NoTypeInformation -Append | |
continue | |
} | |
ELSE{ | |
continue | |
} | |
#endregion | |
#endregion | |
} | |
#endregion | |
} | |
#region Closure | |
IF($OutGridView){ | |
$allDriveInfo | | |
Out-GridView -Title "Drive Info Results from Function Get-LowDriveSpace" | |
} | |
ELSE{ | |
Write-Output $stateCount | |
$closingMsg = -JOIN ( | |
"Drive information has been sought and processed. ", | |
"Results can be found at:`n`n", | |
"$logPath`n`n", | |
"General stats are provided above.`n" | |
) | |
Write-Output $closingMsg | |
return | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment