hp-bios-bruteforce/HP/Manage-HPBiosSettings.ps1

513 lines
18 KiB
PowerShell

<#
.DESCRIPTION
Automatically configure HP BIOS settings
SetBIOSSetting Return Codes
0 - Success
1 - Not Supported
2 - Unspecified Error
3 - Timeout
4 - Failed - (Check for typos in the setting value)
5 - Invalid Parameter
6 - Access Denied - (Check that the BIOS password is correct)
.PARAMETER GetSettings
Instruct the script to get a list of current BIOS settings
.PARAMETER SetSettings
Instruct the script to set BIOS settings
.PARAMETER CsvPath
The path to the CSV file to be imported or exported
.PARAMETER SetupPassword
The current BIOS password
.PARAMETER LogFile
Specify the name of the log file along with the full path where it will be stored. The file must have a .log extension. During a task sequence the path will always be set to _SMSTSLogPath
.EXAMPLE
#Set BIOS settings supplied in the script
Manage-HPBiosSettings.ps1 -SetSettings -SetupPassword ExamplePassword
#Set BIOS settings supplied in a CSV file
Manage-HPBiosSettings.ps1 -SetSettings -CsvPath C:\Temp\Settings.csv -SetupPassword ExamplePassword
#Output a list of current BIOS settings to the screen
Manage-HPBiosSettings.ps1 -GetSettings
#Output a list of current BIOS settings to a CSV file
Manage-HPBiosSettings.ps1 -GetSettings -CsvPath C:\Temp\Settings.csv
.NOTES
Created by: Jon Anderson (@ConfigJon)
Reference: https://www.configjon.com/hp-bios-settings-management/
Modified: 2020-09-17
.CHANGELOG
2019-11-04 - Added additional logging. Changed the default log path to $ENV:ProgramData\BiosScripts\HP.
2020-02-21 - Added the ability to get a list of current BIOS settings on a system via the GetSettings parameter
Added the ability to read settings from or write settings to a csv file with the CsvPath parameter
Added the SetSettings parameter to indicate that the script should attempt to set settings
Changed the $Settings array in the script to be comma seperated instead of semi-colon seperated
Updated formatting
2020-09-14 - Added a LogFile parameter. Changed the default log path in full Windows to $ENV:ProgramData\ConfigJonScripts\HP.
Consolidated duplicate code into new functions (Stop-Script, Get-WmiData). Made a number of minor formatting and syntax changes
2020-09-17 - Improved the log file path configuration
#>
#Parameters ===================================================================================================================
param(
[Parameter(Mandatory=$false)][Switch]$GetSettings,
[Parameter(Mandatory=$false)][Switch]$SetSettings,
[Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][String]$SetupPassword,
[ValidateScript({
if($_ -notmatch "(\.csv)")
{
throw "The specified file must be a .csv file"
}
return $true
})]
[System.IO.FileInfo]$CsvPath,
[Parameter(Mandatory=$false)][ValidateScript({
if($_ -notmatch "(\.log)")
{
throw "The file specified in the LogFile paramter must be a .log file"
}
return $true
})]
[System.IO.FileInfo]$LogFile = "$ENV:ProgramData\ConfigJonScripts\HP\Manage-HPBiosSettings.log"
)
#List of settings to be configured ============================================================================================
#==============================================================================================================================
$Settings = (
"Deep S3,Off",
"Deep Sleep,Off",
"S4/S5 Max Power Savings,Disable",
"S5 Maximum Power Savings,Disable",
"Fingerprint Device,Disable",
"Num Lock State at Power-On,Off",
"NumLock on at boot,Disable",
"Numlock state at boot,Off",
"Prompt for Admin password on F9 (Boot Menu),Enable",
"Prompt for Admin password on F11 (System Recovery),Enable",
"Prompt for Admin password on F12 (Network Boot),Enable",
"PXE Internal IPV4 NIC boot,Enable",
"PXE Internal IPV6 NIC boot,Enable",
"PXE Internal NIC boot,Enable",
"Wake On LAN,Boot to Hard Drive",
"Swap Fn and Ctrl (Keys),Disable"
)
#==============================================================================================================================
#==============================================================================================================================
#Functions ====================================================================================================================
Function Get-TaskSequenceStatus
{
#Determine if a task sequence is currently running
try
{
$TSEnv = New-Object -ComObject Microsoft.SMS.TSEnvironment
}
catch{}
if($NULL -eq $TSEnv)
{
return $False
}
else
{
try
{
$SMSTSType = $TSEnv.Value("_SMSTSType")
}
catch{}
if($NULL -eq $SMSTSType)
{
return $False
}
else
{
return $True
}
}
}
Function Stop-Script
{
#Write an error to the log file and terminate the script
param(
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String]$ErrorMessage,
[Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][String]$Exception
)
Write-LogEntry -Value $ErrorMessage -Severity 3
if($Exception)
{
Write-LogEntry -Value "Exception Message: $Exception" -Severity 3
}
throw $ErrorMessage
}
Function Get-WmiData
{
#Gets WMI data using either the WMI or CIM cmdlets and stores the data in a variable
param(
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String]$Namespace,
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String]$ClassName,
[Parameter(Mandatory=$true)][ValidateSet('CIM','WMI')]$CmdletType,
[Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][String[]]$Select
)
try
{
if($CmdletType -eq "CIM")
{
if($Select)
{
Write-LogEntry -Value "Get the $Classname WMI class from the $Namespace namespace and select properties: $Select" -Severity 1
$Query = Get-CimInstance -Namespace $Namespace -ClassName $ClassName -ErrorAction Stop | Select-Object $Select -ErrorAction Stop
}
else
{
Write-LogEntry -Value "Get the $ClassName WMI class from the $Namespace namespace" -Severity 1
$Query = Get-CimInstance -Namespace $Namespace -ClassName $ClassName -ErrorAction Stop
}
}
elseif($CmdletType -eq "WMI")
{
if($Select)
{
Write-LogEntry -Value "Get the $Classname WMI class from the $Namespace namespace and select properties: $Select" -Severity 1
$Query = Get-WmiObject -Namespace $Namespace -Class $ClassName -ErrorAction Stop | Select-Object $Select -ErrorAction Stop
}
else
{
Write-LogEntry -Value "Get the $ClassName WMI class from the $Namespace namespace" -Severity 1
$Query = Get-WmiObject -Namespace $Namespace -Class $ClassName -ErrorAction Stop
}
}
}
catch
{
if($Select)
{
Stop-Script -ErrorMessage "An error occurred while attempting to get the $Select properties from the $Classname WMI class in the $Namespace namespace" -Exception $PSItem.Exception.Message
}
else
{
Stop-Script -ErrorMessage "An error occurred while connecting to the $Classname WMI class in the $Namespace namespace" -Exception $PSItem.Exception.Message
}
}
Write-LogEntry -Value "Successfully connected to the $ClassName WMI class" -Severity 1
return $Query
}
Function Set-HPBiosSetting
{
#Set a specific HP BIOS setting
param(
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String]$Name,
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][String]$Value,
[Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][String]$Password
)
#Ensure the specified setting exists and get the possible values
$CurrentSetting = $SettingList | Where-Object Name -eq $Name | Select-Object -ExpandProperty Value
if($NULL -ne $CurrentSetting)
{
#Split the current values
$CurrentSettingSplit = $CurrentSetting.Split(',')
#Find the currently set value
$Count = 0
while($Count -lt $CurrentSettingSplit.Count)
{
if($CurrentSettingSplit[$Count].StartsWith('*'))
{
$CurrentValue = $CurrentSettingSplit[$Count]
break
}
else
{
$Count++
}
}
#Setting is already set to specified value
if($CurrentValue.Substring(1) -eq $Value)
{
Write-LogEntry -Value "Setting ""$Name"" is already set to ""$Value""" -Severity 1
$Script:AlreadySet++
}
#Setting is not set to specified value
else
{
if(!([String]::IsNullOrEmpty($Password)))
{
$SettingResult = ($Interface.SetBIOSSetting($Name,$Value,"<utf-16/>" + $Password)).Return
}
else
{
$SettingResult = ($Interface.SetBIOSSetting($Name,$Value)).Return
}
if($SettingResult -eq 0)
{
Write-LogEntry -Value "Successfully set ""$Name"" to ""$Value""" -Severity 1
$Script:SuccessSet++
}
else
{
Write-LogEntry -Value "Failed to set ""$Name"" to ""$Value"". Return code $SettingResult" -Severity 3
$Script:FailSet++
}
}
}
#Setting not found
else
{
Write-LogEntry -Value "Setting ""$Name"" not found" -Severity 2
$Script:NotFound++
}
}
Function Write-LogEntry
{
#Write data to a CMTrace compatible log file. (Credit to SCConfigMgr - https://www.scconfigmgr.com/)
param(
[parameter(Mandatory = $true, HelpMessage = "Value added to the log file.")]
[ValidateNotNullOrEmpty()]
[string]$Value,
[parameter(Mandatory = $true, HelpMessage = "Severity for the log entry. 1 for Informational, 2 for Warning and 3 for Error.")]
[ValidateNotNullOrEmpty()]
[ValidateSet("1", "2", "3")]
[string]$Severity,
[parameter(Mandatory = $false, HelpMessage = "Name of the log file that the entry will written to.")]
[ValidateNotNullOrEmpty()]
[string]$FileName = ($script:LogFile | Split-Path -Leaf)
)
#Determine log file location
$LogFilePath = Join-Path -Path $LogsDirectory -ChildPath $FileName
#Construct time stamp for log entry
if(-not(Test-Path -Path 'variable:global:TimezoneBias'))
{
[string]$global:TimezoneBias = [System.TimeZoneInfo]::Local.GetUtcOffset((Get-Date)).TotalMinutes
if($TimezoneBias -match "^-")
{
$TimezoneBias = $TimezoneBias.Replace('-', '+')
}
else
{
$TimezoneBias = '-' + $TimezoneBias
}
}
$Time = -join @((Get-Date -Format "HH:mm:ss.fff"), $TimezoneBias)
#Construct date for log entry
$Date = (Get-Date -Format "MM-dd-yyyy")
#Construct context for log entry
$Context = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)
#Construct final log entry
$LogText = "<![LOG[$($Value)]LOG]!><time=""$($Time)"" date=""$($Date)"" component=""Manage-HPBiosSettings"" context=""$($Context)"" type=""$($Severity)"" thread=""$($PID)"" file="""">"
#Add value to log file
try
{
Out-File -InputObject $LogText -Append -NoClobber -Encoding Default -FilePath $LogFilePath -ErrorAction Stop
}
catch [System.Exception]
{
Write-Warning -Message "Unable to append log entry to $FileName file. Error message at line $($_.InvocationInfo.ScriptLineNumber): $($_.Exception.Message)"
}
}
#Main program =================================================================================================================
#Configure Logging and task sequence variables
if(Get-TaskSequenceStatus)
{
$TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment
$LogsDirectory = $TSEnv.Value("_SMSTSLogPath")
}
else
{
$LogsDirectory = ($LogFile | Split-Path)
if([string]::IsNullOrEmpty($LogsDirectory))
{
$LogsDirectory = $PSScriptRoot
}
else
{
if(!(Test-Path -PathType Container $LogsDirectory))
{
try
{
New-Item -Path $LogsDirectory -ItemType "Directory" -Force -ErrorAction Stop | Out-Null
}
catch
{
throw "Failed to create the log file directory: $LogsDirectory. Exception Message: $($PSItem.Exception.Message)"
}
}
}
}
Write-Output "Log path set to $LogFile"
Write-LogEntry -Value "START - HP BIOS settings management script" -Severity 1
#Connect to the HP_BIOSEnumeration WMI class
$SettingList = Get-WmiData -Namespace root\hp\InstrumentedBIOS -ClassName HP_BIOSEnumeration -CmdletType WMI
#Connect to the HP_BIOSSettingInterface WMI class
$Interface = Get-WmiData -Namespace root\hp\InstrumentedBIOS -ClassName HP_BIOSSettingInterface -CmdletType WMI
#Connect to the HP_BIOSSetting WMI class
$HPBiosSetting = Get-WmiData -Namespace root\hp\InstrumentedBIOS -ClassName HP_BIOSSetting -CmdletType WMI
#Parameter validation
Write-LogEntry -Value "Begin parameter validation" -Severity 1
if($GetSettings -and $SetSettings)
{
Stop-Script -ErrorMessage "Cannot specify the GetSettings and SetSettings parameters at the same time"
}
if(!($GetSettings -or $SetSettings))
{
Stop-Script -ErrorMessage "One of the GetSettings or SetSettings parameters must be specified when running this script"
}
if($SetSettings -and !($Settings -or $CsvPath))
{
Stop-Script -ErrorMessage "Settings must be specified using either the Settings variable in the script or the CsvPath parameter"
}
Write-LogEntry -Value "Parameter validation completed" -Severity 1
#Set counters to 0
if($SetSettings)
{
$AlreadySet = 0
$SuccessSet = 0
$FailSet = 0
$NotFound = 0
}
#Get the current password status
if($SetSettings)
{
Write-LogEntry -Value "Check current BIOS setup password status" -Severity 1
$PasswordCheck = ($HPBiosSetting | Where-Object Name -eq "Setup Password").IsSet
if($PasswordCheck -eq 1)
{
#Setup password set but parameter not specified
if([String]::IsNullOrEmpty($SetupPassword))
{
Stop-Script -ErrorMessage "The BIOS setup password is set, but no password was supplied. Use the SetupPassword parameter when a password is set"
}
#Setup password set correctly
if(($Interface.SetBIOSSetting("Setup Password","<utf-16/>" + $SetupPassword,"<utf-16/>" + $SetupPassword)).Return -eq 0)
{
Write-LogEntry -Value "The specified setup password matches the currently set password" -Severity 1
}
#Setup password not set correctly
else
{
Stop-Script -ErrorMessage "The specified setup password does not match the currently set password"
}
}
else
{
Write-LogEntry -Value "The BIOS setup password is not currently set" -Severity 1
}
}
#Get the current settings
if($GetSettings)
{
$SettingList = $SettingList | Select-Object Name,Value | Sort-Object Name
$SettingObject = ForEach($Setting in $SettingList){
#Split the current values
$SettingSplit = ($Setting.Value).Split(',')
#Find the currently set value
$SplitCount = 0
while($SplitCount -lt $SettingSplit.Count)
{
if($SettingSplit[$SplitCount].StartsWith('*'))
{
$SetValue = ($SettingSplit[$SplitCount]).Substring(1)
break
}
else
{
$SplitCount++
}
}
[PSCustomObject]@{
Name = $Setting.Name
Value = $SetValue
}
}
if($CsvPath)
{
$SettingObject | Export-Csv -Path $CsvPath -NoTypeInformation
(Get-Content $CsvPath) | ForEach-Object {$_ -Replace '"',""} | Out-File $CsvPath -Force -Encoding ascii
}
else
{
Write-Output $SettingObject
}
}
#Set settings
if($SetSettings)
{
if($CsvPath)
{
Clear-Variable Settings -ErrorAction SilentlyContinue
$Settings = Import-Csv -Path $CsvPath
}
#Set HP BIOS settings - password is set
if($PasswordCheck -eq 1)
{
if($CsvPath)
{
ForEach($Setting in $Settings){
Set-HPBiosSetting -Name $Setting.Name -Value $Setting.Value -Password $SetupPassword
}
}
else
{
ForEach($Setting in $Settings){
$Data = $Setting.Split(',')
Set-HPBiosSetting -Name $Data[0].Trim() -Value $Data[1].Trim() -Password $SetupPassword
}
}
}
#Set HP BIOS settings - password is not set
else
{
if($CsvPath)
{
ForEach($Setting in $Settings){
Set-HPBiosSetting -Name $Setting.Name -Value $Setting.Value
}
}
else
{
ForEach($Setting in $Settings){
$Data = $Setting.Split(',')
Set-HPBiosSetting -Name $Data[0].Trim() -Value $Data[1].Trim()
}
}
}
}
#Display results
if($SetSettings)
{
Write-Output "$AlreadySet settings already set correctly"
Write-LogEntry -Value "$AlreadySet settings already set correctly" -Severity 1
Write-Output "$SuccessSet settings successfully set"
Write-LogEntry -Value "$SuccessSet settings successfully set" -Severity 1
Write-Output "$FailSet settings failed to set"
Write-LogEntry -Value "$FailSet settings failed to set" -Severity 3
Write-Output "$NotFound settings not found"
Write-LogEntry -Value "$NotFound settings not found" -Severity 2
}
Write-Output "HP BIOS settings Management completed. Check the log file for more information"
Write-LogEntry -Value "END - HP BIOS settings management script" -Severity 1