Powershell : Monitor SMB shares


If you were using Windows to serve home drives and shared drives (which, if there’s any logic in that will probably be H: and S: - For which one goes where)

The axiom of the shared drives is in windows for now they have to use SMB, let’s be clear that should be SMBv2 and not SMBv1 - however port wise that is only TCP:445.

Note : If you’re still running SMBv1 - At the bottom of this article, I’ve put all my issues and warnings for SMBv1

Scripting a report for SMB shares

We first need to get the requirements for what we’d like to do, Obviously, a script to report on the space isn’t particularly helpful, so let’s have some more intelligent logic to it

Therefore, the requirements for this script will be as follows:
  1. Query a list of SMB shares
  2. Return the total size in GB
  3. Return the space used in GB
  4. Return the percentage of the drive that’s been used
  5. Write those values to a log file
  6. Future queries/runs will check for log to see if it’s increasing or decreasing in size (with a percentage)
  7. If the percentage of drive used drifts above 95% sending an email
SVMShare-Checker.ps1 - Onscreen Version

When this script is executed it will look like this, reporting on each of the shares with helpful data, it will also tell you if the space is trending up of down, it will also write each run to a CSV file, this will be used later for analysis.



# Define SMB share paths
$smbSharePaths = @(
    '<smb-share-1>',
    '<smb-share-2>',
    '<smb-share-3>'
)

# Determine all single-letter drive names.
$takenDriveLetters = (Get-PSDrive).Name -like '?'

# Create an array to hold the results
$results = @()

foreach ($sharePath in $smbSharePaths) {
    # Find the first unused drive letter.
    $firstUnusedDriveLetter = [char[]] (0x41..0x5a) | 
      where { $_ -notin $takenDriveLetters } | select -first 1
    
    if (-not $firstUnusedDriveLetter) {
        Write-Host "No available drive letters to map $sharePath."
        continue
    }

    # Temporarily map the target UNC path to a drive letter...
    $null = net use "${firstUnusedDriveLetter}:" $sharePath /persistent:no

    # Obtain the resulting drive's space information
    $drive = Get-PSDrive $firstUnusedDriveLetter
    $freeSpaceBytes = $drive.Free
    $totalSpaceBytes = $drive.Used + $freeSpaceBytes

    # Convert bytes to gigabytes (1 GB = 1,073,741,824 bytes)
    $freeSpaceGB = [math]::Round($freeSpaceBytes / 1GB, 3)
    $totalSpaceGB = [math]::Round($totalSpaceBytes / 1GB, 3)
    $usedSpaceGB = [math]::Round($totalSpaceGB - $freeSpaceGB, 3)

    # Calculate percentage used
    if ($totalSpaceGB -gt 0) {
        $percentageUsed = [math]::Round(($usedSpaceGB / $totalSpaceGB) * 100, 3)
    } else {
        $percentageUsed = 0
    }

    # Add the results to the array
    $results += [pscustomobject]@{
        'Date' = Get-Date
        'SMB Share Path' = $sharePath
        'Total Space (GB)' = $totalSpaceGB
        'Free Space (GB)' = $freeSpaceGB
        'Used Space (GB)' = $usedSpaceGB
        'Percentage Used (%)' = $percentageUsed
    }

    # Clean up: delete the mapping
    $null = net use "${firstUnusedDriveLetter}:" /delete
}

# Define the CSV log file path
$csvFilePath = Join-Path -Path $PSScriptRoot -ChildPath 'SMBShareSpaceUsage.csv'

# Read historical data from the CSV file if it exists
if (Test-Path -Path $csvFilePath) {
    $historicalData = Import-Csv -Path $csvFilePath
} else {
    $historicalData = @()
}

# Append the new results to the historical data
$combinedData = $historicalData + $results

# Export the combined data to the CSV file
$combinedData | Export-Csv -Path $csvFilePath -NoTypeInformation

# Output the results as a table
$results | Format-Table -AutoSize

# Trend Analysis
function Analyze-Trends {
    param (
        [string]$path
    )
    
    $data = $combinedData | Where-Object { $_.'SMB Share Path' -eq $path } | Sort-Object -Property 'Date'

    if ($data.Count -ge 2) {
        $previous = $data[-2]
        $current = $data[-1]

        $previousUsedSpace = $previous.'Used Space (GB)'
        $currentUsedSpace = $current.'Used Space (GB)'
        
        if ($previousUsedSpace -ne $currentUsedSpace) {
            $percentageChange = if ($previousUsedSpace -gt 0) {
                [math]::Round((($currentUsedSpace - $previousUsedSpace) / $previousUsedSpace) * 100, 3)
            } else {
                0
            }

            $trend = if ($currentUsedSpace -gt $previousUsedSpace) {
                "Increased by ${percentageChange}%"
            } elseif ($currentUsedSpace -lt $previousUsedSpace) {
                "Decreased by ${percentageChange}%"
            } else {
                "No Change"
            }

            Write-Host "Trend for ${path}: Usage has ${trend} since last check."
        } else {
            Write-Host "Trend for ${path}: No Change."
        }
    } else {
        Write-Host "Not enough data to analyze trends for ${path}."
    }
}

# Analyze trends for each share path
foreach ($sharePath in $smbSharePaths) {
    Analyze-Trends -path $sharePath
}

MailWarning-SMBChecker.ps1 - Mail Alert 🚨 

When executed you get no on-screen output but is does write the data the CSV file from the last script and if any drive is over 95% used you will also get a mail that looks like this, however in this example it is 90% as the code has been updated.


# Define SMB share paths
$smbSharePaths = @(
   '<smb-share-1>',
    '<smb-share-2>',
    '<smb-share-3>'
)

# Email configuration
$smtpServer = "<email-server-fqdn>"
$from = "smb.alert@croucher.cloud"
$to = "lee@croucher.cloud"

# Determine all single-letter drive names.
$takenDriveLetters = (Get-PSDrive).Name -like '?'

# Create an array to hold the results
$results = @()

foreach ($sharePath in $smbSharePaths) {
    # Find the first unused drive letter.
    $firstUnusedDriveLetter = [char[]] (0x41..0x5a) | 
      where { $_ -notin $takenDriveLetters } | select -first 1
    
    if (-not $firstUnusedDriveLetter) {
        Write-Host "No available drive letters to map $sharePath."
        continue
    }

    # Temporarily map the target UNC path to a drive letter...
    $null = net use "${firstUnusedDriveLetter}:" $sharePath /persistent:no

    # Obtain the resulting drive's space information
    $drive = Get-PSDrive $firstUnusedDriveLetter
    $freeSpaceBytes = $drive.Free
    $totalSpaceBytes = $drive.Used + $freeSpaceBytes

    # Convert bytes to gigabytes (1 GB = 1,073,741,824 bytes)
    $freeSpaceGB = [math]::Round($freeSpaceBytes / 1GB, 3)
    $totalSpaceGB = [math]::Round($totalSpaceBytes / 1GB, 3)
    $usedSpaceGB = [math]::Round($totalSpaceGB - $freeSpaceGB, 3)

    # Calculate percentage used
    if ($totalSpaceGB -gt 0) {
        $percentageUsed = [math]::Round(($usedSpaceGB / $totalSpaceGB) * 100, 3)
    } else {
        $percentageUsed = 0
    }

    # Add the results to the array
    $results += [pscustomobject]@{
        'Date' = Get-Date
        'SMB Share Path' = $sharePath
        'Total Space (GB)' = $totalSpaceGB
        'Free Space (GB)' = $freeSpaceGB
        'Used Space (GB)' = $usedSpaceGB
        'Percentage Used (%)' = $percentageUsed
    }

    # Clean up: delete the mapping
    $null = net use "${firstUnusedDriveLetter}:" /delete
}

# Define the CSV log file path
$csvFilePath = Join-Path -Path $PSScriptRoot -ChildPath 'SMBShareSpaceUsage.csv'

# Read historical data from the CSV file if it exists
if (Test-Path -Path $csvFilePath) {
    $historicalData = Import-Csv -Path $csvFilePath
} else {
    $historicalData = @()
}

# Append the new results to the historical data
$combinedData = $historicalData + $results

# Export the combined data to the CSV file
$combinedData | Export-Csv -Path $csvFilePath -NoTypeInformation

# Output the results as a table
$results | Format-Table -AutoSize

# Trend Analysis and Email Alerts
function Analyze-Trends {
    param (
        [string]$path,
        [float]$currentFreeSpaceGB,
        [float]$currentUsedSpaceGB,
        [float]$currentTotalSpaceGB,
        [float]$currentPercentageUsed
    )
    
    $data = $combinedData | Where-Object { $_.'SMB Share Path' -eq $path } | Sort-Object -Property 'Date'

    if ($data.Count -ge 2) {
        $previous = $data[-2]
        $previousPercentageUsed = $previous.'Percentage Used (%)'

        # Determine if we should send an alert
        if ($currentPercentageUsed -gt 95) {
            # Create HTML body
            $htmlBody = @"
<html>
<head>
    <style>
        table {border-collapse: collapse; width: 100%;}
        th, td {border: 1px solid #dddddd; text-align: left; padding: 8px;}
        th {background-color: #f2f2f2;}
    </style>
</head>
<body>
    <h2>SMB Share Alert</h2>
    <p>Dear User,</p>
    <p>This is an alert for the SMB share: $path.</p>
    <p>The current space usage details are as follows:</p>
    <table>
        <tr>
            <th>SMB Share Path</th>
            <th>Total Space (GB)</th>
            <th>Free Space (GB)</th>
            <th>Used Space (GB)</th>
            <th>Percentage Used (%)</th>
        </tr>
        <tr>
            <td>$path</td>
            <td>$currentTotalSpaceGB</td>
            <td>$currentFreeSpaceGB</td>
            <td>$currentUsedSpaceGB</td>
            <td>$currentPercentageUsed</td>
        </tr>
    </table>
    <p>Usage has exceeded 90% of the total space.</p>
    <p>Best regards,<br/>SMB Monitoring Team</p>
</body>
</html>
"@

            # Send email
            Send-MailMessage -From $from -To $to -Subject "SMB Share Alert for $path" -Body $htmlBody -BodyAsHtml -SmtpServer $smtpServer
        }
    }
}

# Analyze trends and send alerts for each share path
foreach ($share in $results) {
    Analyze-Trends -path $share.'SMB Share Path' -currentFreeSpaceGB $share.'Free Space (GB)' -currentUsedSpaceGB $share.'Used Space (GB)' -currentTotalSpaceGB $share.'Total Space (GB)' -currentPercentageUsed $share.'Percentage Used (%)'
}

SMBAnalyser.ps1

When executed this script will take all the data from the CSV file and produce a report on trending which will then notify you with an icon if the space is increasing, consistent or decreasing, the output will only return where the report file has been written, like this:


Then when you open the file which will open in your browser it will look like this:

Note : in this example, we have three shares so the script should be amended in this example in steps of three so you can see an accurate trend, if you only filter on the last known set of data, you will get an accurate results, this section will be marked in bold in the script.


# Path to the CSV file
$csvPath = "SMBShareSpaceUsage.csv"

# Import the CSV data and get the last three entries
$data = Import-Csv -Path $csvPath
$latestEntries = $data[9..-1]

# Define function to get trend icon
function Get-TrendIcon($currentValue, $previousValue) {
    if ($currentValue -gt $previousValue) {
        return "🔺" # Trending up
    } elseif ($currentValue -lt $previousValue) {
        return "🔻" # Trending down
    } else {
        return "➖" # No change
    }
}

# Generate HTML content
$htmlContent = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Latest SMB Share Usage</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f4f4f4;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 20px;
        }
        table, th, td {
            border: 1px solid #ddd;
        }
        th, td {
            padding: 10px;
            text-align: left;
        }
        th {
            background-color: #F3A00C;
            color: white;
        }
        tr:nth-child(even) {
            background-color: #f2f2f2;
        }
    </style>
</head>
<body>
    <h1>Latest SMB Share Usage</h1>
    <table>
        <tr>
            <th>SMB Share Path</th>
            <th>Total Space (GB)</th>
            <th>Free Space (GB)</th>
            <th>Used Space (GB)</th>
            <th>Percentage Used (%)</th>
            <th>Trend</th>
        </tr>
"@

# Determine trends
for ($i = 0; $i -lt $latestEntries.Count; $i++) {
    $currentEntry = $latestEntries[$i]
    $previousEntry = if ($i -gt 0) { $latestEntries[$i-1] } else { $null }

    if ($previousEntry) {
        $trendIcon = Get-TrendIcon([double]$currentEntry.'Used Space (GB)', [double]$previousEntry.'Used Space (GB)')
    } else {
        $trendIcon = "➖" # No trend for the first entry
    }

    $htmlContent += @"
        <tr>
            <td>$($currentEntry.'SMB Share Path')</td>
            <td>$([math]::Round([double]$currentEntry.'Total Space (GB)', 2))</td>
            <td>$([math]::Round([double]$currentEntry.'Free Space (GB)', 2))</td>
            <td>$([math]::Round([double]$currentEntry.'Used Space (GB)', 2))</td>
            <td>$([math]::Round([double]$currentEntry.'Percentage Used (%)', 2))</td>
            <td>$trendIcon</td>
        </tr>
"@
}

$htmlContent += @"
    </table>
</body>
</html>
"@

# Path to save the HTML report
$htmlReportPath = "SMB-Report.html"

# Save the HTML content to a file
$htmlContent | Out-File -FilePath $htmlReportPath -Encoding utf8

Write-Output "Latest entries analysis and HTML report generation completed. Report saved to: $htmlReportPath"


Warnings about SMBv1

SMBv1 Is a protocol for file sharing that should be disabled, this is the reason why ransom is able to spread around your network if it’s enabled because it has minimal safety and security controls.

If you have this enabled in the majority on your domain, you have failed to learn from other peoples misfortune with stuff like cryptolocker and ransomwhete and anything else that maliciously encrypt your data and hold you to ransom to pay some bitcoin amounts. 

Even if you pay these amounts, you’re not guaranteed to get your data back, but you are funding further attacks, If you have this legacy protocol enabled, make sure you have good accurate off-line back ups for when the inevitable happens.

SMBv1 Misconceptions

SMBv1 is a protocol that I’ve heard people talk about requiring TCP:139 and TCP:445, this is not technically accurate since Windows 2000.

Back in the good old days with Windows 2000 it needed to leverage NETBIOS as the protocol it used to communicate on this port, however, since Windows 2000 it no longer needs TCP:139 and now runs exclusively on TCP:445

SMBv1 Corridor

This is a weird name for a title, so let me clear that up, if you have a single server with SMBv1 enabled then, you are not secure and mitigated from the threat of SMBv1, even if this is one single server That’s been allowed for legacy purposes to accommodate older systems.

Probability will tell you that if you’ve got one server running in the older and more vulnerable SMBv1 protocol that there’s a slim chance it’s going to be found.

Probability for this example is not on your side, The reason that legacy server is still online is because there will be a small subset of your business function that’s old and legacy and running something crazy like Windows XP And Windows XP can only use SMB1.

That means if that legacy computer gets infected with ransomware - which in the laws of probability is a higher percentage because it’s older and doesn’t get security updates and probably will be running a legacy end point protection because of its age, means that device is exclusively talking to this server.

If the Server that runs your SMBv1 has a standalone file share and you are happy to lose All those files that can be encrypted, then that is one thing, however, the chances in our complicated environment is that that one server for legacy reasons is also allowed to talk to any other server using SMBv1 - so this affects every server that is allowed to use the SMBv1 protocol.
Previous Post Next Post

نموذج الاتصال