Screencast 29: Bones

Powershell Script - bones.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

<#
.SYNOPSIS
Simple HTML report generator for server status, free RAM, and Disk capacity.
The report info is injected into a web page. If anything is in the red it'll yell
at you via email.
.NOTES
Original script from Martin Pugh (@TheSurlyAdm1n), greatly hacked/rewritten/made worse
by Tobin Bradley (@fuzzytolerance).
#>

[CmdletBinding()]
Param (
[Parameter(ValueFromPipeline=$true,
ValueFromPipelinebyPropertyName=$true)]
[Alias("Servers")]
[string[]]$Name = (Get-Content ".\servers.txt"),
[int]$DriveAlertThreshold = 10,
[int]$RAMAlertThreshold = 512,
[string]$Credential = "domain\username",
[string]$PathToCred = ".\",
[string]$smtpServer = "servername",
[string]$publishPath = "\\where\to\put\it\index.html",
[string]$toMail = "hottub@fun.com"
)

Begin {
$path = split-path -parent $MyInvocation.MyCommand.Definition
Set-Location -Path $Path

Function Get-Credentials {
Param (
[String]$AuthUser = $env:USERNAME
)
$Key = [byte]29,36,18,74,72,75,85,52,73,44,0,21,98,76,99,27

#Build the path to the credential file
$CredFile = $AuthUser.Replace("\","~")
$File = $PathToCred + "\Credentials-$CredFile.crd"
#And find out if it's there, if not create it
If (-not (Test-Path $File)) {
(Get-Credential $AuthUser).Password | ConvertFrom-SecureString -Key $Key | Set-Content $File
}
#Load the credential file
$Password = Get-Content $File | ConvertTo-SecureString -Key $Key
$AuthUser = (Split-Path $File -Leaf).Substring(12).Replace("~","\")
$AuthUser = $AuthUser.Substring(0,$AuthUser.Length - 4)
$Credential = New-Object System.Management.Automation.PsCredential($AuthUser,$Password)
Return $Credential
}

Function Send-Mail {
param($To, $From, $Subject, $Body, $mailServer)
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($mailServer)
$msg.From = $From
$msg.To.Add($To)
$msg.Subject = $Subject
$msg.IsBodyHtml = 1
$msg.Body = $Body
$smtp.Send($msg)
}

Write-Verbose "$(Get-Date): Script begins!"
$AllComputers = @()
}

Process {
#Gather all computer names before processing
$Name = $Name | Sort
ForEach ($Computer in $Name) {
$AllComputers += $Computer
}
}

End {
#Sort the servers by name, then start getting information
Write-Verbose "Sort server names and gather Credential information"

$DiskData = @()
$date = Get-Date
$thedate = "<p>Last Ran " + $date + "</p>"
$errors = $null

If ($Credential) {
$Cred = Get-Credentials $Credential
}

ForEach ($Computer in $AllComputers) {
Write-Verbose "Testing $Computer..."
$ErrorReport = $null
$DriveReport = $null
If (Test-Connection $Computer -Quiet) {
#Set parameters for splat, determine if checking local
$CredParameter = @{
ComputerName = $Computer
ErrorAction = "Stop"
}
If ($Computer.ToUpper() -notlike "*$($env:COMPUTERNAME.ToUpper())*" -and $Cred) {
$CredParameter.Add("Credential",$Cred)
}

#Get uptime and memory information
Try {
$WMI = Get-WmiObject Win32_OperatingSystem @CredParameter
If ($WMI) {
$Uptime = New-TimeSpan -Start $($WMI.ConvertToDateTime($WMI.LastBootUpTime)) -End (Get-Date)
$UpText = "<td>Up for $($Uptime.Days)d, $($Uptime.Hours)h, $($Uptime.Minutes)m</td>"
}
Else {
$UpText = "<td class=""up"">Up</td>"
}
#Get disk information and pretty up the data
$Disks = Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3" @CredParameter | Select `
@{LABEL="Server";EXPRESSION={$Computer}},
@{LABEL="DriveLetter";EXPRESSION={$_.DeviceID}},
@{LABEL="Size";EXPRESSION={[int]("{0:N0}" -f ($_.Size/1gb))}},
@{LABEL="FreeSize";EXPRESSION={[int]("{0:N0}" -f ($_.FreeSpace/1gb))}},
@{LABEL="perUsed";EXPRESSION={[int]("{0:N0}" -f ((($_.Size - $_.FreeSpace)/$_.Size)*100))}},
@{LABEL="perFree";EXPRESSION={[int]("{0:N0}" -f (100-(($_.Size - $_.FreeSpace)/$_.Size)*100))}},
VolumeName
$DiskData += $Disks

# check the ram
$theram = ([math]::round($WMI.FreePhysicalMemory / 1024))
if ($theram -ge $RAMAlertThreshold) {
$FreeRAM = "<td class=""text-right"">$theram MB</td>"
}
else {
$FreeRAM = "<td class=""down text-right"">$theram MB</td>"
$errors += "Low free RAM on $Computer.<br>"
}
}
Catch {
Write-Verbose "Error encountered gathering information for $Computer"
$ErrorReport = $Error[0]
$Error.Clear | Out-Null
}

#Create the simple Status table
If ($ErrorReport) {
$UpText = "<td class=""down"">WMI Error</td>"
$DiskHTML = "<div class=""red"">$($Error[0])</div>"
}
ElseIf ($Disks) {
$DiskHTML = $null
ForEach ($Disk in $Disks) {
If ($Disk.perFree -le $DriveAlertThreshold) {
$FreeClass = "red"
$errors += "$Computer has less than 10% free space on drive $($Disk.DriveLetter) <br>"
}
Else {
$FreeClass = "free"
}
$DiskHTML += "<div class=""green"" style=""width:$($Disk.perUsed)%"">$($Disk.DriveLetter) $($Disk.Size)gb</div><div class=""$FreeClass"" style=""width:$($Disk.perFree)%"">$($Disk.FreeSize)gb ($($Disk.perFree)%)</div>`n<div style=""clear:both""></div>"
}
}
Else {
$DiskHTML = ""
}
$DetailHTML += "<tr><td>$Computer</td>$UpText $FreeRAM<td>$DiskHTML</td></tr>`n"
}
Else {
$DetailHTML += "<tr><td>$Computer</td><td class=""down"">DOWN</td><td class=""down""></td><td class=""down""></td></tr>`n"
$errors += "$Computer is down.<br>"
}
}

# Insert Stuff into index.html
(Get-Content .\index.html) -join "" | foreach{$_ -replace "<!-- Server Report -->(.*?)<!-- End Server Report -->","<!-- Server Report -->$DetailHTML<!-- End Server Report -->"} | Set-Content ./index.html
(Get-Content .\index.html) -join "" | foreach{$_ -replace "<!-- Updated -->(.*?)<!-- End Updated -->","<!-- Updated -->$date<!-- End Updated -->"} | Set-Content ./index.html

# Copy index.html to web server
Copy-Item -Path .\index.html -Destination $publishPath

#Send email if shit went bad
if ($errors) {
Send-Mail -To $toMail -From "noreply@noreply.com" -Subject "He's dead Jim!" -Body "<h1>Oh shit.</h1> <p> $errors </p> <p>Head to http://maps.co.mecklenburg.nc.us/bones for more information.</p>" -mailServer $smtpServer
}

Write-Verbose "$(Get-Date): Script completed!"
}

Sample Index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<style>
img {
max-width: 100%;
}
header {
margin: 75px 0 40px;
}
footer {
background: #d42127;
margin-top: 20px;
box-shadow: 0 -2px 10px #666;
}
nav {
box-shadow: 0 2px 10px #666;
}
td, td.div { white-space: nowrap; }
td.div { overflow: hidden; }
td.up { background-color:#32CD32; }
td.down { background-color:#B22222; }
div.red { background-color:#B22222;
float:left;
text-align:right;
}
div.green {
background-color:#32CD32;
float:left;
}
div.free {
background-color:#7FFF00;
float:left;
text-align:right;
}
span.green {
background: #32CD32;
}
span.free {
background: #7FFF00;
}
.up, .down, .red, .green, .free {
padding: 5px;
}

</style>
</head>

<body>
<div class="container">
<header class="text-center">
Updated <!-- Updated --><!-- End Updated -->
</header>
<div>
<h2>Server Report</h2>
<table class="table">
<thead>
<tr>
<th>Server</th>
<th>Status</th>
<th class="text-right">Free Ram</th>
<th style="width: 60%">Disk Status (
<span class="green">Capacity</span>/
<span class="free">Free</span>)</th>
</tr>
</thead>
<tbody>
<!-- Server Report --><!-- End Server Report -->
</tbody>
</table>
</div>
</div>
</body>

</html>

Resources