Lesson Learned #406:Monitoring Resource Usage and Open Ports with PowerShell & Bash Script
Published Jul 27 2023 08:44 AM 2,093 Views

In today's fast-paced digital landscape, it is crucial for organizations to efficiently monitor and manage their infrastructure's resource utilization. High resource consumption or misconfigured open ports can lead to performance issues, downtimes, and security vulnerabilities. To address these concerns, we present a PowerShell script that provides valuable insights into the resource usage and open ports of the environment where an application is running. This script allows administrators to identify potential bottlenecks and optimize their systems for better performance and security.

 

Understanding the PowerShell Script:

 

The PowerShell script is designed to gather real-time data on CPU, memory, I/O usage, network interface status, open ports, and running processes. The data is collected periodically and stored in separate CSV files for further analysis.

 

Duration and Data Collection Interval:

 

The script is equipped with a customizable duration, allowing users to define how long the monitoring process should run. For instance, setting the duration to 20 minutes will continuously collect data for the specified timeframe.

 

Data Collected and Their Significance:

  1. General Usage Data:

    • CPU Usage: Monitors the CPU load percentage to identify potential performance bottlenecks.
    • Memory Usage: Tracks the percentage of used memory to assess memory requirements and prevent memory-related issues.
  2. Network Usage Data:

    • Interface Status: Records the status (Up/Down) of all network interfaces, offering insights into connectivity health.
    • Description and Name: Provides details about each network interface, facilitating identification and troubleshooting.
  3. Ports Usage Data:

    • Protocol: Logs the protocol used by each open port to ascertain potential security risks.
    • Local and Foreign Addresses: Captures IP addresses and port numbers to identify incoming and outgoing connections.
    • State: Reveals the state of each port (Listening, Established, etc.), aiding in identifying active connections.
    • Process ID and Application Name: Associates open ports with corresponding processes, helping in resource allocation and security analysis.
  4. Processes Usage Data:

    • Process Name and ID: Lists all running processes along with their unique IDs for process identification.
    • CPU, Memory, and Virtual Memory Usage: Provides resource consumption statistics for each running process.
    • Handles: Indicates the number of handles used by each process, which can help identify resource leaks.

 

DNS Resolution Verification:

 

The script also performs DNS resolution verification for a specified list of server names. This ensures that DNS records are correctly resolving, preventing potential connectivity issues.

 

Conclusion:

With this comprehensive PowerShell script, administrators can gain deeper insights into their infrastructure's performance and resource utilization. By monitoring CPU, memory, I/O, network interfaces, open ports, and running processes, organizations can identify and address potential issues proactively. Armed with this knowledge, IT teams can optimize their systems for maximum efficiency and security, ensuring smooth and uninterrupted operations.

 

I hope this article provides valuable information about the PowerShell script as an example and its capabilities for monitoring resource usage and open ports. 

 

 

 

# Define the duration for script execution (in minutes)
$durationMinutes = 20
$intervalSeconds = 120
$endTime = (Get-Date).AddMinutes($durationMinutes)

# User-specified output path
$outputPath = "C:\temp\OutputFolder"  # Replace with the desired output folder path

# List of server names for DNS resolution verification
$serverNames = @(
    "myserverexample.database.windows.net",
    "example.com",
    "nonexistentserver12345.com"
)

# Function to log messages to console and a log file
function Log-Message {
    param (
        [string]$Message,
        [string]$timestamp
    )

    Write-Output "$timestamp - $Message"
    $logFilePath = Join-Path -Path $outputPath -ChildPath "ScriptLog.log"
    "$timestamp - $Message" | Out-File -FilePath $logFilePath -Append
}

# Function to gather general CPU, memory, and I/O usage
function Get-GeneralUsageData {
    param (
        [string]$timestamp
    )
    $cpuUsage = Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty LoadPercentage
    $memoryUsage = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -Property @{Name = "MemoryUsage(%)"; Expression = { [math]::Round(($_.TotalVisibleMemorySize - $_.FreePhysicalMemory) / $_.TotalVisibleMemorySize * 100, 2) } }

    $usageData = [PSCustomObject]@{
        Timestamp = $timestamp
        CPUUsage = $cpuUsage
        MemoryUsage = $memoryUsage."MemoryUsage(%)"
    }

    return $usageData
}

# Function to gather network interface status, description, and name for UP adapters
function Get-NetworkUsageData {
    param (
        [string]$timestamp
    )
    $networkInfo = Get-NetAdapter | Where-Object { $_.Status -eq "Up" }

    $networkData = foreach ($adapter in $networkInfo) {
        $status = $adapter.Status
        $description = $adapter.InterfaceDescription

        [PSCustomObject]@{
            Timestamp = $timestamp
            Status = $status
            Description = $description
        }
    }

    return $networkData
}

# Function to gather ports usage data
function Get-PortsUsageData {
    param (
        [string]$timestamp
    )
    $netstatOutput = netstat -ano
    $portsData = $netstatOutput | ForEach-Object {
        $line = $_.Trim() -replace '\s+', ' '
        $parts = $line -split ' '
        $protocol = $parts[0]
        $localAddress = $parts[1]
        $foreignAddress = $parts[2]
        $state = $parts[3]
        $processId = $parts[-1]

        if (-not [string]::IsNullOrWhiteSpace($processId) -and $processId -match '^\d+$') {
            $process = Get-Process -Id $processId -ErrorAction SilentlyContinue
            if ($process) {
                $appName = $process.Name
            }
            else {
                $appName = "N/A"
            }
        }
        else {
            $processId = "N/A"
            $appName = "N/A"
        }

        [PSCustomObject]@{
            Timestamp = $timestamp
            Protocol = $protocol
            LocalAddress = $localAddress
            ForeignAddress = $foreignAddress
            State = $state
            ProcessId = $processId
            ApplicationName = $appName
        }
    }

    return $portsData
}

# Function to gather processes usage data
function Get-ProcessesUsageData {
    param (
        [string]$timestamp
    )
    $processes = Get-Process
    $processesData = foreach ($process in $processes) {
        $processName = $process.Name
        $processId = $process.Id
        $cpuUsage = $process.CPU
        $memoryUsage = $process.WorkingSet
        $virtualMemory = $process.VirtualMemorySize
        $handles = $process.Handles

        [PSCustomObject]@{
            Timestamp = $timestamp
            ProcessName = $processName
            ProcessId = $processId
            CPUUsage = $cpuUsage
            MemoryUsage = $memoryUsage
            VirtualMemory = $virtualMemory
            Handles = $handles
        }
    }

    return $processesData
}

# Function to test DNS resolution for a list of server names
function Test-DnsResolution {
    param (
        [string]$timestamp
    )
   
    $dnsResolutionData = foreach ($server in $serverNames) {
        $startTime = Get-Date
        try {
            $ipAddress = [System.Net.Dns]::GetHostAddresses($server) | Select-Object -ExpandProperty IPAddressToString
            $endTime = Get-Date
            $timeTaken = ($endTime - $startTime).TotalMilliseconds
            $result = "Resolved"
        }
        catch {
            $ipAddress = "N/A"
            $timeTaken = "N/A"
            $result = "Failed to resolve"
        }

        [PSCustomObject]@{
            Timestamp = $timestamp
            ServerName = $server
            IPAddress = $ipAddress
            TimeTakenMilliseconds = $timeTaken
            Result = $result
        }
    }

    return $dnsResolutionData
}

# Function to save data to a CSV file
function Save-DataToCsv {
    param(
        [Parameter(Mandatory = $true)]
        [string]$FilePath,

        [Parameter(Mandatory = $true)]
        [array]$Data
    )
    $logFilePath = Join-Path -Path $outputPath -ChildPath $FilePath 
    $Data | Export-Csv -Path $logFilePath -NoTypeInformation -Append
}


# Create the output folder if it doesn't exist
if (-Not (Test-Path -Path $outputPath -PathType Container)) {
    New-Item -ItemType Directory -Path $outputPath | Out-Null
}

# Infinite loop for continuous monitoring until the specified duration
while ((Get-Date) -lt $endTime) {

    $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
    Log-Message "Starting data collection iteration..." -timestamp $timestamp
    # Gather data

    Log-Message "Gathering general usage data..." -timestamp $timestamp
    $generalUsageData = Get-GeneralUsageData -timestamp $timestamp
    Log-Message "Gathering network usage data..." -timestamp $timestamp
    $networkUsageData = Get-NetworkUsageData -timestamp $timestamp
    Log-Message "Gathering ports usage data..." -timestamp $timestamp
    $portsUsageData = Get-PortsUsageData -timestamp $timestamp
    Log-Message "Gathering processes usage data..." -timestamp $timestamp
    $processesUsageData = Get-ProcessesUsageData -timestamp $timestamp

    Log-Message "Performing DNS resolution verification..." -timestamp $timestamp
    $dnsResolutionData = Test-DnsResolution -timestamp $timestamp

    # Save data to CSV
    Save-DataToCsv -FilePath "GeneralUsage.csv" -Data $generalUsageData
    Save-DataToCsv -FilePath "NetworkUsage.csv" -Data $networkUsageData
    Save-DataToCsv -FilePath "PortsUsage.csv" -Data $portsUsageData
    Save-DataToCsv -FilePath "ProcessesUsage.csv" -Data $processesUsageData
    Save-DataToCsv -FilePath "DnsResolution.csv" -Data $dnsResolutionData

    Log-Message "Data collection iteration completed." -timestamp $timestamp
    # Wait for the interval before the next iteration
    Start-Sleep -Seconds $intervalSeconds
}

 

 

 

I would like to share a small linux version about the previous code:

 

 

#!/bin/bash

# Define the duration for script execution (in minutes)
durationMinutes=20
intervalSeconds=120
endTime=$((SECONDS + durationMinutes*60))

# User-specified output path
outputPath="/tmp/OutputFolder"  # Replace with the desired output folder path

# List of server names for DNS resolution verification
serverNames=(
    "myserver.database.windows.net"
    "example.com"
    "nonexistentserver12345.com"
)

# Function to log messages to console and a log file
function log_message {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "$timestamp - $message"
    echo "$timestamp - $message" >> "$outputPath/ScriptLog.log"
}

# Function to gather general CPU, memory, and I/O usage
function get_general_usage_data {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local cpuUsage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}')
    local memoryUsage=$(free | grep Mem | awk '{print ($3/$2)*100}')
    echo "Timestamp,CPUUsage(%),MemoryUsage(%)"
    echo "$timestamp,$cpuUsage,$memoryUsage"
}

# Function to gather network interface status and description for UP adapters
function get_network_usage_data {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local networkInfo=$(ip link show up)
    echo "Timestamp,Status,Description"
    while read -r line; do
        local interface=$(echo "$line" | awk '{print $2}' | cut -d':' -f1)
        local status=$(echo "$line" | grep -o 'state [^ ]*' | awk '{print $2}')
        local description=$(echo "$line" | awk '{print $9}')
        echo "$timestamp,$status,$description"
    done <<< "$networkInfo"
}

# Function to gather ports usage data
function get_ports_usage_data {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local netstatOutput=$(netstat -anop)
    echo "Timestamp,Protocol,LocalAddress,ForeignAddress,State,ProcessID,ApplicationName"
    while read -r line; do
        local protocol=$(echo "$line" | awk '{print $1}')
        local localAddress=$(echo "$line" | awk '{print $4}')
        local foreignAddress=$(echo "$line" | awk '{print $5}')
        local state=$(echo "$line" | awk '{print $6}')
        local processId=$(echo "$line" | awk '{print $7}')
        local appName=""
        if [[ ! -z "$processId" && "$processId" =~ ^[0-9]+$ ]]; then
            appName=$(ps -p "$processId" -o comm=)
        else
            processId="N/A"
        fi
        echo "$timestamp,$protocol,$localAddress,$foreignAddress,$state,$processId,$appName"
    done <<< "$netstatOutput"
}

# Function to gather processes usage data
function get_processes_usage_data {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local processes=$(ps -eo pid,comm,%cpu,%mem,rss,vsz,uname)
    echo "Timestamp,ProcessID,ProcessName,CPUUsage(%),MemoryUsage(RSS KB),VirtualMemory(VSZ KB),UserName"
    while read -r line; do
        local processId=$(echo "$line" | awk '{print $1}')
        local processName=$(echo "$line" | awk '{print $2}')
        local cpuUsage=$(echo "$line" | awk '{print $3}')
        local memoryUsage=$(echo "$line" | awk '{print $5}')
        local virtualMemory=$(echo "$line" | awk '{print $6}')
        local userName=$(echo "$line" | awk '{print $7}')
        echo "$timestamp,$processId,$processName,$cpuUsage,$memoryUsage,$virtualMemory,$userName"
    done <<< "$processes"
}

# Function to test DNS resolution for a list of server names
function test_dns_resolution {
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "Timestamp,ServerName,IPAddress,TimeTakenMilliseconds,Result"
    for server in "${serverNames[@]}"; do
        local startTime=$(date +%s%3N)
        ipAddress=$(getent ahosts "$server" | awk '/STREAM/ {print $1; exit}')
        local endTime=$(date +%s%3N)
        local timeTaken=$((endTime - startTime))
        local result="Resolved"
        if [ -z "$ipAddress" ]; then
            ipAddress="N/A"
            timeTaken="N/A"
            result="Failed to resolve"
        fi
        echo "$timestamp,$server,$ipAddress,$timeTaken,$result"
    done
}

# Function to save data to a CSV file
function save_data_to_csv {
    local filePath="$1"
    local data="$2"
    echo "$data" >> "$outputPath/$filePath"
}

# Create the output folder if it doesn't exist
mkdir -p "$outputPath"

# Infinite loop for continuous monitoring until the specified duration
while [ $SECONDS -lt $endTime ]; do
    timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    log_message "Starting data collection iteration..." "$timestamp"

    # Gather data
    log_message "Gathering general usage data..." "$timestamp"
    generalUsageData=$(get_general_usage_data)

    log_message "Gathering network usage data..." "$timestamp"
    networkUsageData=$(get_network_usage_data)

    log_message "Gathering ports usage data..." "$timestamp"
    portsUsageData=$(get_ports_usage_data)

    log_message "Gathering processes usage data..." "$timestamp"
    processesUsageData=$(get_processes_usage_data)

    log_message "Performing DNS resolution verification..." "$timestamp"
    dnsResolutionData=$(test_dns_resolution)

    # Save data to CSV
    save_data_to_csv "GeneralUsage.csv" "$generalUsageData"
    save_data_to_csv "NetworkUsage.csv" "$networkUsageData"
    save_data_to_csv "PortsUsage.csv" "$portsUsageData"
    save_data_to_csv "ProcessesUsage.csv" "$processesUsageData"
    save_data_to_csv "DnsResolution.csv" "$dnsResolutionData"

    log_message "Data collection iteration completed." "$timestamp"

    # Wait for the interval before the next iteration
    sleep $intervalSeconds
done

 

 

Enjoy!

Version history
Last update:
‎Jul 27 2023 09:07 AM
Updated by: