Skip to content

Instantly share code, notes, and snippets.

@everydayPowerShell
Last active February 17, 2019 23:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save everydayPowerShell/8b9d400ba4c577f742825b7df3fe5965 to your computer and use it in GitHub Desktop.
Save everydayPowerShell/8b9d400ba4c577f742825b7df3fe5965 to your computer and use it in GitHub Desktop.
Function to collect low drive space on list of systems.
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