Category: PowerShell

  • Windows Security Features Check PowerShell Script

    This PowerShell script provides a quick and colorful overview of key Windows 11 security features, including Microsoft Defender, Firewall, Secure Boot, BitLocker, VBS, Credential Guard, and TPM status. It uses error handling to ensure reliability and presents results in an easy-to-read format, highlighting whether each feature is enabled or disabled.

    Click to view script…
    # Quick Windows 11 Security Features Check
    
    # Check if the script is running with administrator privileges
    if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Warning "This script should be run as Administrator for full functionality."
    }
    
    # Microsoft Defender Status
    Write-Host "`nMicrosoft Defender Status:" -ForegroundColor Cyan
    $defenderStatus = Get-MpComputerStatus
    Write-Host "Antivirus Enabled: " -NoNewline
    if ($defenderStatus.AntivirusEnabled) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    Write-Host "Real-Time Protection: " -NoNewline
    if ($defenderStatus.RealTimeProtectionEnabled) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    Write-Host "Tamper Protected: " -NoNewline
    if ($defenderStatus.IsTamperProtected) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    
    # Firewall Status
    Write-Host "`nFirewall Status:" -ForegroundColor Cyan
    $firewallStatus = Get-NetFirewallProfile
    foreach ($profile in $firewallStatus) {
        Write-Host "$($profile.Name) Profile: " -NoNewline
        if ($profile.Enabled) { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
    }
    
    # Secure Boot Status
    Write-Host "`nSecure Boot:" -ForegroundColor Cyan
    try {
        $secureBoot = Confirm-SecureBootUEFI
        Write-Host "Status: " -NoNewline
        if ($secureBoot) { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
    } catch {
        Write-Host "Status: Not supported or error occurred" -ForegroundColor Yellow
    }
    
    # BitLocker Status
    Write-Host "`nBitLocker Status:" -ForegroundColor Cyan
    $bitLockerStatus = Get-BitLockerVolume
    foreach ($volume in $bitLockerStatus) {
        Write-Host "$($volume.MountPoint) Protection: " -NoNewline
        if ($volume.ProtectionStatus -eq "On") { Write-Host "On" -ForegroundColor Green } else { Write-Host "Off" -ForegroundColor Red }
    }
    
    # VBS and Credential Guard
    Write-Host "`nVBS and Credential Guard:" -ForegroundColor Cyan
    try {
        $deviceGuard = Get-CimInstance -ClassName Win32_DeviceGuard -ErrorAction Stop
        $vbsStatus = if ($deviceGuard.VirtualizationBasedSecurityStatus -eq 2) { "Enabled" } else { "Disabled" }
        $credGuard = if ($deviceGuard.SecurityServicesRunning -contains 1) { "Enabled" } else { "Disabled" }
        Write-Host "VBS: " -NoNewline
        if ($vbsStatus -eq "Enabled") { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
        Write-Host "Credential Guard: " -NoNewline
        if ($credGuard -eq "Enabled") { Write-Host "Enabled" -ForegroundColor Green } else { Write-Host "Disabled" -ForegroundColor Red }
    } catch {
        Write-Host "VBS and Credential Guard status not available on this system." -ForegroundColor Yellow
    }
    
    # TPM Status
    Write-Host "`nTPM Status:" -ForegroundColor Cyan
    $tpmStatus = Get-Tpm
    Write-Host "TPM Present: " -NoNewline
    if ($tpmStatus.TpmPresent) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    Write-Host "TPM Ready: " -NoNewline
    if ($tpmStatus.TpmReady) { Write-Host "Yes" -ForegroundColor Green } else { Write-Host "No" -ForegroundColor Red }
    
    Write-Host "`nSecurity Features Check Complete" -ForegroundColor Cyan
  • Identify Pending Windows Updates PowerShell Script

    This PowerShell script utilizes the Windows Update Agent API to scan for available updates that are not yet installed, displaying a detailed list of pending updates including their titles, KB numbers, download status, and descriptions. It provides a quick and reliable way to check the update status of a Windows system without requiring manual intervention through the Settings app.

    Click to view script…
    # PowerShell script to check for Windows updates and list pending updates
    
    try {
        Write-Output "Checking for updates..."
        
        # Create a new update session
        $session = New-Object -ComObject Microsoft.Update.Session
        
        # Create an update searcher
        $searcher = $session.CreateUpdateSearcher()
        
        # Set the search criteria to find updates that are not installed and not hidden
        $criteria = "IsInstalled=0 and IsHidden=0"
        
        # Perform the search
        $result = $searcher.Search($criteria)
        
        # Check if the search was successful (ResultCode 2 means Succeeded)
        if ($result.ResultCode -eq 2) {
            $updates = $result.Updates
            if ($updates.Count -gt 0) {
                Write-Output "Found $($updates.Count) pending updates:"
                $counter = 1
                foreach ($update in $updates) {
                    # Get the KB article ID if available
                    $kb = if ($update.KBArticleIDs.Count -gt 0) { $update.KBArticleIDs[0] } else { "N/A" }
                    # Check if the update is downloaded
                    $status = if ($update.IsDownloaded) { "Downloaded" } else { "Not Downloaded" }
                    Write-Output "$counter. Title: $($update.Title) (KB$kb) - $status"
                    Write-Output "   Description: $($update.Description)"
                    Write-Output ""
                    $counter++
                }
            } else {
                Write-Output "No pending updates found."
            }
        } else {
            Write-Output "Update search failed with result code $($result.ResultCode)"
        }
    } catch {
        Write-Output "An error occurred: $($_.Exception.Message)"
    }
  • Retrieve Detailed System Specifications PowerShell Script

    This PowerShell script collects and displays comprehensive system specifications for a Windows machine, including details about the operating system, processor, memory, disk drives, graphics, network adapters, and motherboard/BIOS. It organizes the information into a clear, readable format, making it ideal for system diagnostics, inventory tracking, or personal reference.

    Click to view script…
    # Get OS Information
    $os = Get-WmiObject -Class Win32_OperatingSystem
    $installDate = [System.Management.ManagementDateTimeConverter]::ToDateTime($os.InstallDate)
    Write-Output "Operating System Information:"
    Write-Output "OS Name: $($os.Caption)"
    Write-Output "Version: $($os.Version)"
    Write-Output "Build Number: $($os.BuildNumber)"
    Write-Output "Installation Date: $installDate"
    Write-Output ""
    
    # Get Processor Information
    $processors = Get-WmiObject -Class Win32_Processor
    $totalCores = ($processors | Measure-Object -Property NumberOfCores -Sum).Sum
    $totalThreads = ($processors | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
    $processorName = $processors[0].Name
    $maxClockSpeed = $processors[0].MaxClockSpeed
    Write-Output "Processor Information:"
    Write-Output "Processor: $processorName"
    Write-Output "Total Cores: $totalCores"
    Write-Output "Total Threads: $totalThreads"
    Write-Output "Max Clock Speed: $maxClockSpeed MHz"
    Write-Output ""
    
    # Get Memory Information
    $memory = Get-WmiObject -Class Win32_PhysicalMemory
    $totalRAM = [math]::Round(($memory | Measure-Object -Property Capacity -Sum).Sum / 1GB, 2)
    Write-Output "Memory Information:"
    Write-Output "Total RAM: $totalRAM GB"
    Write-Output ""
    
    # Get Disk Information
    $disks = Get-WmiObject -Class Win32_DiskDrive
    Write-Output "Disk Information:"
    foreach ($disk in $disks) {
        $sizeGB = [math]::Round($disk.Size / 1GB, 2)
        Write-Output "Disk Model: $($disk.Model)"
        Write-Output "Size: $sizeGB GB"
        Write-Output ""
    }
    
    # Get Graphics Information
    $gpus = Get-WmiObject -Class Win32_VideoController
    Write-Output "Graphics Information:"
    foreach ($gpu in $gpus) {
        Write-Output "Graphics Card: $($gpu.Name)"
        Write-Output "Resolution: $($gpu.VideoModeDescription)"
        Write-Output ""
    }
    
    # Get Network Information
    $adapters = Get-NetAdapter | Where-Object {$_.Status -eq 'Up'}
    Write-Output "Network Information:"
    foreach ($adapter in $adapters) {
        Write-Output "Adapter Name: $($adapter.Name)"
        Write-Output "MAC Address: $($adapter.MacAddress)"
        $ipAddresses = Get-NetIPAddress -InterfaceIndex $adapter.InterfaceIndex | Where-Object {$_.AddressFamily -eq 'IPv4'}
        Write-Output "IP Addresses:"
        foreach ($ip in $ipAddresses) {
            Write-Output "  $($ip.IPAddress)"
        }
        Write-Output ""
    }
    
    # Get Motherboard and BIOS Information
    $motherboard = Get-WmiObject -Class Win32_BaseBoard
    $bios = Get-WmiObject -Class Win32_BIOS
    $biosDate = [System.Management.ManagementDateTimeConverter]::ToDateTime($bios.ReleaseDate)
    Write-Output "Motherboard and BIOS Information:"
    Write-Output "Motherboard Manufacturer: $($motherboard.Manufacturer)"
    Write-Output "Motherboard Model: $($motherboard.Product)"
    Write-Output "BIOS Manufacturer: $($bios.Manufacturer)"
    Write-Output "BIOS Version: $($bios.Version)"
    Write-Output "BIOS Release Date: $biosDate"
  • List Windows Version and Updates PowerShell Script

    This PowerShell script retrieves and displays the current Windows operating system name and version, including the major, minor, build, and revision numbers. It also lists all installed updates, sorted by installation date, with details such as the hotfix ID, description, and when each update was applied.

    Click to view script…
    # Get OS name
    $os = Get-CimInstance -ClassName Win32_OperatingSystem
    $osName = $os.Caption
    
    # Get OS version
    $osVersion = [System.Environment]::OSVersion.Version
    
    # Display OS name and version
    Write-Output "Operating System: $osName"
    Write-Output "Version: $($osVersion.Major).$($osVersion.Minor).$($osVersion.Build).$($osVersion.Revision)"
    
    # Get and display installed updates
    Write-Output "Installed Updates:"
    Get-HotFix | Sort-Object InstalledOn | Format-Table HotFixID, Description, InstalledOn
  • Interactive Sequential Folder Creator PowerShell Script

    This PowerShell script allows users to select a target directory either by entering a path directly or by navigating through subdirectories with a numbered menu. Once a directory is chosen, the script creates a series of sequentially numbered folders using a base name provided by the user.

    Click to view script…
    # Function: Recursively select a folder by listing subdirectories
    function Select-Folder($startingPath) {
        $currentPath = $startingPath
        while ($true) {
            Write-Host "`nCurrent Directory: $currentPath" -ForegroundColor Cyan
    
            # Get subdirectories (if any)
            $subDirs = Get-ChildItem -Path $currentPath -Directory -ErrorAction SilentlyContinue
    
            if (!$subDirs -or $subDirs.Count -eq 0) {
                Write-Host "No subdirectories found in $currentPath. Using this folder."
                break
            }
    
            # List options: 0 to select current folder, then list each subfolder with a number
            Write-Host "0: [Select this directory]"
            $index = 1
            foreach ($dir in $subDirs) {
                Write-Host ("{0}: {1}" -f $index, $dir.Name)
                $index++
            }
    
            $choice = Read-Host "Enter the number to navigate into a folder, or 0 to select the current directory"
    
            if (-not [int]::TryParse($choice, [ref]$null)) {
                Write-Host "Invalid input. Please enter a number."
                continue
            }
    
            $choice = [int]$choice
    
            if ($choice -eq 0) {
                break
            }
            elseif ($choice -ge 1 -and $choice -le $subDirs.Count) {
                $currentPath = $subDirs[$choice - 1].FullName
            }
            else {
                Write-Host "Invalid selection. Please try again."
            }
        }
        return $currentPath
    }
    
    # Prompt the user to choose the directory selection method
    Write-Host "Select target directory method:" -ForegroundColor Green
    Write-Host "1: Type the full directory path"
    Write-Host "2: Navigate directories using a numbered list"
    $method = Read-Host "Enter 1 or 2"
    
    if ($method -eq "1") {
        $targetDirectory = Read-Host "Enter the target directory path"
        if (-not (Test-Path $targetDirectory)) {
            Write-Host "Directory '$targetDirectory' does not exist. Exiting." -ForegroundColor Red
            exit
        }
    }
    elseif ($method -eq "2") {
        # Optionally allow the user to set a starting directory; default is C:\
        $startingPath = Read-Host "Enter starting directory path (press Enter for default C:\)"
        if ([string]::IsNullOrWhiteSpace($startingPath)) {
            $startingPath = "C:\"
        }
        if (-not (Test-Path $startingPath)) {
            Write-Host "The starting directory '$startingPath' does not exist. Exiting." -ForegroundColor Red
            exit
        }
        $targetDirectory = Select-Folder $startingPath
        Write-Host "Selected Directory: $targetDirectory" -ForegroundColor Green
    }
    else {
        Write-Host "Invalid selection. Exiting." -ForegroundColor Red
        exit
    }
    
    # Prompt user for the base folder name
    $baseName = Read-Host "Enter the base folder name for the folders to be created"
    
    # Prompt user for the number of folders to create
    $numFoldersInput = Read-Host "Enter the number of folders to create"
    if (-not [int]::TryParse($numFoldersInput, [ref]$null)) {
        Write-Host "The number of folders must be a valid integer. Exiting." -ForegroundColor Red
        exit
    }
    $numFolders = [int]$numFoldersInput
    
    # Create folders sequentially
    for ($i = 1; $i -le $numFolders; $i++) {
        # Construct folder name: "BaseName 1", "BaseName 2", etc.
        $folderName = "{0} {1}" -f $baseName, $i
        $folderPath = Join-Path $targetDirectory $folderName
    
        if (-not (Test-Path $folderPath)) {
            New-Item -Path $folderPath -ItemType Directory | Out-Null
            Write-Host "Created folder: $folderPath" -ForegroundColor Yellow
        }
        else {
            Write-Host "Folder already exists: $folderPath" -ForegroundColor DarkYellow
        }
    }
  • Installed Applications Info PowerShell Script

    This PowerShell script scans the Windows registry for installed applications, retrieving key details such as application name, version number, publisher, and installation date. It then displays the information in a neatly formatted table, making it easy to audit and manage software installations on your system.

    Click to view script…
    # Define registry paths for 64-bit and 32-bit applications
    $registryPaths = @(
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
        "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
    )
    
    # Retrieve applications from the registry
    $installedApps = foreach ($path in $registryPaths) {
        Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
            Where-Object { $_.DisplayName -and $_.DisplayVersion }
    }
    
    # Display the results in a formatted table
    $installedApps |
        Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
        Sort-Object DisplayName |
        Format-Table -AutoSize
  • USB Device Logger PowerShell Script

    A PowerShell script that logs detailed information about all connected USB devices, including their names, PNP device IDs, and descriptions, along with a timestamp. This tool is ideal for tracking USB connections over time for auditing or troubleshooting purposes.

    Click to view script…
    <#
    .SYNOPSIS
        Logs all USB devices connected to the system.
    
    .DESCRIPTION
        This script uses CIM/WMI to query for USB devices (by filtering for PNPDeviceIDs that start with "USB")
        and logs their details (Name, PNPDeviceID, Description) along with a timestamp. The log is appended to a file,
        which by default is stored in the same directory as the script (USBDeviceLog.txt).
    
    .NOTES
        Run this script with the necessary permissions. 
        Adjust the log file path if needed.
    #>
    
    # Define the log file location (you can change this to an absolute path if desired)
    $logFilePath = Join-Path -Path $PSScriptRoot -ChildPath "USBDeviceLog.txt"
    
    # Get the current timestamp
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    
    # Create the log entry header
    $logEntry = "USB Device Log - $timestamp`r`n" + ("=" * 50) + "`r`n"
    
    # Query for USB devices by filtering Win32_PnPEntity for entries with a PNPDeviceID starting with "USB"
    try {
        $usbDevices = Get-CimInstance -ClassName Win32_PnPEntity | Where-Object { $_.PNPDeviceID -like "USB*" }
    }
    catch {
        Write-Error "Error querying USB devices: $_"
        exit
    }
    
    if ($usbDevices) {
        foreach ($device in $usbDevices) {
            $logEntry += "Name         : " + $device.Name + "`r`n"
            $logEntry += "PNPDeviceID  : " + $device.PNPDeviceID + "`r`n"
            $logEntry += "Description  : " + $device.Description + "`r`n"
            $logEntry += ("-" * 40) + "`r`n"
        }
    }
    else {
        $logEntry += "No USB devices found." + "`r`n"
    }
    
    $logEntry += "`r`n"
    
    # Append the log entry to the log file
    try {
        Add-Content -Path $logFilePath -Value $logEntry
        Write-Host "USB devices logged successfully to $logFilePath" -ForegroundColor Green
    }
    catch {
        Write-Error "Failed to write to log file: $_"
    }
  • Drive Space Information PowerShell Script

    This PowerShell script retrieves and displays space information for all local fixed drives, including total size, free space, used space, and the percentage of free space.

    Click to view script…
    # Get-DriveSpaceInfo.ps1
    # This script retrieves space information for each local drive (DriveType=3) on the computer.
    # It displays the drive letter, total size, free space, used space, and percentage of free space.
    
    # Retrieve local disk drives (DriveType=3 indicates local fixed disks)
    $drives = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3"
    
    if (!$drives) {
        Write-Host "No local drives found."
        exit
    }
    
    foreach ($drive in $drives) {
        $driveLetter = $drive.DeviceID
        $totalSize = $drive.Size
        $freeSpace = $drive.FreeSpace
    
        # Calculate used space and percentage of free space (if total size is not zero)
        if ($totalSize -gt 0) {
            $usedSpace = $totalSize - $freeSpace
            $percentFree = [Math]::Round(($freeSpace / $totalSize) * 100, 2)
            $totalSizeGB = [Math]::Round($totalSize / 1GB, 2)
            $freeSpaceGB = [Math]::Round($freeSpace / 1GB, 2)
            $usedSpaceGB = [Math]::Round($usedSpace / 1GB, 2)
        }
        else {
            $usedSpace = 0
            $percentFree = 0
            $totalSizeGB = 0
            $freeSpaceGB = 0
            $usedSpaceGB = 0
        }
    
        Write-Host "Drive: $driveLetter" -ForegroundColor Cyan
        Write-Host "  Total Size: $totalSizeGB GB"
        Write-Host "  Free Space: $freeSpaceGB GB ($percentFree`%)"
        Write-Host "  Used Space: $usedSpaceGB GB"
        Write-Host "-------------------------------------"
    }
  • Largest Files Explorer with Interactive Details PowerShell Script

    This PowerShell script scans a specified directory for files, lists the largest files in a neat, truncated table, and lets you view full details for any selected file or all files, providing a comprehensive yet user-friendly overview of disk usage.

    Click to view script…
    <#
    .SYNOPSIS
        Lists the largest files in a directory with a truncated display and full detail lookup.
    
    .DESCRIPTION
        This script recursively scans a specified directory (or the current directory by default),
        sorts all files by size in descending order, and displays the top N largest files as specified by the user.
        It shows a truncated file name and full path for a neat table view.
        After displaying the table, the user can choose an index (or "all") to view full details of the file(s).
    
    .NOTES
        Run as Administrator if scanning protected directories.
    #>
    
    # Function to convert bytes to a human-readable format.
    function Convert-BytesToHumanReadable {
        param ([long]$bytes)
        if ($bytes -ge 1PB) {
            return "{0:N2} PB" -f ($bytes / 1PB)
        }
        elseif ($bytes -ge 1TB) {
            return "{0:N2} TB" -f ($bytes / 1TB)
        }
        elseif ($bytes -ge 1GB) {
            return "{0:N2} GB" -f ($bytes / 1GB)
        }
        elseif ($bytes -ge 1MB) {
            return "{0:N2} MB" -f ($bytes / 1MB)
        }
        elseif ($bytes -ge 1KB) {
            return "{0:N2} KB" -f ($bytes / 1KB)
        }
        else {
            return "$bytes B"
        }
    }
    
    # Function to truncate long strings.
    function Truncate-String {
        param (
            [Parameter(Mandatory = $true)]
            [string]$str,
            [int]$maxLength = 50
        )
        if ($str.Length -gt $maxLength) {
            return $str.Substring(0, $maxLength - 3) + "..."
        }
        else {
            return $str
        }
    }
    
    # Prompt the user for the directory to scan (default is current directory).
    $directory = Read-Host "Enter the directory to scan (or press Enter for current directory)"
    if ([string]::IsNullOrWhiteSpace($directory)) {
        $directory = (Get-Location).Path
    }
    
    if (-not (Test-Path $directory)) {
        Write-Error "Directory '$directory' does not exist."
        exit
    }
    
    # Prompt the user for the number of items to list.
    $numberOfItems = Read-Host "Enter the number of largest files to list"
    if (-not [int]::TryParse($numberOfItems, [ref]$null)) {
        Write-Error "Invalid number entered. Please enter a valid integer."
        exit
    }
    $numberOfItems = [int]$numberOfItems
    
    Write-Host "Scanning directory '$directory' for files..." -ForegroundColor Cyan
    
    # Recursively retrieve all files within the specified directory.
    try {
        $files = Get-ChildItem -Path $directory -File -Recurse -ErrorAction SilentlyContinue
    } catch {
        Write-Error "Error retrieving files: $_"
        exit
    }
    
    if (!$files) {
        Write-Host "No files found in '$directory'."
        exit
    }
    
    # Sort the files by size (Length) descending and take the top N items.
    $largestFiles = $files | Sort-Object Length -Descending | Select-Object -First $numberOfItems
    
    # Build a table with an index and truncated file name and path.
    $i = 1
    $result = foreach ($file in $largestFiles) {
        [PSCustomObject]@{
            Index         = $i
            "File Name"   = Truncate-String -str $file.Name -maxLength 30
            "Full Path"   = Truncate-String -str $file.FullName -maxLength 60
            "Size (Bytes)"= $file.Length
            "Size (Human)"= Convert-BytesToHumanReadable -bytes $file.Length
        }
        $i++
    }
    
    # Display the results in a formatted table.
    Write-Host "`nTop $numberOfItems largest files in '$directory':" -ForegroundColor Green
    $result | Format-Table -AutoSize
    
    # Allow the user to view full details if needed.
    $choice = Read-Host "`nEnter a file index to view full details, type 'all' to view details for all files, or press Enter to exit"
    if (-not [string]::IsNullOrWhiteSpace($choice)) {
        if ($choice -match '^(all)$') {
            Write-Host "`nFull details for all files:" -ForegroundColor Cyan
            $largestFiles | Format-List *
        }
        elseif ([int]::TryParse($choice, [ref]$null)) {
            $index = [int]$choice
            if ($index -ge 1 -and $index -le $largestFiles.Count) {
                Write-Host "`nFull details for file at index $($index):" -ForegroundColor Cyan
                $largestFiles[$index - 1] | Format-List *
            }
            else {
                Write-Host "Invalid index entered." -ForegroundColor Yellow
            }
        }
    }
  • DNS Ping Test Utility PowerShell Script

    This PowerShell script allows users to select from a list of popular DNS servers and perform customizable ping tests to verify network connectivity. It supports both continuous and fixed-count pings, configurable intervals, optional logging, and displays summary statistics for comprehensive network diagnostics.

    Click to view script…
    <#
    .SYNOPSIS
        DNS Ping Test Utility
    
    .DESCRIPTION
        This script allows the user to choose from a list of popular DNS servers (e.g., Google, Cloudflare, OpenDNS, etc.)
        and then performs either continuous or fixed-count ping tests to verify network connectivity.
        Users can configure the interval between pings and optionally log the results to a file for further analysis.
        
    .NOTES
        Press Ctrl+C to exit continuous ping mode.
    #>
    
    # Define a list of DNS providers.
    $dnsProviders = @(
        [PSCustomObject]@{Name="Google DNS";       IP="8.8.8.8"},
        [PSCustomObject]@{Name="Cloudflare DNS";   IP="1.1.1.1"},
        [PSCustomObject]@{Name="OpenDNS";          IP="208.67.222.222"},
        [PSCustomObject]@{Name="Quad9 DNS";        IP="9.9.9.9"},
        [PSCustomObject]@{Name="Level3 DNS";       IP="4.2.2.2"}
    )
    
    # Display the DNS server options.
    Write-Host "Select a DNS server to ping:" -ForegroundColor Cyan
    for ($i = 0; $i -lt $dnsProviders.Count; $i++) {
        Write-Host ("{0}. {1} ({2})" -f ($i + 1), $dnsProviders[$i].Name, $dnsProviders[$i].IP)
    }
    
    # Prompt the user to choose a DNS server.
    [int]$choice = Read-Host "Enter the number corresponding to your choice"
    if ($choice -lt 1 -or $choice -gt $dnsProviders.Count) {
        Write-Error "Invalid selection. Exiting."
        exit
    }
    
    $selectedDNS = $dnsProviders[$choice - 1]
    Write-Host "You selected: $($selectedDNS.Name) ($($selectedDNS.IP))" -ForegroundColor Green
    
    # Ask if the user wants a fixed number of pings or continuous ping.
    $pingCountInput = Read-Host "Enter number of pings (or press Enter for continuous ping)"
    if ($pingCountInput -match '^\d+$') {
        $pingCount = [int]$pingCountInput
        $isContinuous = $false
    } else {
        $isContinuous = $true
    }
    
    # Ask for the interval between pings in seconds.
    $intervalInput = Read-Host "Enter interval in seconds between pings (default is 1 second)"
    if ([string]::IsNullOrWhiteSpace($intervalInput)) {
        $interval = 1
    } else {
        $interval = [double]$intervalInput
    }
    
    # Ask if the user wants to log the results to a file.
    $logChoice = Read-Host "Do you want to log results to a file? (Y/N)"
    $logEnabled = $false
    if ($logChoice -match '^[Yy]') {
        $logEnabled = $true
        $logFile = Read-Host "Enter log file path (or press Enter for default 'DNSPingLog.txt')"
        if ([string]::IsNullOrWhiteSpace($logFile)) {
            $logFile = "DNSPingLog.txt"
        }
        Write-Host "Logging enabled. Results will be saved to $logFile" -ForegroundColor Green
    }
    
    # Initialize an array to store successful ping response times if a fixed ping count is specified.
    if (-not $isContinuous) {
        $results = @()
    }
    
    Write-Host "Starting ping test to $($selectedDNS.IP)..." -ForegroundColor Cyan
    
    # Function to log output if logging is enabled.
    function Log-Output {
        param (
            [string]$message
        )
        if ($logEnabled) {
            $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            Add-Content -Path $logFile -Value "[$timestamp] $message"
        }
    }
    
    # Run the ping test.
    if ($isContinuous) {
        Write-Host "Press Ctrl+C to stop continuous ping."
        while ($true) {
            try {
                $pingResult = Test-Connection -ComputerName $selectedDNS.IP -Count 1 -ErrorAction SilentlyContinue
                $timestamp = Get-Date -Format "HH:mm:ss"
                if ($pingResult) {
                    $replyTime = $pingResult.ResponseTime
                    $output = "[$timestamp] Ping $($i)/$($pingCount): Reply from $($selectedDNS.IP): time = $replyTime ms"
                } else {
                    $output = "[$timestamp] Ping $($i)/$($pingCount): Request timed out for $($selectedDNS.IP)."
                }
                Write-Host $output
                Log-Output -message $output
            }
            catch {
                Write-Host "An error occurred: $_"
                Log-Output -message "Error: $_"
            }
            Start-Sleep -Seconds $interval
        }
    } else {
        for ($i = 1; $i -le $pingCount; $i++) {
            try {
                $pingResult = Test-Connection -ComputerName $selectedDNS.IP -Count 1 -ErrorAction SilentlyContinue
                $timestamp = Get-Date -Format "HH:mm:ss"
                if ($pingResult) {
                    $replyTime = $pingResult.ResponseTime
                    $output = "[$timestamp] Ping $($i)/$($pingCount): Reply from $($selectedDNS.IP): time = $replyTime ms"
                    $results += $replyTime
                } else {
                    $output = "[$timestamp] Ping $($i)/$($pingCount): Request timed out for $($selectedDNS.IP)."
                }
                Write-Host $output
                Log-Output -message $output
            }
            catch {
                Write-Host "An error occurred: $_"
                Log-Output -message "Error: $_"
            }
            Start-Sleep -Seconds $interval
        }
        
        # Provide summary statistics for the fixed-count ping test.
        if ($results.Count -gt 0) {
            $avgTime = [Math]::Round(($results | Measure-Object -Average).Average, 2)
            Write-Host "`nPing test completed. Average response time: $avgTime ms" -ForegroundColor Green
            Log-Output -message "Ping test completed. Average response time: $avgTime ms"
        } else {
            Write-Host "`nPing test completed. No successful pings." -ForegroundColor Yellow
            Log-Output -message "Ping test completed. No successful pings."
        }
    }