Being able to determine when the last time a computer was restarted is a task that every support person needs to pull off at one time or another. Windows has several methods in place for finding restart information, but most of these solutions are difficult to use when querying multiple computers and don’t provide historical information. In this article, we’ll review the options available and then look at how to get the information you need via PowerShell.
There are several ways to get the last restart time of a computer. The options available for querying the “uptime” or last start time of a computer are decent, but they can be hard to find and don’t allow much flexibility. If you need to review uptime for many computers, the methods built into Windows aren’t designed to report information for several computers or for computers that could be in different locations.
The SystemInfo utility built into Windows displays the last restart time in an easy to digest format, but only after filtering the data:
Systeminfo | find "System Boot Time"
System Boot Time: 12/17/2019, 9:46:08 PM
For those more comfortable with a programmatic solution, we can pull the last restart time from WMI, but the output is hard to understand:
PS C:\> Wmic os get lastbootuptime
LastBootUpTime
20191217214608.500331-300
Restart information can also be found using the Net Statistics command, looking at stats on your NIC, and getting the value of “Up Time” from Task Manager. If you are looking for restart information for a one-off request, one of these options might be all that you need. But what if you need this information from a remote computer? What about multiple computers in your network? These options have limitations and can be difficult to use in scripts or across multiple machines.
One overlooked spot for restart information is the Windows Event Logs. Microsoft writes a wealth of information to the system event log about different events related to shut-down and restart operations. The events and their corresponding ID’s keep track and document all power up, power down and uptime situations related to runtime and power cycling. The trick is to know what to look for and how to turn the data into something useful and easy to work with.
There are five event IDs that are reserved for power up, power down and uptime events.
Event ID | Description |
1074 | System has been shutdown by a process/user |
6005 | The Event log service was started |
6006 | The Event log service was stopped |
6008 | The previous system shutdown at time on date was unexpected |
6013 | The system uptime is number seconds |
Event ID 1704 documents shut down events. Event ID’s 6006, 6008 and 6013 document events related to a power cycle and may or may not be useful depending on your particular situation. Pairing the 6000 events with 1074 gives a picture of how long restart operations took to complete. 6008 is important for recognizing when a computer may have blue screened or lost power unexpectedly. 6013 is not related to power cycle events, instead, it documents how long a computer has been running since the last restart.
We can build a query of this information using some simple PowerShell code. Let’s start with the 1074 events and see what that looks like.
PS C:\> Get-WinEvent -FilterHashtable @{logname = 'System'; id = 1074} | Format-Table -wrap
With one line of code, we can quickly pull all the restart info from the logs and see a clear picture of when the PC has restarted. Keep in mind that this log is from a newly built PC with only a few log entries. The PowerShell command returns ALL matching entries in the event log. If the PC being queried is a year or two old, the list of events returned can be lengthy. Use the -MaxEvents parameter to slim down the list of events.
PS C:\> Get-WinEvent -FilterHashtable @{logname = 'System'; id = 1074} -MaxEvents 1 | Format-Table -wrap
If we want to get the events from multiple PCs, then it’s just one more line of code:
PS C:\> $computers = "Server1","Server2"
PS C:\> $computers | ForEach-Object {Get-WinEvent -ComputerName $_ -FilterHashtable @{logname = 'System'; id = 1074} -MaxEvents 2 | format-table -wrap}
Now, let’s add in those other event IDs and see what that looks like for just one computer. You can choose whichever events you want. I am using 1074, 6005, 6006, and 6008 in this example.
PS C:\> Get-WinEvent -FilterHashtable @{logname = 'System'; id = 1074, 6005, 6006, 6008} -MaxEvents 6 | Format-Table -wrap
The results of the PowerShell query show different events that occurred at different times with enough detail to understand what happened for each event. However, the results are grouped by the ProviderName. The results become more confusing if we pull results from multiple computers. It would be easier to understand what occurred on a machine and when it happened if the events where displayed in sequential date order rather than grouped by ProviderName.
To do this, we must write the events to a new object so we can bypass the grouping of events by ProviderName. It sounds difficult, but it’s not once you see it in action. This is a good time to switch from trying to write one or two lines of code and instead saving the code as a script so it can so it can be a repeatable action.
You can see in the section of code above that we are creating a new object called $EventData which contains the fields Date, EventID, User, Action, Reason, ReasonCode, Comment, Computer, Message, Process. Those fields are the same names as the fields in the event log data.
Once the object is created, we need to map the event ID data fields from the event log to the $EventData fields. All this does is tell PowerShell where to write the data in our new object when it reads a log entry from the event log.
It may not be obvious what is going on in the following fields:
$EventData.User = $_.Properties
[6].Value
$EventData.Process
= $_.Properties[0].Value
$EventData.
Action
= $_.Properties[4].Value
$EventData.
Reason
= $_.Properties[2].Value
$EventData.
ReasonCode
= $_.Properties[3].Value
$EventData.
Comment
= $_.Properties[5].Value
These fields hold the data from our 1074 events. If you look at one of the earlier examples, you’ll notice there is a message field and within the message data there are extra fields of data called Reason, Reason Code, Shutdown Type and Comment. That data is saved in an array, which is nothing more than a table with a bunch of rows. To pull the data out, we need to separate all those rows into different fields in our $EventData object we created.
The numbers in brackets after the word properties represents the row number in the table. How did I figure that out? The information is in the event log, it’s just not obvious at first glance. Notice the fields in the screenshot below named Param1 through Param7. I mentioned earlier that the data is contained in an array. The rows in an array each have a number ofr an “index”. The data in the array can be accessed if what number row the data is in. The index in an array always starts with zero, so the array index for our data is 0-6 and that index represents Param1 through Param7.
If you’re having any issues following along, I have saved all the code as a function called Get-RestartInfo which you can download from my GitHub repo. The function uses the same code in this article plus a few extra bits to provide help information. To run the function, you load it into memory and then call the function like this:
The output in now listed in sequential order by date. Those extra event id’s help tell a story about what happened and how long restarts took to complete. This is also a good time to mention that sometimes there is too much data to show in a PowerShell console. When those situations come about, you can use Out-Gridview to get a better view of your data.
The same command above with Out-GridView looks like this:
PS C:\> Get-RestartInfo | Out-Gridview
The results look like this:
Using PowerShell to get eventlog data is useful when you need to see more than just the most recent restart information. What you have seen here is how to query the event logs using one line of code to retrieve the restart information for a computer. We then built on that single line of code by getting the restart information from more than one computer. To query the multiple computers only required one additional line of code. We then made our own script to better display the output of the results.
By saving our code as a script, we’re able to re-run the query and not have to retype all the syntax. You can make your own version of this script and tailor the output exactly how you like or download and edit my code.
Get our latest blog posts delivered in a weekly email.