In this article, we will discuss how to get Last BIOS Time. In today’s fast-paced IT environment, ensuring that systems run efficiently is paramount. One critical aspect of this efficiency is the system boot time, often referred to as the “Last BIOS Time.”
Monitoring and managing this time can help IT professionals optimize system performance and troubleshoot issues more effectively.This blog post will delve into a PowerShell script designed to fetch the Last BIOS Time from the startup section of the task manager, alerting users if it exceeds a specified threshold.
Background
The Last BIOS Time is a metric available in the startup section of the Windows Task Manager that indicates how long it takes for the system’s BIOS to initialize during the boot process. A longer BIOS time can signify potential issues such as hardware problems or misconfigurations. For IT professionals and Managed Service Providers (MSPs), keeping track of this metric can be crucial for maintaining system health and ensuring quick startup times.
The Script:
#Requires -Version 5.1 <# .SYNOPSIS Gets the Last BIOS time from the startup section of task manager and alerts if it exceeds a threshold you specify. .DESCRIPTION Gets the Last BIOS time from the startup section of task manager and alerts if it exceeds a threshold you specify. Can save the result to a custom field. .EXAMPLE (No Parameters) ## EXAMPLE OUTPUT WITHOUT PARAMS ## Last BIOS Time: 14.6s PARAMETER: -BootCustomField "BootTime" Saves the boot time to this Text Custom Field. .EXAMPLE -BootCustomField "BootTime" ## EXAMPLE OUTPUT WITH BootCustomField ## Last BIOS Time: 14.6s PARAMETER: -Seconds 20 Sets the threshold for when the boot time is greater than this number. In this case the boot time is over the threshold. .EXAMPLE -Seconds 20 ## EXAMPLE OUTPUT WITH Seconds ## Last BIOS Time: 14.6s [Error] Boot time exceeded threshold of 20s by 5.41s. Boot time: 14.6s PARAMETER: -Seconds 10 Sets the threshold for when the boot time is greater than this number. In this case the boot time is under the threshold. .EXAMPLE -Seconds 10 ## EXAMPLE OUTPUT WITH Seconds ## Last BIOS Time: 14.6s [Info] Boot time under threshold of 10s by 4.59s. Boot time: 14.6s .OUTPUTS String .NOTES Minimum OS Architecture Supported: Windows 10, Windows Server 2016 Release Notes: Initial Release By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). #> [CmdletBinding()] param ( $Seconds, [String]$BootCustomField ) begin { if ($env:bootCustomField -and $env:bootCustomField -notlike "null") { $BootCustomField = $env:bootCustomField } if ($env:bootTimeThreshold -and $env:bootTimeThreshold -notlike "null") { # Remove any non digits [double]$Seconds = $env:bootTimeThreshold -replace '[^0-9.]+' } function Set-NinjaProperty { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String]$Name, [Parameter()] [String]$Type, [Parameter(Mandatory = $True, ValueFromPipeline = $True)] $Value, [Parameter()] [String]$DocumentName ) $Characters = $Value | Measure-Object -Character | Select-Object -ExpandProperty Characters if ($Characters -ge 10000) { throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded, value is greater than 10,000 characters.") } # If we're requested to set the field value for a Ninja document we'll specify it here. $DocumentationParams = @{} if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName } # This is a list of valid fields that can be set. If no type is given, it will be assumed that the input doesn't need to be changed. $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG" if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type! Please check here for valid types. https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" } # The field below requires additional information to be set $NeedsOptions = "Dropdown" if ($DocumentName) { if ($NeedsOptions -contains $Type) { # We'll redirect the error output to the success stream to make it easier to error out if nothing was found or something else went wrong. $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1 } } else { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1 } } # If an error is received it will have an exception property, the function will exit with that error information. if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions } # The below type's require values not typically given in order to be set. The below code will convert whatever we're given into a format ninjarmm-cli supports. switch ($Type) { "Checkbox" { # While it's highly likely we were given a value like "True" or a boolean datatype it's better to be safe than sorry. $NinjaValue = [System.Convert]::ToBoolean($Value) } "Date or Date Time" { # Ninjarmm-cli expects the GUID of the option to be selected. Therefore, the given value will be matched with a GUID. $Date = (Get-Date $Value).ToUniversalTime() $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date $NinjaValue = $TimeSpan.TotalSeconds } "Dropdown" { # Ninjarmm-cli is expecting the guid of the option we're trying to select. So we'll match up the value we were given with a guid. $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name" $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID if (-not $Selection) { throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown") } $NinjaValue = $Selection } default { # All the other types shouldn't require additional work on the input. $NinjaValue = $Value } } # We'll need to set the field differently depending on if its a field in a Ninja Document or not. if ($DocumentName) { $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1 } else { $CustomField = Ninja-Property-Set -Name $Name -Value $NinjaValue 2>&1 } if ($CustomField.Exception) { throw $CustomField } } } process { $Ticks = try { # Get boot time from performance event logs $PerfTicks = Get-WinEvent -FilterHashtable @{LogName = "Microsoft-Windows-Diagnostics-Performance/Operational"; Id = 100 } -MaxEvents 1 -ErrorAction SilentlyContinue | ForEach-Object { # Convert the event to XML and grab the Event node $eventXml = ([xml]$_.ToXml()).Event # Output boot time in ms [int64]($eventXml.EventData.Data | Where-Object { $_.Name -eq 'BootTime' }).InnerXml } # Get the boot POST time from the firmware, when available $FirmwareTicks = Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" -Name "FwPOSTTime" -ErrorAction SilentlyContinue # Get the boot POST time from Windows, used as fall back $OsTicks = Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" -Name "POSTTime" -ErrorAction SilentlyContinue # Use most likely to be accurate to least accurate if ($FirmwareTicks -gt 0) { $FirmwareTicks } elseif ($OsTicks -gt 0) { $OsTicks } elseif ($PerfTicks -and $PerfTicks -gt 0) { $PerfTicks } else { # Fall back to reading System event logs $StartOfBoot = Get-WinEvent -FilterHashtable @{LogName = 'System'; Id = 12 } -MaxEvents 1 | Select-Object -ExpandProperty TimeCreated $LastUpTime = Get-WmiObject Win32_OperatingSystem -ErrorAction Stop | Select-Object @{Label = 'LastBootUpTime'; Expression = { $_.ConvertToDateTime($_.LastBootUpTime) } } | Select-Object -ExpandProperty LastBootUpTime New-TimeSpan -Start $LastUpTime -End $StartOfBoot -ErrorAction Stop | Select-Object -ExpandProperty TotalMilliseconds } } catch { Write-Host "[Error] Failed to get Last BIOS Time from registry." exit 2 } $TimeSpan = [TimeSpan]::FromMilliseconds($Ticks) $BootTime = if ($TimeSpan.Days -gt 0) { "$($TimeSpan.Days)d, $($TimeSpan.Hours)h, $($TimeSpan.Minutes)m, $($TimeSpan.Seconds + [Math]::Round($TimeSpan.Milliseconds / 1000, 1))s" } elseif ($TimeSpan.Hours -gt 0) { "$($TimeSpan.Hours)h, $($TimeSpan.Minutes)m, $($TimeSpan.Seconds + [Math]::Round($TimeSpan.Milliseconds / 1000, 1))s" } elseif ($TimeSpan.Minutes -gt 0) { "$($TimeSpan.Minutes)m, $($TimeSpan.Seconds + [Math]::Round($TimeSpan.Milliseconds / 1000, 1))s" } elseif ($TimeSpan.Seconds -gt 0) { "$($TimeSpan.Seconds + [Math]::Round($TimeSpan.Milliseconds / 1000, 1))s" } else { # Fail safe output "$($TimeSpan.Days)d, $($TimeSpan.Hours)h, $($TimeSpan.Minutes)m, $($TimeSpan.Seconds + [Math]::Round($TimeSpan.Milliseconds / 1000, 1))s" } Write-Host "Last BIOS Time: $BootTime" if ($BootCustomField) { Set-NinjaProperty -Name $BootCustomField -Type Text -Value $BootTime } if ($Seconds -gt 0) { if ($TimeSpan.TotalSeconds -gt $Seconds) { Write-Host "[Error] Boot time exceeded threshold of $($Seconds)s by $($TimeSpan.TotalSeconds - $Seconds)s. Boot time: $BootTime" exit 1 } Write-Host "[Info] Boot time under threshold of $($Seconds)s by $($Seconds - $TimeSpan.TotalSeconds)s. Boot time: $BootTime" } exit 0 } end { }
Detailed Breakdown
The provided PowerShell script is designed to fetch the Last BIOS Time from a Windows system and alert the user if it exceeds a specified threshold. Additionally, it can save the result to a custom field for documentation purposes. Below is a detailed step-by-step explanation of how the script works.
Step-by-Step Explanation
1. Parameter Definition:
- The script starts by defining two parameters: $Seconds and $BootCustomField.
- $Seconds specifies the threshold for the boot time.
- $BootCustomField is a custom field where the boot time can be saved.
2. Environment Variables Check:
- The script checks for environment variables bootCustomField and bootTimeThreshold.
- If these are set, they override the script parameters.
3. Set-NinjaProperty Function:
- This function is used to set the boot time value to a specified custom field.
- It includes validation to ensure the value does not exceed character limits and handles different data types appropriately.
4. Process Block:
- The script retrieves the Last BIOS Time using several methods, prioritizing accuracy:
- Performance event logs.
- Registry values for firmware POST time.
- System event logs as a fallback.
- The retrieved time is converted into a human-readable format.
5. Output and Alerts:
- The script outputs the Last BIOS Time.
- If a custom field is specified, it sets this value using the Set-NinjaProperty function.
- If a threshold is set, it compares the boot time against this threshold and alerts if it is exceeded.
Potential Use Cases
Imagine an IT professional named Alex, who manages a fleet of company laptops. One day, Alex notices that several users are reporting unusually long startup times. By deploying this PowerShell script across the network, Alex can automatically fetch and monitor the Last BIOS Time for each system.
If any system exceeds the predefined threshold, Alex is immediately alerted and can investigate further, potentially identifying hardware issues or misconfigurations that need addressing.
Comparisons
Other methods to accomplish similar results might include using built-in Windows tools or third-party software. However, these approaches often lack the customization and automation capabilities of a PowerShell script. For example:
- Built-in Tools: Tools like Task Manager can show the Last BIOS Time but do not provide alerts or automation.
- Third-Party Software: While these can offer comprehensive monitoring, they may come at a higher cost and require additional configuration.
FAQs
Q: What if the script fails to retrieve the Last BIOS Time?
A: The script includes multiple fallback methods to ensure the Last BIOS Time is retrieved accurately. If all methods fail, it outputs an error message.
Q: Can I modify the threshold after deploying the script?
A: Yes, you can set the threshold using the -Secondsparameter or by setting the bootTimeThreshold environment variable.
Q: How do I save the Last BIOS Time to a custom field?
A: Use the -BootCustomField parameter to specify the custom field where the boot time should be saved.
Implications
Monitoring the Last BIOS Time can significantly impact IT security and performance. A prolonged BIOS time might indicate underlying issues that could affect the overall system stability and user productivity. By proactively monitoring and addressing these issues, IT professionals can maintain optimal system performance and reduce downtime.
Recommendations
- Regularly monitor the Last BIOS Time using this script to catch potential issues early.
- Set realistic thresholds based on the typical performance of your systems.
- Document any anomalies and investigate promptly to prevent long-term issues.
Final Thoughts
In conclusion, this PowerShell script offers a robust solution for monitoring the Last BIOS Time, providing critical insights into system performance. By integrating this script into your IT management practices, you can ensure that systems run smoothly and efficiently. NinjaOne offers a suite of tools that can further enhance your IT operations, providing comprehensive monitoring and automation capabilities to keep your systems in top shape.