Community Pick: Many members of our community have endorsed this article.

Windows boot event logging and monitoring with PowerShell

Raj-GTSystems Engineer
CERTIFIED EXPERT
Published:
Updated:
The article will show you how you can maintain a simple logfile of all Startup and Shutdown events on Windows servers and desktops with PowerShell. The script can be easily adapted into doing more like gracefully silencing/updating your monitoring system upon planned maintenance reboots etc.
Having read the introduction you may be wondering why anyone would need this solution.  Even more so if you are a seasoned Windows Administrator.  After all, all of this information is right there at your fingertips in the Event Logs right?  Well you are right, and in fact the scripts are actually about getting this information from the Event Log but presenting it in a more human readable manner.  Not everyone wants to go through the logs to find out why their desktop or server rebooted the night before and if you are, like me, just managing the server and not the application it gives the application owner a simple way to track these events without having to dig through the logs themselves.  If you want, you can also use these scripts to update or silence the monitoring system when a scheduled change is taking place saving the admin valuable time which is what I mainly use these scripts for in our production environment.
 

Bootlog.PNG


The solution consists of two scripts that are assigned as Startup and Shutdown scripts using Group Policy (Computer Configuration\Windows Settings\Scripts).  The scripts and the logfile should ideally be stored local to the machine to avoid creating unnecessary dependencies.  As it relies entirely on Windows Event log entries, there will be nothing else to configure on the monitored system.

Group-Policy.PNG
Since the Shutdown script, ShutdownLog.ps1 is the simplest, let me start with that one.  The logic of the script is quite simple.  The first time you run the script, it creates the logfile and updates it with the last 3 shutdown events from the System Event log.  It does that by querying for event 1074 which is logged every time a shutdown event (restart or power off) is called by the system.  If the logfile is already present, it will just update it with the latest shutdown event entry.

We are only writing some of the data to the logfile rather than dumping the whole event entry to keep things tidy.  If your monitoring system supports HTTP updates, you can also feed the extracted data after updating the logfile.  For example, the production version of the script will silence the alerts for 15 minutes if the shutdown is being called by an interactive user (as opposed to a service account) and add an entry to the monitoring system’s log against the server so the other Administrators can be aware of this event.  For the disciplined, using shutdown comments and reason codes will really come in handy here.
 
<#
.SYNOPSIS
    Log shutdown event information to $logfile

.DESCRIPTION
    When run (preferably via Group Policy at shutdown) the script will create/update $logfile with the shutdown event information from the machine. Even if you don't have the need to maintain a logfile, you can use the script to update/silence your monitoring system or even send an e-mail alert etc.

.INPUTS
    None

.OUTPUTS
    None

.LINK
    https://github.com/Raj-GT/Windows-Boot-Event-Logging

.LINK
    https://www.experts-exchange.com/articles/25559/Windows-boot-event-logging-and-monitoring-with-PowerShell.html

.NOTES
    Version:    1.1
    Author:     Nimal Raj
    Revisions:  13/07/2017      Initial draft of v1.1
#>

#Requires -Version 3.0

#--------------------------------------------------------[Variables]--------------------------------------------------------
$logfile = "C:\Logs\bootlog.csv"

#--------------------------------------------------------[Functions]--------------------------------------------------------

#--------------------------------------------------------[Execution]--------------------------------------------------------
# If logfile doesn't exist, create it and add the CSV headers
If (!(Test-Path $logfile))
    {
        New-Item -Path $logfile -ItemType File -Force
        Add-Content -Path $logfile "Time,UserName,EventType,Process,ReasonCode,Comment,Message"
    }

# Phrase the last Event 1074 (InstanceId 2147484722) entry into variables
$event = Get-EventLog -LogName System -Source User32 -InstanceId 2147484722 -Newest 1
$Time = $event.TimeGenerated.ToString()
$UserName = $event.ReplacementStrings[6]
$EventType = $event.ReplacementStrings[4]
$Process = $event.ReplacementStrings[0]
$ReasonCode = $event.ReplacementStrings[3]
$Comment = $event.ReplacementStrings[5]
$Message = $event.Message.Split("`r`n")[0]

# Add event contents to logfile
Add-content -Path $logfile "$Time,$UserName,$EventType,$Process,$ReasonCode,$Comment,$Message"
    
# Add any actions you want performed at every shutdown/restart below this line
# E.g. You can update the monitoring system, send an e-mail or do something else
# You can also re-use the shutdown event variables in your code


The second script, Startup.ps1 will again create the logfile first if it does not already exist.  It will then query the System Event log for the latest startup event entry indicated by event 6009.  So far so good but this is where things get a little more interesting.  Windows also logs an event 6008 immediately above the normal startup entry if it detects the last shutdown being unexpected.  We are going to use this entry to determine if the startup is a cleanboot (orderly shutdown and startup) or a crashboot (reboot due to a system crash or power outage) and update the logfile accordingly.  Once we have this info, we can again feed this into the monitoring system and activate alerts and alarms as we see fit.


<#
.SYNOPSIS
    Log startup event information to $logfile

.DESCRIPTION
    When run (preferably via Group Policy at startup) the script will create/update $logfile with the startup event information from the machine. The script can even identify if the last boot was a cleanboot (orderly shutdown or restart) or crashboot (unexpected shutdown or reset) which can be useful even if you don't have the need to maintain a logfile. You can use this logic to update/silence your monitoring system or even send an e-mail alert (upon detecting a crashboot) etc.

.INPUTS
    None

.OUTPUTS
    None

.LINK
    https://github.com/Raj-GT/Windows-Boot-Event-Logging

.LINK
    https://www.experts-exchange.com/articles/25559/Windows-boot-event-logging-and-monitoring-with-PowerShell.html

.NOTES
    Version:    1.1
    Author:     Nimal Raj
    Revisions:  13/07/2017      Initial draft of v1.1
#>

#Requires -Version 3.0

#--------------------------------------------------------[Variables]--------------------------------------------------------
$logfile = "C:\Logs\bootlog.csv"

#--------------------------------------------------------[Functions]--------------------------------------------------------

#--------------------------------------------------------[Execution]--------------------------------------------------------
# If logfile doesn't exist, create it and add the CSV headers
If (!(Test-Path $logfile))
    {
        New-Item -Path $logfile -ItemType File -Force
        Add-Content -Path $logfile "Time,UserName,EventType,Process,ReasonCode,Comment,Message"
    }

# Grab the last Event 6009 entry (InstanceId 2147489657)
$cleanboot = Get-EventLog -LogName System -Source EventLog -InstanceId 2147489657 -Newest 1

# Look for Event 6008 (InstanceId 2147489656) immediately above/below
$crashboot = Get-EventLog -LogName System -Source EventLog -InstanceId 2147489656 -After $cleanboot.TimeGenerated.AddSeconds(-3) -Before $cleanboot.TimeGenerated.AddSeconds(3) -ErrorAction SilentlyContinue

# If $crashboot is empty then assume it is a cleanboot
If (!($crashboot))
    {
    $Time = $cleanboot.TimeGenerated.ToString()
    $UserName = $cleanboot.UserName
    $EventType = "cleanboot"
    $Process = $cleanboot.Source
    $ReasonCode = ""
    $Comment = ""
    $Message = $cleanboot.Message
    
    # Perform any other actions you want for a cleanboot

    }
    else
    {
    $Time = $crashboot.TimeGenerated.ToString()
    $UserName = $crashboot.UserName
    $EventType = "crashboot"
    $Process = $crashboot.Source
    $ReasonCode = ""
    $Comment = ""
    $Message = $crashboot.Message
    
    # Perform any other actions you want for a crashboot
    
    }

#Add event contents to logfile
Add-content -Path $logfile "$Time,$UserName,$EventType,$Process,$ReasonCode,$Comment,$Message"

# Add any actions you want performed at every startup below this line
# E.g. You can update the monitoring system, send an e-mail or do something else
# You can also re-use the startup event variables in your code


Logging boot events to file and updating the monitoring system is just one example.  You can use the boot state detection logic to do something else, like send an e-mail or launch some other script etc. depending on your needs.

I hope you enjoyed my little article and find the scripts useful.  I would be very interested in knowing your own uses of this script and if you think it can be improved further.  This is my first ever article for Experts Exchange and I am hoping to improve my technical writing with the help of your feedback so please keep them coming.

Please don't forget to vote if you like the article and would like to see more in the future.
23
32,152 Views
Raj-GTSystems Engineer
CERTIFIED EXPERT

Comments (5)

CERTIFIED EXPERT

Commented:
Nice and informative article Raj.
Thanks for sharing.
Voted as Good Article :)
Raj-GTSystems Engineer
CERTIFIED EXPERT

Author

Commented:
Thanks for the vote Yashwant.
Steve LingisSAP RF ITS Integration & MobiControl Specialist

Commented:
Scripts work fine, and are very useful.   Thanks.  But I am not following how either script queries the correct event-id(s).   Thanks Again!
Raj-GTSystems Engineer
CERTIFIED EXPERT

Author

Commented:
Hi Steve,

Thank you for the comment. I am using the InstanceIDs that correspond to the EventIDs I mentioned in the article as it is somewhat faster to execute.
Get-EventLog -LogName System -Source User32 -Newest 10 | ? { $_.EventID -eq "1074"}

Open in new window


Get-EventLog -LogName System -Source User32 -Newest 10 -InstanceId 2147484722

Open in new window


Both commands above will produce the same result.

Thanks,
Raj-GT
Raj-GTSystems Engineer
CERTIFIED EXPERT

Author

Commented:
An updated version of the scripts are now on GitHub at https://github.com/Raj-GT/Windows-Boot-Event-Logging

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.