Jun 022016
 

So I have not posted anything to my blog for quite some time. I’ve decided to fix that, and I’m also changing the focus of my blog slightly from a broad catch-all for my technology interests to a more focused blog on what I’m learning with PowerShell, operating systems, and hardware.

First up is a new script I created to generate a server uptime report. Is also uses Microsoft SCCM WMI classes to determine if a reboot is pending (useful if updates delivered via SCCM didn’t trigger a reboot).

I also decided that I didn’t want to maintain a list of servers to query, so I have my script querying Active Directory OUs to compile the server list.

There are some things that you need to set in order to get this operational. The first is the list of OUs you wish to search. The other is information for the email report. The email stuff can be skipped if you wish, but I’ve found it quite useful, as long as you have an open SMTP relay.

Some things I need to do:

  • Allow toggle of CCM query. Not everyone has SCCM running.
  • Add optional authentication to email for relays that are not unauthenticated.
  • Additional error handling.

So here you go, and as always, let me know if you see something I can improve upon. I am learning this stuff after all.

#requires -Version 2 -Modules ActiveDirectory

# Author: Robert Hollingshead 
# Version: 1 
# Version History: First version. 
# Purpose: Get a list of servers based on arbitrary OU's, and then check each server for uptime. 
# Also grab pending reboot flag from CCM. Send as a report to an email address. 
# To-Do List: Allow toggle of CCM reboot-pending. Add optional authentication to mail. Additional error handling.

# Uncomment this to disable progress bar.
# $ProgressPreference = 'SilentlyContinue'

# Enter OUs to search here. One per line, follow the format below, omit comma on last line.
# You definately want to edit this stuff.
$OUsToSearch = @(
  'OU=OU=Servers,DC=Contoso,DC=net', 
  'OU=Domain Controllers,DC=Contoso,DC=net'
)

# Enter your mail information here. 
$MailSender = '{sender email address here}'
$MailRecipients = '{recipient list here}'
$MailSubject = "Server UpTime Report for $(Get-Date -Format d)"
$MailBody = $MailSubject + ' is attached'
$MailServer = '{smtp server here}'

# Change nothing below this line. 
# ===============================

# Grab the class so that we can use convert to date fu nction. 
$WMI = [wmiclass]'\\.\root\cimv2:win32_operatingsystem'

# Set up some arrays. 
[system.array]$UpTimeReport = $null
[system.array]$Servers = $null

# Query Active Directory
ForEach ($OU in $OUsToSearch) 
{ 
  $OUResult = $null
  Try 
  { 
    $OUResult = Get-ADComputer -SearchBase $OU -Filter * -ErrorAction Stop
    $Servers = $Servers + $OUResult
  }
  catch 
  {
    Write-Warning -Message "Failed to get servers in $OU"
  }
}

# Set up for progress bar.
[long]$totalitems = $Servers.count
[long]$progress = 1
[long]$percentage = 0

# Query each server for uptime. 
foreach ($Server in $Servers) 
{
  # Calculate percent complete and write-progress (if enabled)
  $percentage = ($progress * 100) / $totalitems
  Write-Progress -Activity 'Querying servers.' -PercentComplete $percentage `
  -Status "$percentage% complete..." -CurrentOperation "Checking $($Server.name)."

  # Attempt to get last bootup time and local date/time, then calculate. If fails, handle error. 
  try 
  { 
    $Stopwatch = Measure-Command -Expression {
      $OSProps = Get-WmiObject -ComputerName $Server.name -Class win32_operatingsystem -Property LocalDateTime, LastBootupTime
      $UpTime = $WMI.ConvertToDateTime($OSProps.LocalDateTime) - $WMI.ConvertToDateTime($OSProps.LastBootUpTime)
      $LastBootTime = $WMI.ConvertToDateTime($OSProps.LastBootUpTime)

      # Use CCM to determine if a reboot is pending.
      $CCMWmi = [wmiclass]"\\$($Server.name)\ROOT\ccm\ClientSDK:CCM_ClientUtilities"
      $Reboot = ($CCMWmi.DetermineIfRebootPending()).RebootPending
    }
    $ServerUptime = "$($UpTime.days)+$($UpTime.Hours):$($UpTime.Minutes):$($UpTime.Seconds).$($UpTime.Milliseconds)"
    $TimeToRetrieve = "$($Stopwatch.Minutes):$($Stopwatch.Seconds).$($Stopwatch.Milliseconds)"
  }
  catch 
  {
    $ServerUptime = 'Not Available'
    $LastBootTime = 'Not Available'
    $TimeToRetrieve = 'Failed To Retrieve'
    $Reboot = $false
  }

  # Create object to be added to report. 
  $CurrentServer = New-Object -TypeName PSObject
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'ServerName' -Value $($Server.name) 
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'UpTime' -Value $($ServerUptime)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'LastBootTime' -Value $($LastBootTime)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'TimeToRetrieve' -Value $($TimeToRetrieve)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'TimeRetrieveFinished' -Value $(Get-Date -Format T)
  $CurrentServer | Add-Member -MemberType NoteProperty -Name 'RebootPending' -Value $($Reboot)
  
  # Add to report object.
  $UpTimeReport = $UpTimeReport + $CurrentServer
  
  # Increment progress counter for progress bar. 
  $progress++
}

$ReportPath = $env:Temp + '\ServerUpTime.csv'
$UpTimeReport | Export-Csv $ReportPath
Send-MailMessage -From $MailSender -To $MailRecipients -Subject $MailSubject -Body $MailBody -Attachments $ReportPath -SmtpServer $MailServer
%d bloggers like this: