Showing posts with label disk space. Show all posts
Showing posts with label disk space. Show all posts

Friday, 14 October 2016

Windows Server Health Check Script - Powershell

Here is a script that I have developed to run a "health check" on a windows server. The script can be run on demand, but I tend to run them from Windows Task Scheduler with a trigger to run on windows startup to automatically check some of the key things within a windows server. At a high level, here is what is contained within the script;
  • A report of all services with a start mode of "Automatic" but are not in a "running" state
  • Any events in the windows Application log from the past 24 hours that are not categorized as "informational"
  • Any events in the windows System log from the past 24 hours that are not categorized as "informational"
  • A size report of all logical disks in the server showing size, and free space in GB and as a percentage
  • Average CPU load on server
  • Current memory usage (as a percentage)
All this information is retrieved by the script and stored in a .html file. At the end of the script the .html file is saved to disk and sent as an email attachment.

A few other points to note;
  • The start-sleep command is used at the start of the script to delay the script running for 300 seconds (5 minutes). If the script runs as soon as it is triggered on windows startup, not all windows components are ready/loaded and the results of the script are incomplete
  • The log file is saved by default into C:\Admin\ServerHealthChecks with a filename based on the target IP/hostname and date/time the script runs. This can be changed using the $logfile variable. Make sure the path specified in $logfile exists otherwise the .html file will not save correctly
  • If a disk has 10-20% free space, it's text will be orange in colour. Less than 10% will be red. Otherwise it will be green
  • Likewise, if CPU usage is 80-90% it will be orange. 90-100% will be red
  • If free memory is 10-20%, it will be orange. Less than 10% will be red
  • Be sure to update the variables at the end of the script (under #Send Log File via Email) to work with your email system/environment
Powershell script/code is below;

#Server Health Checker
#Author: Peter Morrissey

start-sleep 300

$target = "127.0.0.1"

#Get Last Bootup Time
$osprops = get-wmiobject -class win32_operatingsystem 
$lastboot = $osprops.ConvertToDateTime($osprops.LastBootUpTime)

#Log File
$error.clear()
$fulldate = Get-Date
$logfile = "c:\" + "Admin\" + "ServerHealthChecks\" + $FullDate.ToString("yyyyMMddHHmm") + "-" + "$Target" + ".html"
$global:strname = $env:username
$global:html = @()
$html += "<html>"
$html += '<font face="courier">'
$html += "<title>Server Health Check - $Target</title>"
$html += "<h3>$FullDate</h3>"
$html += "<h4>Server Health Check Script - Run by $global:strname</h4>"
$html += "<h4>Target Server: $Target</h4>"
$html += "<h4>Hostname: $env:computername</h4>"
$html += "<h4>Last Boot: $lastboot </h4>"
$html += "<p>"
$html += "******************************************************************************************* <p>"

#Services Check
write-host "Getting Service Information"
$html += '<u>Service Report - Services with StartMode "Auto" and State not currently "Running" </u><p>'

$servicelist = invoke-command -computername $target {
        get-wmiobject -class Win32_Service | Where {$_.StartMode -eq "Auto" -and $_.State -ne "Running"} | Select DisplayName, Name, StartMode, State
        }
if ($servicelist){$temphtml = $servicelist | Select DisplayName, Name, StartMode, State | ConvertTo-HTML -fragment
                    ForEach ($line in $temphtml){$html += "$Line"} }
ELSE {write-host "All automatic services are running";$html += "All automatic services are running <p>"}

$html += "<p> ******************************************************************************************* <p>"

#EventLog Checks
write-host "Querying Application Log on $Target"
$appeventlog = invoke-command -computername $target {
        $targetDate = Get-Date
        $targetdate = $targetdate.adddays(-1)
        get-eventlog -logname "Application" -after $targetdate | where-object {$_.entrytype -ne "Information" -and $_.Source -ne "Print" -and $_.Source -ne "TermServDevices"} 
        }

if ($appeventlog){#write-host $AppEventLog
                  $html += "<u>Application Log Non-Information Events (Last 24 Hours)</u><p>" 
                  $temphtml = $appeventlog |  Select TimeGenerated, EntryType, Source, Message | ConvertTo-HTML -fragment
                  foreach ($line in $temphtml){$html += "$line"}
                  }
                  
ELSE {$html += "No non-informational events found in application log in past 24 hours <p>"}

$html += "<p>*******************************************************************************************<p>"

write-host "Querying System Log on $Target"

$syseventlog = invoke-command -computername $Target {
        $targetdate = get-date
        $targetdate = $targetdate.adddays(-1)
        get-eventlog -logname "System" -after $targetdate | where-object {$_.entrytype -ne "Information" -and $_.source -ne "Print" -and $_.source -ne "TermServDevices"} 
        }

if ($syseventlog){#write-host $syseventlog
                  $html += "<u>System Log Non-Information Events (Last 24 Hours)</u><p>"
                  $temphtml = $syseventlog | Select TimeGenerated, EntryType, Source, Message | ConvertTo-HTML -fragment
                  ForEach ($line in $temphtml){$html += "$Line"}
                  }
                  
else {$html += "No non-informational events found in system log in past 24 hours<p>"}

$html += "<p>*******************************************************************************************<p>"

#Local Disk Health Check
write-host "Getting logical disk information"
$diskreport = invoke-command -computername $target {
    Get-WmiObject Win32_logicaldisk | Select DeviceID, MediaType, VolumeName, `
    @{Name="Size(GB)";Expression={[decimal]("{0:N0}" -f($_.size/1gb))}}, `
    @{Name="Free Space(GB)";Expression={[decimal]("{0:N0}" -f($_.freespace/1gb))}}, `
    @{Name="Free (%)";Expression={"{0,6:P0}" -f(($_.freespace/1gb) / ($_.size/1gb))}} `
    }

$html += "<u>Logical Disk Report</u><p>"
$temphtml = $DiskReport | Select DeviceID, VolumeName, "Size(GB)", "Free Space(GB)", "Free (%)" | ConvertTo-HTML -fragment
foreach ($Line in $temphtml)
    {
    if ($line -like "*%<*")
        {
        $lineindex = [array]::IndexOf($temphtml, $line)
        $templine = $line -split "%"
        $templine = $templine -replace(" ","")
        $templine = $templine[0] -split "<td>"
        $templine = $templine[5]
        $templine = $templine.trim()
        $templine = [decimal]$templine
               
        if ($templine -le 20 -and $templine -ge 10){$temphtml[$lineindex] = $temphtml[$lineindex].Replace("<td>",'<td><font color="orange">')}
        if ($templine -lt 10 ){$temphtml[$lineindex] = $temphtml[$lineindex].Replace("<td>",'<td><font color="red">');
                                $smtpclient = new-object system.net.mail.smtpClient 
                                $mailmessage = New-Object system.net.mail.mailmessage 
                                $smtpclient.Host = "192.168.0.5" 
                                $mailmessage.from = ("alerts@mitx.dynu.com") 
                                $mailmessage.To.add("peter@mitx.dynu.com")
                                $mailmessage.Subject = “Server Health Check Alert - Disk Space Low - $target"
                                $mailmessage.Body = "Low disk space found on $target -  $temphtml"
                                $mailmessage.IsBodyHtml = $true
                                $smtpclient.Send($mailmessage)
                                $mailmessage.dispose()
                              }
        }
    }

foreach ($line in $temphtml){$html += "$line"}

$html += "<p>*******************************************************************************************<p>"

#Check CPU Load
write-host "Getting CPU Stats"
$cpudata = get-wmiobject win32_processor -computername $target | measure-object -property LoadPercentage -average | select Average
$html += "<u>CPU Load (Average)</u><p>"
$cpuusage = $($cpudata.average)
    if ($cpuusage -ge 80 -and $cpuusage -le 90){$html += '<font color="orange">' + "Average CPU Load: $cpuusage%" + '</font>'}
    elseif ($cpuusage -gt 90){$html += '<font color="red">' + "Average CPU Load: $cpuusage%" + '</font>'}
    elseif ($cpuusage -lt 80){$html += '<font color = "green">' + "Average CPU Load: $cpuusage%" + '</font>'}
$html += "<p>*******************************************************************************************<p>"

#Check Memory Usage
write-host "Getting Memory Usage"
$html += "<u>Memory Usage Report</u><p>"
$memdata = get-wmiobject win32_operatingsystem -computername $target | select FreePhysicalMemory, FreeVirtualMemory, TotalVirtualMemorySize, TotalVisibleMemorySize
$freephysicalmem = $($memdata.freephysicalmemory)
$freevirtualmem = $($memdata.freevirtualmemory)
$totalvirtualmem = $($memdata.totalvirtualmemorysize)
$totalvisiblemem = $($memdata.totalvisiblememorysize)
$memusage = ($totalvisiblemem - $freephysicalmem) / $totalvisiblemem * 100
$freemem = $freephysicalmem / $totalvisiblemem * 100
[decimal]$freemem = "{0:N0}" -f $freemem

if ($freemem -lt 10){$html += '<font color="red">' + "Free Memory (%): $freemem" + '</font>'}
elseif ($freemem -gt 10 -and $freemem -le 20){$html += '<font color="orange">' + "Free Memory (%): $freemem" + '</font>'}
elseif ($freemem -gt 20){$html += '<font color="green">' + "Free Memory (%): $freemem" + '</font>'}
$html += "<p>*******************************************************************************************<p>"

#Export Log File
$html | out-file $LogFile

#Open Log File in Internet Explorer
#Invoke-Item $Logfile

#Send Log File via Email
$smtpclient = new-object system.net.mail.smtpClient 
$mailmessage = New-Object system.net.mail.mailmessage 
$smtpclient.Host = "mail.server.com" 
$mailmessage.from = ("user@domain.com") 
$mailmessage.To.add("user@domain.com")
$mailmessage.Subject = “Server Health Check Results - $target"
$mailmessage.Body = "Server Health Check Results for $target"
$mailmessage.Attachments.Add($LogFile)
$mailmessage.IsBodyHtml = $true
$smtpclient.Send($mailmessage)
$mailmessage.dispose()

Tuesday, 11 October 2016

Removing old Windows Updates - Windows Installer Directory

Windows updates can tend to take up a large number of space after some time on the system drive (C:\), and are not easily removed.

Updates are typically installed into the C:\Windows\Installer directory and hold the .msi and .msp files used to install (or uninstall) windows updates.

A company called "Homedev" have developed a product called "Patch Cleaner" that is clever enough to search this windows installer directory and detect which patches can safely be removed. Over time windows updates tend to replace, outdate or supersede each other - rendering them useless and taking up precious hard disk space on your hard disk!

How does it work? Well, as explained by Homedev, their application queries the operating system for a list of all the currently installed patches and updates. It then compares this list returned by the operating system against all the files in the C:\Windows\Installer directory. Anything that's found in the folder but not in the list provided by the operating system is flagged as able to be removed by the application.

The application also has the ability to relocate the files to another location first (such as another drive, like an external USB hdd) which reduces the risk involved in removing some of these files. If it turns out the files are required they can simply be copied back to the C:\Windows\Installer directory.

The latest version of the application can be downloaded and installed from the below website;

http://www.homedev.com.au/free/patchcleaner


  1. Once you have downloaded the file, double click it to run/execute
  2. Click Next to begin the installation process

  3. Select I Agree on the license agreement then select Next

  4. It is recommended to leave the default installation path and set to Everyone to be able to access the application. Click Next

  5. Click Next to begin the installation

  6. Click Close to exit the wizard once installation has completed

  7. You can now run PatchCleaner from the shortcut placed on the desktop or from within the Start Menu

  8. Upon startup, the application will automatically scan for files that can be removed

  9. Once the scan is finished, you will be presented with a window like the one below. The screen details how many files are orphaned and can be removed (in this example, there are 32 files, totalling 2.17GB in size). You can click either the Delete button to permanently delete the files, or the Move button the move all these files to another location (as mentioned previously)

  10. After deleting or moving the files, the application will run another scan and present a window again like the one above.