Powershell – Get last boot time on remote servers and export results to CSV

Standard

Here’s a script to help you get the last boot time from remote windows servers. If you need to check the uptime of servers or troubleshoot unexpected restarts etc. then this script can be very useful.
The script uses WMI to get the information from the remote servers and then exports the results to a CSV file in the directory where the script was run. I have structured the script in three sections variables, functions and script main. I will go over these sections one by one now.

Variables

#VARIABLES

#$DebugPreference = "continue" #uncomment to get debug info
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath #path to the directory in which the script is run.
$servers = "$dir\Servers.txt" #last boot time will be retrieved on all the servers given in this file.
$CSVPath = "$dir\LastBootTimes.csv" #results will be exported to csv to a file with this path

The object $dir always points to the directory in which the script is run and is used in the paths to files I work with in the script. This makes the script more robust because I can copy the script wherever I want without having to change any paths at all or edit the script.
$servers is the path to “servers.txt” file. You must create this file before you run the script and enter the names (one per line) of the servers you want to get the last boot time from.
$CSVPath is the path to CSV file which will be created when the script is run.

Functions

1. Get-LastBootTime

#FUNCTIONS
function Get-LastBootTime {
	[CmdletBinding()]
	[OutputType([System.String])]
	param(
		[Parameter(ValueFromPipeline=$true)][System.String[]]$server = $env:COMPUTERNAME
	)
	begin {
		function Test-PortAlive {
			#############################################################################################
			##Function:			Test-PortAlive
			##
			##Description:		Tests connection on a given server on a given port.
			##
			##Created by:		Noam Wajnman
			##Creation Date:	April 02, 2014	
			##############################################################################################
			[CmdletBinding()]
			[OutputType([System.boolean])]
			param(
				[Parameter(ValueFromPipeline=$true)][System.String[]]$server
			)
			$socket = new-object Net.Sockets.TcpClient
			$connect = $socket.BeginConnect($server, 135, $null, $null) #port set to 135 (RPC)
			$NoTimeOut = $connect.AsyncWaitHandle.WaitOne(500, $false) #timeout value set to 500 ms
			if ($NoTimeOut) {
				$socket.EndConnect($connect) | Out-Null
				return $true				
			}
			else {
				return $false
			}
		}
	}
	process {		
		$BootTime = $null
		$server = $($server).toUpper()	
		$alive = Test-PortAlive -server $server
		if ($alive) {
			Write-Debug "connection to $server is open on port $port"			
			$OSInfo = Get-WmiObject win32_operatingsystem -ComputerName $server #get the info with WMI
			$BootTime = $OSInfo.ConvertToDateTime($OSInfo.LastBootUpTime) #convert to datetime
			$BootTime = '{0:yyyy-MM-dd HH:mm:ss}' -f $BootTime #parse time to sortable format
			if ($BootTime) {
				Write-Debug "$server was rebooted last time at:`t$BootTime"					
				$result = "" | select "Server","LastBootTime" #creating a custom object
				$result.Server = $server
				$result.LastBootTime = $BootTime
				$result #return the $result object
			}
			else {
				Write-Debug "couldn't get boot time from server $server"
			}
		}			
		else {
			Write-Debug "Error - connection to $server is not open on port $port"			
		}
	}	
}

the function Get-LastBootTime takes one parameter $server which can also be passed to it via the pipeline.
Pipeline functions usually have three parts “begin”, “process” and “end” (this function only uses “begin” and “process”). In the begin section I have included another function “Test-PortAlive” but I won’t go into details about it as I have covered this in a previous post dedicated to that function. In this script I use it to test if the server is alive and the RPC port 135 is open as this is needed to run the WMI query.
The “process” part of the function is where last boot time information is retrieved using WMI. Basically the function attempts to get the info. If successful then a custom object with the properties “server” and “LastBootTime” is created and returned.

Script Main

#SCRIPT MAIN

clear
$BootTimes = @(gc $servers | Get-LastBootTime) #create and populate array with the last boot times of the given servers.
$BootTimes = $BootTimes | Sort-Object -Property "LastBootTime" #Sort array by last boot time
$BootTimes #Prints the $bootTimes array to the console 
$BootTimes | Export-Csv $CSVPath -NoTypeInformation -Force #Export the results to CSV

In the script main the first thing which happens is to create the array $BootTimes and fill it with the data from Get-LastBootTime function. This is done by running Get-Content on the servers.txt file and then piping the server names to the Get-LastBootTime function.
After this I sort the $BootTimes array by last boot time.
Finally the $BootTimes array is exported to CSV in the script directory.
I have copied in the full script below. I hope you will find it useful.
Enjoy!!

################################################################################################
##Script:			Get-LastBootTime.ps1
##
##Description:		Gets the time of the last boot on the servers given in "servers.txt" and
#+					exports the results to a csv file in the script dir.
##Created by:		Noam Wajnman
##Creation Date:	May 21, 2013
##Updated:			April 02, 2014
################################################################################################
#FUNCTIONS
function Get-LastBootTime {
	[CmdletBinding()]
	[OutputType([System.String])]
	param(
		[Parameter(ValueFromPipeline=$true)][System.String[]]$server = $env:COMPUTERNAME
	)
	begin {
		function Test-PortAlive {
			#############################################################################################
			##Function:			Test-PortAlive
			##
			##Description:		Tests connection on a given server on a given port.
			##
			##Created by:		Noam Wajnman
			##Creation Date:	April 02, 2014	
			##############################################################################################
			[CmdletBinding()]
			[OutputType([System.boolean])]
			param(
				[Parameter(ValueFromPipeline=$true)][System.String[]]$server
			)
			$socket = new-object Net.Sockets.TcpClient
			$connect = $socket.BeginConnect($server, 135, $null, $null) #port set to 135 (RPC)
			$NoTimeOut = $connect.AsyncWaitHandle.WaitOne(500, $false) #timeout value set to 500 ms
			if ($NoTimeOut) {
				$socket.EndConnect($connect) | Out-Null
				return $true				
			}
			else {
				return $false
			}
		}
	}
	process {		
		$BootTime = $null
		$server = $($server).toUpper()	
		$alive = Test-PortAlive -server $server
		if ($alive) {
			Write-Debug "connection to $server is open on port $port"			
			$OSInfo = Get-WmiObject win32_operatingsystem -ComputerName $server #get the info with WMI
			$BootTime = $OSInfo.ConvertToDateTime($OSInfo.LastBootUpTime) #convert to datetime
			$BootTime = '{0:yyyy-MM-dd HH:mm:ss}' -f $BootTime #parse time to sortable format
			if ($BootTime) {
				Write-Debug "$server was rebooted last time at:`t$BootTime"					
				$result = "" | select "Server","LastBootTime" #creating a custom object
				$result.Server = $server
				$result.LastBootTime = $BootTime
				$result #return the $result object
			}
			else {
				Write-Debug "couldn't get boot time from server $server"
			}
		}			
		else {
			Write-Debug "Error - connection to $server is not open on port $port"			
		}
	}	
}
#VARIABLES
#$DebugPreference = "continue" #uncomment to get debug info
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath #path to the directory in which the script is run.
$servers = "$dir\Servers.txt" #last boot time will be retrieved on all the servers given in this file.
$CSVPath = "$dir\LastBootTimes.csv" #results will be exported to csv to a file with this path
#SCRIPT MAIN
clear
$BootTimes = @(gc $servers | Get-LastBootTime) #create and populate array with the last boot times of the given servers.
$BootTimes = $BootTimes | Sort-Object -Property "LastBootTime" #Sort array by last boot time
$BootTimes #Prints the $bootTimes array to the console 
$BootTimes | Export-Csv $CSVPath -NoTypeInformation -Force #Export the results to CSV
Advertisements

11 thoughts on “Powershell – Get last boot time on remote servers and export results to CSV

    • Hi David,
      If you want email the results then you can use this function to do it.

      Add this to the #FUNCTIONS section

      Function Send-Email {
      #############################################################################################
      ##Function: Send-Email
      ##
      ##Description: Sends an email. Can send with attachment if given as parameter.
      ##
      ##Created by: Noam Wajnman
      ##Creation Date: November 13, 2012
      ##############################################################################################
      param (
      [string]$Subject = $(throw “Subject parameter required.”),
      [string]$Body = $(throw “Bodytext parameter required.”),
      [string]$Senderemail = $(throw “SenderEmail Parameter required.”),
      [string]$Recipientemail = $(throw “Recipient Parameter required.”),
      [string]$Attachment = $null,
      [string]$SMTP #Your SMTP/Exchange server
      )
      $MailMessage = New-Object System.Net.Mail.MailMessage
      $SMTPClient = New-Object System.Net.Mail.smtpClient
      $SMTPClient.host = $SMTP
      $Recipient = New-Object System.Net.Mail.MailAddress($Recipientemail, “Recipient”)
      $Sender = New-Object System.Net.Mail.MailAddress($Senderemail, “Sender”)

      $MailMessage.Sender = $Sender
      $MailMessage.From = $Sender
      $MailMessage.Subject = $Subject
      $MailMessage.To.add($Recipient)
      $MailMessage.Body = $Body
      Write-Debug “Attachment is $Attachment”
      if ($Attachment) {
      Write-Debug “Entered attachment block”
      $MailMessage.attachments.add($Attachment)
      }
      $SMTPClient.Send($MailMessage)
      }

      Add this to the #VARIABLES section (and modify according to your needs)
      $Subject = “the mail subject line”
      $Body = “Body Text”
      $Senderemail = “LastBootTime@somewhere.com”
      $Recipientemail = Somebody@somewhere.com
      $SMTP = “Some_SMTP_Server”

      Add this to the end of the #SCRIPT MAIN section
      Send-Email -subject $subject -body $Body -senderemail $senderemail -recipientemail $recipientemail -SMTP $SMTP -attachment $CSVPath

      That should do the trick.

      Hope this helps! 🙂

  1. brianresume@hotmail.com

    I am seeing the Output file to the csv coming out as follows: Within PowerShell the Output shows the servers correctly.

    Server LastBootTime
    System.String[] 10/25/2014 2:56
    System.String[] 10/25/2014 2:57
    System.String[] 10/25/2014 19:50
    System.String[] 10/25/2014 19:50
    System.String[] 10/26/2014 2:56
    System.String[] 10/26/2014 2:57
    System.String[] 10/26/2014 2:58
    System.String[] 10/28/2014 11:25

    Thanks

    Brian

    • Hi Brian,
      I just tested the script and it works ok for me and I see the output in the CSV correctly. However the problem looks like the server name output in your CSV is the object type instead of the object value. You could try to edit line 56 so it looks like this:
      $result.Server = “$($server)”
      That should force the script to get the string value of the $server object.

      Let me know if that helped.

      \Noam

  2. Andrea

    Noam,

    Was looking for something like this for a while! Nice job! I am getting quite a lot of these returns however… but it is exporting some results to the .csv. Is this due to OSinfo not having the object criteria?

    You cannot call a method on a null-valued expression.
    At C:\scripts\CompleteMajor.ps1:51 char:13
    + $BootTime = $OSInfo.ConvertToDateTime($OSInfo.LastBootUpTime) #conve …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    • Hi Andrea, It looks like the WMI query isn’t returning anything on the servers where you see the error. It could be a connectivity or permissions issue. I recommend that you look into this.

    • You’ll need to change the recipient email parameter to an array kind of like this:
      $recepientemails = @(‘someEmail1@somewhere.com’,’someEmail2@somewhere.com’)

      Then at the end you need to wrap the send-email function with a foreach loop kind of like this:
      foreach ($recipientemail in $recipientemails) {
      Send-Email -subject $subject -body $Body -senderemail $senderemail -recipientemail
      $recipientemail -SMTP $SMTP -attachment $CSVPath
      }

      then it will send the file to each email address in the $recipientEmails array.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s