HP.ClientManagement.psm1

#
# Copyright 2018-2022 HP Development Company, L.P.
# All Rights Reserved.
#
# NOTICE: All information contained herein is, and remains the property of HP Development Company, L.P.
#
# The intellectual and technical concepts contained herein are proprietary to HP Development Company, L.P
# and may be covered by U.S. and Foreign Patents, patents in process, and are protected by
# trade secret or copyright law. Dissemination of this information or reproduction of this material
# is strictly forbidden unless prior written permission is obtained from HP Development Company, L.P.


Add-Type -AssemblyName System.Web
Set-StrictMode -Version 3.0
#requires -Modules "HP.Private"

Add-Type -TypeDefinition @'
  public enum BiosUpdateCriticality
  {
    Recommended=0,
    Critical=1
  }
'@


<#
.SYNOPSIS
  Retrieve an HP BIOS Setting object by name
 
.DESCRIPTION
  Read an HP-specific BIOS setting, identified by the specified name.
 
.PARAMETER Name
  The name of the setting to retrieve. This parameter is mandatory, and has no default.
 
.PARAMETER Format
  This parameter allows to specify the formatting of the result. Possible values are:
 
    * BCU: format as HP BIOS Config Utility input format
    * CSV: format as a comma-separated values list
    * XML: format as XML
    * JSON: format as JSON
 
  If not specified, the default PowerShell formatting is used.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.NOTES
  Required HP BIOS.
 
.EXAMPLE
  Get-HPBIOSSetting -Name "Serial Number" -Format BCU
#>

function Get-HPBIOSSetting {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetting")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $true)]
    $Name,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $false)]
    [ValidateSet('XML','JSON','BCU','CSV')]
    $Format,
    [Parameter(ParameterSetName = 'NewSession',Position = 2,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 3,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $ns = getNamespace
  Write-Verbose "Reading HP BIOS Setting '$Name' from $ns on '$ComputerName'"
  $result = $null

  $params = @{
    Class = "HP_BIOSSetting"
    Namespace = $ns
    Filter = "Name='$name'"
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') {
    $params.CimSession = newCimSession -Target $ComputerName
  }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') {
    $params.CimSession = $CimSession
  }

  try {
    $result = Get-CimInstance @params -ErrorAction stop
  } catch [Microsoft.Management.Infrastructure.CimException]
  {
    if ($_.Exception.Message.trim() -eq "Access denied")
    {
      throw [System.UnauthorizedAccessException]"Access denied: Please ensure you have the rights to perform this operation."
    }
    throw [System.NotSupportedException]"$($_.Exception.Message): Please ensure this is a supported HP device."
  }


  if (-not $result) {
    $Err = "Setting not found: '" + $name + "'"
    throw [System.Management.Automation.ItemNotFoundException]$Err
  }
  Add-Member -InputObject $result -Force -NotePropertyName "Class" -NotePropertyValue $result.CimClass.CimClassName | Out-Null
  Write-Verbose "Retrieved HP BIOS Setting '$name' ok."

  switch ($format) {
    { $_ -eq 'CSV' } { return convertSettingToCSV ($result) }
    { $_ -eq 'XML' } { return convertSettingToXML ($result) }
    { $_ -eq 'BCU' } { return convertSettingToBCU ($result) }
    { $_ -eq 'JSON' } { return convertSettingToJSON ($result) }
    default { return $result }
  }
}


<#
 .SYNOPSIS
  Get device UUID via standard OS providers
 
.DESCRIPTION
  This function gets the system UUID via standard OS providers. This should normally match the result from Get-HPBIOSUUID.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPDeviceUUID
#>

function Get-HPDeviceUUID () {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceUUID")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystemProduct'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "UUID")).trim().ToUpper()
}


<#
 .SYNOPSIS
  Get BIOS UUID from the BIOS
 
.DESCRIPTION
  This function gets the system UUID from the BIOS. This should normally match the result from Get-HPDeviceUUID.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPBIOSUUID
#>

function Get-HPBIOSUUID {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSUUID")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $params = @{ Name = 'Universally Unique Identifier (UUID)' }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params -ErrorAction stop
  if ($obj.Value -match '-') {
    return (getFormattedBiosSettingValue $obj)
  }
  else {
    $raw = ([guid]::new($obj.Value)).ToByteArray()
    $raw[0],$raw[3] = $raw[3],$raw[0]
    $raw[1],$raw[2] = $raw[2],$raw[1]
    $raw[4],$raw[5] = $raw[5],$raw[4]
    $raw[6],$raw[7] = $raw[7],$raw[6]
    return ([guid]::new($raw)).ToString().ToUpper().trim()
  }
}


<#
.SYNOPSIS
  Get the current BIOS version
 
.DESCRIPTION
  This function gets the current BIOS version. If available, and the -includeFamily switch is specified, the BIOS family is also included.
 
.PARAMETER IncludeFamily
  Include BIOS family in the result
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPBIOSVersion
#>

function Get-HPBIOSVersion {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSVersion")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $false)]
    [switch]$IncludeFamily,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(Position = 1,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  $verfield = getWmiField $obj "SMBIOSBIOSVersion"
  $ver = $null

  Write-Verbose "Received object with $verfield"
  try {
    $ver = extractBIOSVersion $verfield
  }
  catch { throw [System.InvalidOperationException]"The BIOS version on this system could not be parsed. This BIOS may not be supported." }
  if ($includeFamily.IsPresent) { $result = $ver + " " + $verfield.Split()[0] }
  else { $result = $ver }
  $result.TrimStart("0").trim()
}

<#
.SYNOPSIS
  Get the BIOS author (manufacturer)
 
.DESCRIPTION
  This function gets the BIOS manufacturer via the Win32_BIOS WMI class. In some cases, the BIOS manufacturer may be different than the device manufacturer.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPBIOSAuthor
#>

function Get-HPBIOSAuthor {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSAuthor")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "Manufacturer")).trim()

}

<#
.SYNOPSIS
  Get the Device manufacturer
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.DESCRIPTION
  This function gets the device manufacturer via standard Windows WMI providers. In some cases, the BIOS manufacturer may be different than the device manufacturer.
 
.EXAMPLE
  Get-HPDeviceManufacturer
#>

function Get-HPDeviceManufacturer {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceManufacturer")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystem'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "Manufacturer")).trim()
}

<#
.SYNOPSIS
  Get the device serial number
 
.DESCRIPTION
  Get the system serial number via Windows WMI. This command is equivalent to reading the SerialNumber property in the Win32_BIOS WMI class.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPDeviceSerialNumber
#>

function Get-HPDeviceSerialNumber {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceSerialNumber")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop

  ([string](getWmiField $obj "SerialNumber")).trim()
}

<#
.SYNOPSIS
  Get the device model string, which is the marketing name of the device.
 
.DESCRIPTION
  Get the device model string, which is the marketing name of the device.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPDeviceModel
#>

function Get-HPDeviceModel {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceModel")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystem'
    Namespace = 'root\cimv2'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop

  ([string](getWmiField $obj "Model")).trim()
}




<#
.SYNOPSIS
  Get the device PartNumber (or SKU)
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.DESCRIPTION
  Get the device part number for the current device. This function is equivalent to reading the field SystemSKUNumber from the WMI class Win32_ComputerSystem.
 
.EXAMPLE
  Get-HPDevicePartNumber
#>

function Get-HPDevicePartNumber {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDevicePartNumber")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystem'
    Namespace = 'root\cimv2'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop

  ([string](getWmiField $obj "SystemSKUNumber")).trim().ToUpper()
}


<#
.SYNOPSIS
  Get the product ID
 
.DESCRIPTION
  This product ID (Platform ID) is a 4-character hexadecimal string. It corresponds to the Product field in the Win32_BaseBoard WMI class.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPDeviceProductID
#>

function Get-HPDeviceProductID {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceProductID")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_BaseBoard'
    Namespace = 'root\cimv2'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "Product")).trim().ToUpper()
}


<#
.SYNOPSIS
  Get the device asset tag
 
.DESCRIPTION
  Retrieves the asset tag for a device (also called the Asset Tracking Number).
  Some computers may have a blank asset tag, others may have the asset tag pre-populated with the serial number value.
 
.PARAMETER ComputerName
  Alias -Target. Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPDeviceAssetTag
#>

function Get-HPDeviceAssetTag {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceAssetTag")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $params = @{
    Name = 'Asset Tracking Number'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params -ErrorAction stop
  getFormattedBiosSettingValue $obj
}


<#
.SYNOPSIS
  Get the value of a BIOS setting.
 
.DESCRIPTION
  This function retrieves the value of a BIOS setting. Whereas the Get-HPBIOSSetting retrieves all setting fields, Get-HPBIOSSettingValue retrieves only the setting's value.
 
.NOTES
  Requires HP BIOS.
 
.PARAMETER name
  The name of the setting to retrieve
 
.PARAMETER ComputerName
  Alias -Target. Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPBIOSSettingValue -Name 'Asset Tracking Number'
#>

function Get-HPBIOSSettingValue {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSettingValue")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $true)]
    [string]$Name,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $false)]
    [CimSession]$CimSession
  )
  $params = @{
    Name = $Name
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params
  if ($obj) {
    getFormattedBiosSettingValue $obj
  }


}


<#
.SYNOPSIS
  Retrieve all BIOS settings
 
.DESCRIPTION
  Retrieve all BIOS settings on a machine, either as native objects, or as a specified format.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER Format
  This parameter allows to specify the formatting of the result. Possible values are:
 
    * BCU: format as HP BIOS Config Utility input format
    * CSV: format as a comma-separated values list
    * XML: format as XML
    * JSON: format as JSON
    * brief: (default) format as a list of names
 
.PARAMETER NoReadonly
  When true, don't include read-only settings into the response. Default is false.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Get-HPBIOSSettingsList -Format BCU
 
.NOTES
  - Although the function supports BCU, note that redirecting the function's output to a file will not be usable by BCU, because PowerShell will insert a unicode BOM in the file. To obtain a compatible file, either remove the BOM manually or consider using bios-cli.ps1.
  - BIOS settings of type 'password' are not output when using XML, JSON, BCU, or CSV formats.
  - By convention, when representing multiple values in an enumeration as a single string, the value with an asterisk in front is the currently active value. For example, given the string "One,*Two,Three" representing three possible enumeration choices, the current active value is "Two".
  - Requires HP BIOS.
#>

function Get-HPBIOSSettingsList {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSettingsList")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $false)]
    [Parameter(Position = 0,Mandatory = $false)]
    [ValidateSet('XML','JSON','BCU','CSV','brief')]
    [string]$Format,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $false)]
    [Parameter(Position = 1,Mandatory = $false)] [switch]$NoReadonly,
    [Parameter(ParameterSetName = 'NewSession',Position = 2,Mandatory = $false)]
    [Alias('Target')]
    [Parameter(Position = 2,Mandatory = $false)] [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 3,Mandatory = $false)]
    [Parameter(Position = 3,Mandatory = $false)] [CimSession]$CimSession
  )
  $ns = getNamespace

  Write-Verbose "Getting all BIOS settings from '$ComputerName'"
  $params = @{
    ClassName = 'HP_BIOSSetting'
    Namespace = $ns
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  try {
    $cs = Get-CimInstance @params -ErrorAction stop
  }
  catch [Microsoft.Management.Infrastructure.CimException]{
    if ($_.Exception.Message.trim() -eq "Access denied")
    {
      throw [System.UnauthorizedAccessException]"Access denied: Please ensure you have the rights to perform this operation."
    }
    throw [System.NotSupportedException]"$($_.Exception.Message): Please ensure this is a supported HP device."
  }

  switch ($format) {
    { $_ -eq 'BCU' } {
      # to BCU format
      $now = Get-Date
      Write-Output "BIOSConfig 1.0"
      Write-Output ";"
      Write-Output "; Created by CMSL function Get-HPBIOSSettingsList"
      Write-Output "; Date=$now"
      Write-Output ";"
      Write-Output "; Found $($cs.count) settings"
      Write-Output ";"
      foreach ($c in $cs) {
        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            convertSettingToBCU ($c)
          }
        }
      }
      return
    }

    { $_ -eq 'XML' } {
      # to IA format
      Write-Output "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes"" ?>"
      Write-Output "<ImagePal>"
      Write-Output " <BIOSSettings>"

      foreach ($c in $cs) {
        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            convertSettingToXML ($c)
          }
        }
      }
      Write-Output " </BIOSSettings>"
      Write-Output "</ImagePal>"
      return
    }

    { $_ -eq 'JSON' } {
      # to JSON format
      $first = $true
      "[" | Write-Output


      foreach ($c in $cs) {
        Add-Member -InputObject $c -Force -NotePropertyName "Class" -NotePropertyValue $c.CimClass.CimClassName | Out-Null

        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            if ($first -ne $true) {
              Write-Output ","
            }
            convertSettingToJSON ($c)
            $first = $false
          }
        }

      }
      "]" | Write-Output

    }

    { $_ -eq 'CSV' } {
      # to CSV format
      Write-Output ("NAME,CURRENT_VALUE,READONLY,TYPE,PHYSICAL_PRESENCE_REQUIRED,MIN,MAX,");
      foreach ($c in $cs) {
        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            convertSettingToCSV ($c)
          }
        }
      }
      return
    }
    { $_ -eq 'brief' } {
      foreach ($c in $cs) {
        if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
          Write-Output $c.Name
        }
      }
      return
    }
    default {
      if (-not $noreadonly.IsPresent) {
        return $cs
      }
      else {
        return $cs | Where-Object IsReadOnly -EQ 0
      }
    }
  }
}


<#
.SYNOPSIS
  This is a private function for internal use only
 
.DESCRIPTION
  This is a private function for internal use only
 
.EXAMPLE
 
.NOTES
  - This is a private function for internal use only
#>

function Set-HPPrivateBIOSSettingValuePayload {
  param(
    [Parameter(ParameterSetName = 'Payload',Position = 0,Mandatory = $true,ValueFromPipeline = $true)]
    [string]$Payload
  )

  $portable = $Payload | ConvertFrom-Json

  if ($portable.purpose -ne "hp:sureadmin:biossetting") {
    throw "The payload should be generated by New-HPSureAdminBIOSSettingValuePayload function"
  }

  [SureAdminSetting]$setting = [System.Text.Encoding]::UTF8.GetString($portable.Data) | ConvertFrom-Json

  Set-HPPrivateBIOSSetting -Setting $setting
}

<#
.SYNOPSIS
  Set the value of a BIOS setting
 
.DESCRIPTION
  This function sets the value of an HP BIOS setting. Note that the setting may have various constraints restricting the input that can be provided.
 
.PARAMETER Name
  The name of a setting. Note that the setting name is usually case sensitive.
 
.PARAMETER Value
  The new value of a setting
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER Password
  The setup password, if a password is active
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.PARAMETER SkipPrecheck
  Skip reading the setting value from the BIOS, before applying it. This is useful as an optimization when the setting is guaranteed to exist on the system, or when preparing an HP Sure Admin platform for a remote platform which may contain settings not present on the local platform.
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - By convention, when representing multiple values in an enumeration as a single string, the value with an asterisk in front is the currently active value. For example, given the string "One,*Two,Three" representing three possible enumeration choices, the current active value is "Two".
 
.EXAMPLE
  Set-HPBIOSSettingValue -Name "Asset Tracking Number" -Value "Hello World" -password 's3cr3t'
#>

function Set-HPBIOSSettingValue {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSettingValue")]
  param(
    [Parameter(ParameterSetName = "NewSession",Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 0,Mandatory = $false)]
    [AllowEmptyString()]
    [string]$Password,

    [Parameter(ParameterSetName = "NewSession",Position = 1,Mandatory = $true)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 1,Mandatory = $true)]
    [string]$Name,

    [Parameter(ParameterSetName = "NewSession",Position = 2,Mandatory = $true)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 2,Mandatory = $true)]
    [AllowEmptyString()]
    [string]$Value,

    [Parameter(ParameterSetName = "NewSession",Position = 3,Mandatory = $false)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 3,Mandatory = $false)]
    [switch]$SkipPrecheck,

    [Parameter(ParameterSetName = 'NewSession',Position = 4,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",

    [Parameter(ParameterSetName = 'ReuseSession',Position = 4,Mandatory = $true)]
    [CimSession]$CimSession
  )

  [SureAdminSetting]$setting = New-Object -TypeName SureAdminSetting
  $setting.Name = $Name
  $setting.Value = $Value

  $params = @{
    Setting = $setting
    Password = $Password
    CimSession = $CimSession
    ComputerName = $ComputerName
    SkipPrecheck = $SkipPrecheck
  }
  Set-HPPrivateBIOSSetting @params
}

<#
.SYNOPSIS
  Check if the BIOS Setup password is set
 
.DESCRIPTION
  This function returns $true if a BIOS password is currently active, or $false otherwise.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.NOTES
  Requires HP BIOS.
 
.EXAMPLE
  Get-HPBIOSSetupPasswordIsSet
 
.LINK
  [Set-HPBIOSSetupPassword](Set-HPBIOSSetupPassword)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](Get-HPBIOSSetupPasswordIsSet)
#>

function Get-HPBIOSSetupPasswordIsSet () {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetupPasswordIsSet")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession

  )
  $params = @{ Name = "Setup Password" }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params
  return [boolean]$obj.IsSet
}

<#
.SYNOPSIS
  Set the BIOS Setup password
 
.DESCRIPTION
  Set the BIOS Setup password to the specific value. The password must comply with the current active security policy.
 
.PARAMETER NewPassword
  The new password to set. A value is required. To clear the password, use Clear-HPBIOSSetupPassword
 
.PARAMETER Password
  The existing setup password, if any. If there is no password set, this parameter may be omitted. Use Get-HPBIOSSetupPasswordIsSet to determine if a password is currently set.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Set-HPBIOSSetupPassword -NewPassword 'newpw' -Password 'oldpw'
 
.LINK
  [Clear-HPBIOSSetupPassword](Clear-HPBIOSSetupPassword)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](Get-HPBIOSSetupPasswordIsSet)
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - Multiple attempts to change the password with an incorrect existing password may trigger BIOS lockout mode, which can be cleared by rebooting the system.
#>

function Set-HPBIOSSetupPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/set-HPBIOSSetupPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $true)]
    [string]$NewPassword,

    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 1,Mandatory = $false)]
    [string]$Password,


    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 2,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",

    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 3,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{}
  $settingName = 'Setup Password'


  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params

  $r = $iface | Invoke-CimMethod -ErrorAction Stop -MethodName 'SetBIOSSetting' -Arguments @{
    Name = $settingName
    Password = '<utf-16/>' + $Password
    Value = '<utf-16/>' + $newPassword
  }

  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw [System.InvalidOperationException]$Err
  }
}

<#
.SYNOPSIS
  Clear the BIOS Setup password
 
.DESCRIPTION
  This function clears the BIOS setup password. To set the password, use Set-HPBIOSSetupPassword
 
.PARAMETER Password
  The existing setup password. Use Get-HPBIOSSetupPasswordIsSet to determine if a password is currently set.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Clear-HPBIOSSetupPassword -Password 'oldpw'
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - Multiple attempts to change the password with an incorrect existing password may trigger BIOS lockout mode, which can be cleared by rebooting the system.
 
.LINK
  [Set-HPBIOSSetupPassword](Set-HPBIOSSetupPassword)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](Get-HPBIOSSetupPasswordIsSet)
#>

function Clear-HPBIOSSetupPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSSetupPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $true)]
    [string]$Password,

    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $settingName = 'Setup Password'


  $params = @{}
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetBiosSetting -Arguments @{ Name = "Setup Password"; Value = "<utf-16/>"; Password = "<utf-16/>" + $Password; }
  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw [System.InvalidOperationException]$Err
  }
}


<#
.SYNOPSIS
  Check if the BIOS Power-On password is set
 
.DESCRIPTION
  This function returns $true if a BIOS power-on password is currently active, or $false otherwise.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.NOTES
  Changes in the state of the BIOS Power-On Password may not be visible until the system is rebooted and the POST prompt is accepted to enable the BIOS Power-On password.
 
.EXAMPLE
  Get-HPBIOSPowerOnPasswordIsSet
 
.LINK
  [Set-HPBIOSPowerOnPassword](Set-HPBIOSPowerOnPassword)
 
.LINK
  [Clear-HPBIOSPowerOnPassword](Clear-HPBIOSPowerOnPassword)
#>

function Get-HPBIOSPowerOnPasswordIsSet () {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSPowerOnPasswordIsSet")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession

  )
  $params = @{ Name = "Power-On Password" }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params
  return [boolean]$obj.IsSet
}

<#
.SYNOPSIS
  Set the BIOS Power-On password
 
.DESCRIPTION
  This function clears any active power-on password. The Password must comply with password complexity requirements active on the system.
 
.PARAMETER NewPassword
  The password to set. A value is required. To clear the password, use Clear-HPBIOSPowerOnPassword
 
.PARAMETER Password
  The existing setup password (not power-on password), if any. If there is no setup password set, this parameter may be omitted. Use Get-HPBIOSSetupPasswordIsSet to determine if a setup password is currently set.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.NOTES
  Changes in the state of the BIOS Power-On Password may not be visible until the system is rebooted and the POST prompt is accepted to enable the BIOS Power-On password.
 
.EXAMPLE
  Set-HPBIOSPowerOnPassword -NewPassword 'newpw' -Password 'setuppw'
 
.LINK
  [Clear-HPBIOSPowerOnPassword](Clear-HPBIOSPowerOnPassword)
 
.LINK
  [Get-HPBIOSPowerOnPasswordIsSet](Get-HPBIOSPower\OnPasswordIsSet)
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - On many platform families, changing the Power-On password requires that a BIOS password is active.
 
#>

function Set-HPBIOSPowerOnPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSPowerOnPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $true)]
    [string]$NewPassword,
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 1,Mandatory = $false)]
    [string]$Password,

    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 3,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 4,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $settingName = 'Power-On Password'

  $params = @{}
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetBiosSetting -Arguments @{ Name = $settingName; Value = "<utf-16/>" + $newPassword; Password = "<utf-16/>" + $Password; }
  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw $Err
  }
}

<#
.SYNOPSIS
  Clear the BIOS Power-On password
 
.DESCRIPTION
  This function clears any active power-on password.
 
.PARAMETER Password
  The existing setup (not power-on) password. Use Get-HPBIOSSetupPasswordIsSet to determine if a password is currently set. See important note regarding the BIOS Setup Password prerequisite below.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Clear-HPBIOSPowerOnPassword -Password 's3cr3t'
 
.LINK
  [Set-HPBIOSPowerOnPassword](Set-HPBIOSPowerOnPassword)
 
.LINK
  [Get-HPBIOSPowerOnPasswordIsSet](Get-HPBIOSPowerOnPasswordIsSet)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](Get-HPBIOSSetupPasswordIsSet)
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - On many platform families, changing the Power-On password requires that a BIOS password is active.
  - If BIOS Setup Password is not set, it's required to be first set in order to clear the Power-On password.
 
#>

function Clear-HPBIOSPowerOnPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSPowerOnPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $false)]
    [string]$Password,


    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $settingName = 'Power-On Password'


  $params = @{}
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetBiosSetting -Arguments @{
    Name = "Power-On Password"
    Value = "<utf-16/>"
    Password = ("<utf-16/>" + $Password)
  }
  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw [System.InvalidOperationException]$Err
  }
}

<#
.SYNOPSIS
  Set one or more BIOS settings from a file
 
.DESCRIPTION
  This function sets multiple BIOS settings from a file. The file format may be specified via the -format parameter, however the function will try to infer the format from the file extension.
 
.PARAMETER File
  The settings file (relative or absolute path) to process
  - Note that BIOS passwords are not encrypted in this file, so it is essential to protect its content until applied to the target system.
 
.PARAMETER Format
  The file format (XML, JSON, CSV, or BCU).
 
.PARAMETER Password
  The current BIOS setup password, if any.
 
.PARAMETER NoSummary
  Suppress the one line summary at the end of the import
 
.PARAMETER ErrorHandling
  This value is used by wrapping scripts to prevent this function from raising exceptions or warnings.
    0 - operate normally
    1 - raise exceptions as warnings
    2 - no warnings or exceptions, fail silently
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Set-HPBIOSSettingValuesFromFile -File .\file.bcu -NoSummary
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
#>

function Set-HPBIOSSettingValuesFromFile {
  [CmdletBinding(DefaultParameterSetName = "NotPassThruNewSession",
    HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSettingValuesFromFile")]
  param(
    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 0,Mandatory = $true)]
    [System.IO.FileInfo]$File,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 1,Mandatory = $false)]
    [ValidateSet('XML','JSON','BCU','CSV')]
    [string]$Format = $null,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 2,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 2,Mandatory = $false)]
    [string]$Password,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 3,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 3,Mandatory = $false)]
    [switch]$NoSummary,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 4,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 5,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 5,Mandatory = $false)]
    $ErrorHandling = 2,

    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 6,Mandatory = $true)]
    [CimSession]$CimSession
  )

  if (-not $Format) {
    $Format = (Split-Path -Path $File -Leaf).Split(".")[1].ToLower()
    Write-Verbose "Format from file extension: $Format"
  }

  Write-Verbose "Format specified: '$Format'. Reading file..."
  [System.Collections.Generic.List[SureAdminSetting]]$settingsList = Get-HPPrivateSettingsFromFile -FileName $File -Format $Format

  $params = @{
    SettingsList = $settingsList
    ErrorHandling = $ErrorHandling
    ComputerName = $ComputerName
    CimSession = $CimSession
    Password = $Password
    NoSummary = $NoSummary
  }
  Set-HPPrivateBIOSSettingsList @params -Verbose:$VerbosePreference
}

<#
.SYNOPSIS
  Reset BIOS settings to shipping defaults
 
.DESCRIPTION
  Reset BIOS to shipping defaults. The actual defaults are platform specific.
 
.PARAMETER Password
  The current BIOS setup password, if any.
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  Set-HPBIOSSettingDefaults -Password 's3cr3t'
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
 
#>

function Set-HPBIOSSettingDefaults {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSettingDefaults")]
  param(
    [Parameter(ParameterSetName = "NewSession",Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 0,Mandatory = $false)]
    [AllowEmptyString()]
    [string]$Password,

    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",

    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $authorization = "<utf-16/>" + $Password
  Set-HPPrivateBIOSSettingDefaultsAuthorization -ComputerName $ComputerName -CimSession $CimSession -Authorization $authorization -Verbose:$VerbosePreference
}

function Set-HPPrivateBIOSSettingDefaultsAuthorization {
  param(
    [string]$Authorization,
    [string]$ComputerName,
    [CimSession]$CimSession
  )

  Write-Verbose "Calling SetSystemDefaults() on $ComputerName"
  $params = @{}
  if ($CimSession) {
    $params.CimSession = $CimSession
  }
  else {
    $params.CimSession = newCimSession -Target $ComputerName
  }
  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetSystemDefaults -Arguments @{ Password = $Authorization; }

  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw $Err
  }
}


<#
.SYNOPSIS
  This is a private function for internal use only
 
.DESCRIPTION
  This is a private function for internal use only
 
.EXAMPLE
 
.NOTES
  - This is a private function for internal use only
#>

function Set-HPPrivateBIOSSettingDefaultsPayload {
  param(
    [Parameter(ParameterSetName = 'Payload',Position = 0,Mandatory = $true,ValueFromPipeline = $true)]
    [string]$Payload
  )

  $portable = $Payload | ConvertFrom-Json

  if ($portable.purpose -ne "hp:sureadmin:resetsettings") {
    throw "The payload should be generated by New-HPSureAdminSettingDefaultsPayload function"
  }

  [SureAdminSetting]$setting = [System.Text.Encoding]::UTF8.GetString($portable.Data) | ConvertFrom-Json

  Set-HPPrivateBIOSSettingDefaultsAuthorization -Authorization $setting.AuthString
}

<#
.SYNOPSIS
  Get system uptime
 
.DESCRIPTION
  Get the system boot time and uptime
 
.PARAMETER ComputerName
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER CimSession
  A pre-established CIM Session (as created by [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) cmdlet). Use this to pass a preconfigured session object to optimize remote connections or specify the connection protocol (Wsman or DCOM). If not specified, the function will create its own one-time use CIM Session object, and default to DCOM protocol.
 
.EXAMPLE
  (Get-HPDeviceUptime).BootTime
 
#>

function Get-HPDeviceUptime {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceUptime")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_OperatingSystem'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $result = Get-CimInstance @params -ErrorAction stop
  $resultobject = @{}
  $resultobject.BootTime = $result.LastBootUpTime

  $span = (Get-Date) - ($resultobject.BootTime)
  $resultobject.Uptime = "$($span.days) days, $($span.hours) hours, $($span.minutes) minutes, $($span.seconds) seconds"
  $resultobject
}



<#
.SYNOPSIS
    Get current boot mode and uptime
 
.DESCRIPTION
  Returns an object containing system uptime, last boot time, whether secure boot is enabled, and whether the system was booted in UEFI or Legacy mode.
 
 
.EXAMPLE
    $IsUefi = (Get-HPDeviceBootInformation).Mode -eq "UEFI"
 
#>

function Get-HPDeviceBootInformation {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceBootInformation")]
  param()

  $mode = @{}

  try {
    $sb = Confirm-SecureBootUEFI
    $mode.Mode = "UEFI"
    $mode.SecureBoot = $sb
  }
  catch {
    $mode.Mode = "Legacy"
    $mode.SecureBoot = $false
  }

  try {
    $uptime = Get-HPDeviceUptime
    $mode.Uptime = $uptime.Uptime
    $mode.BootTime = $uptime.BootTime
  }
  catch {
    $mode.Uptime = "N/A"
    $mode.BootTime = "N/A"
  }

  $mode
}



<#
.SYNOPSIS
  Check and apply available BIOS updates (or downgrades)
 
.DESCRIPTION
  This function uses an internet service to retrieve the list of BIOS updates available for a platform, and optionally checks it against the current system.
 
  The result is a series of records, with the following definition:
 
    * Ver - the BIOS update version
    * Date - the BIOS release date
    * Bin - the BIOS update binary file
 
  Online Mode uses Seamless Firmware Update Service that can update the BIOS in the background while the operating system is running (no authentication needed). 2022 and newer HP computers with Intel processors support Seamless Firmware Update Service.
  Offline Mode updates the BIOS after reboot and this mode requires authentication (password or payload).
 
.PARAMETER Platform
  The Platform ID to check. It can be obtained via Get-HPDeviceProductID. The Platform ID cannot be specified for a flash operation. If not specified, current Platform ID is checked.
 
.PARAMETER Target
  Execute the command on specified target computer. If not specified, the command is executed on the local computer.
 
.PARAMETER Format
  The file format (XML, JSON, CSV, list) to output. If not specified, a list of PowerShell objects is returned.
 
.PARAMETER Latest
  If specified, only return or download the latest available BIOS version between remote and local. If -Platform is specified, local BIOS will not be read and the latest BIOS version available remotely will be returned.
 
.PARAMETER Check
  If specified, return true if the latest version corresponds to the installed version or installed version is higher, false otherwise. This check is only valid when comparing against current platform.
 
.PARAMETER All
  Include all known BIOS update information. This may include additional data such as dependencies, rollback support, and criticality.
 
.PARAMETER Download
  Download the BIOS file to the current directory or a path specified by SaveAs.
 
.PARAMETER Flash
  Apply the BIOS update to the current system.
 
.PARAMETER Password
  Specify the BIOS password, if a password is active. This switch is only used when -flash is specified.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
 
.PARAMETER Version
  The BIOS version to download. If not specified, the latest version available will be downloaded.
 
.PARAMETER SaveAs
  The filename for the downloaded BIOS file. If not specified, the remote file name will be used.
 
.PARAMETER Quiet
  Do not display a progress bar during BIOS file download.
 
.PARAMETER Overwrite
  Force overwriting any existing file with the same name during BIOS file download. This switch is only used when -download is specified.
 
.PARAMETER Yes
  Answer 'yes' to the 'Are you sure you want to flash' prompt. To prevent flashing the BIOS accidentally it is recommended to not specify Yes by default.
 
.PARAMETER Force
  Force the BIOS to update even if the target BIOS is already installed.
 
.PARAMETER BitLocker
  Provide an answer to the BitLocker check prompt (if any). The value may be one of:
    stop - (default option) stop if BitLocker is detected but not suspended, and prompt
    ignore - skip the BitLocker check
    suspend - suspend BitLocker if active, and continue
 
.PARAMETER Url
  Alternate Url source to provide platform's BIOS update catalog (xml)
 
.PARAMETER Offline
  This parameter selects the offline mode to flash the BIOS instead of the default online mode. If specified the actual flash will only occur after reboot at pre-OS environment. This mode is selected by default when downgrading the BIOS version and requires authentication so either a Password or a PayloadFile should be specified.
 
.PARAMETER NoWait
  If specified, the script does not wait for the online flash background task to finish. If the user reboots the PC during the online flash it will complete only after reboot.
 
.NOTES
  - Flash is only supported on Windows 10 1709 (Fall Creators Updated) and later.
  - UEFI boot mode is required for flashing; legacy mode is not supported.
  - The flash operation requires 64-bit PowerShell (not supported under 32-bit PowerShell).
 
  **WinPE notes**
 
  - Use '-BitLocker ignore' when using this function in WinPE, as BitLocker checks are not applicable in Windows PE.
  - Requires that the WInPE image is built with the WinPE-SecureBootCmdlets.cab component.
 
.EXAMPLE
  Get-HPBIOSUpdates
#>

function Get-HPBIOSUpdates {

  [CmdletBinding(DefaultParameterSetName = "ViewSet",
    HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSUpdates")]
  param(
    [Parameter(ParameterSetName = "DownloadSet",Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = "ViewSet",Position = 0,Mandatory = $false)]
    [Parameter(Position = 0,Mandatory = $false)]
    [ValidatePattern("^[a-fA-F0-9]{4}$")]
    [string]$Platform,

    [ValidateSet('XML','JSON','CSV','List')]
    [Parameter(ParameterSetName = "ViewSet",Position = 1,Mandatory = $false)]
    [string]$Format,

    [Parameter(ParameterSetName = "ViewSet",Position = 2,Mandatory = $false)]
    [switch]$Latest,

    [Parameter(ParameterSetName = "CheckSet",Position = 3,Mandatory = $false)]
    [switch]$Check,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 4,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 4,Mandatory = $false)]
    [Parameter(ParameterSetName = "ViewSet",Position = 4,Mandatory = $false)]
    [string]$Target = ".",

    [Parameter(ParameterSetName = "ViewSet",Position = 5,Mandatory = $false)]
    [switch]$All,

    [Parameter(ParameterSetName = "DownloadSet",Position = 6,Mandatory = $true)]
    [switch]$Download,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 7,Mandatory = $true)]
    [switch]$Flash,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 8,Mandatory = $false)]
    [string]$Password,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 9,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 9,Mandatory = $false)]
    [string]$Version,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 10,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 10,Mandatory = $false)]
    [string]$SaveAs,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 11,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 11,Mandatory = $false)]
    [switch]$Quiet,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 12,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 12,Mandatory = $false)]
    [switch]$Overwrite,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 13,Mandatory = $false)]
    [switch]$Yes,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 14,Mandatory = $false)]
    [ValidateSet('Stop','Ignore','Suspend')]
    [string]$BitLocker = 'Stop',

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 15,Mandatory = $false)]
    [switch]$Force,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 16,Mandatory = $false)]
    [string]$Url = "https://ftp.hp.com/pub/pcbios",

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 17,Mandatory = $false)]
    [switch]$Offline,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 18,Mandatory = $false)]
    [switch]$NoWait
  )

  if ($PSCmdlet.ParameterSetName -eq "FlashSetPassword") {
    Test-HPFirmwareFlashSupported -CheckPlatform

    if ((Get-HPPrivateIsSureAdminEnabled) -eq $true) {
      throw "Sure Admin is enabled, you must use Update-HPFirmware with a payload instead of a password"
    }
  }

  if (-not $platform) {
    # if platform is not provided, $platform is current platform
    $platform = Get-HPDeviceProductID -Target $target
  }

  $platform = $platform.ToUpper()
  Write-Verbose "Using platform ID $platform"


  $uri = [string]"$Url/{0}/{0}.xml" -f $platform.ToUpper()
  Write-Verbose "Retrieving catalog file $uri"
  $ua = Get-HPPrivateUserAgent
  try {
    [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
    $data = Invoke-WebRequest -Uri $uri -UserAgent $ua -UseBasicParsing -ErrorAction Stop
  }
  catch [System.Net.WebException]{
    if ($_.Exception.Message.contains("(404) Not Found"))
    {
      throw [System.Management.Automation.ItemNotFoundException]"Unable to retrieve BIOS data for a platform with ID $platform (data file not found)."
    }
    throw $_.Exception
  }

  [xml]$doc = [System.IO.StreamReader]::new($data.RawContentStream).ReadToEnd()
  if ((-not $doc) -or (-not (Get-Member -InputObject $doc -Type Property -Name "BIOS")) -or (-not (Get-Member -InputObject $doc.bios -Type Property -Name "Rel")))
  {
    throw [System.FormatException]"Source data file is unsupported or corrupt"
  }

  #reach to Rel nodes to find Bin entries in xml
  #ignore any entry not ending in *.bin e.g. *.tgz, *.cab
  $unwanted_nodes = $doc.SelectNodes("//BIOS/Rel") | Where-Object { -not ($_.Bin -like "*.bin") }
  $unwanted_nodes | Where-Object {
    $ignore = $_.ParentNode.RemoveChild($_)
  }

  #trim the 0 from the start of the version and then sort on the version value
  $refined_doc = $doc.SelectNodes("//BIOS/Rel") | Select-Object -Property @{ Name = 'Ver'; expr = { $_.Ver.TrimStart("0") } },'Date','Bin','RB','L','DP' `
     | Sort-Object -Property Ver -Descending

  #latest version
  $latestVer = $refined_doc[0]

  if (($PSCmdlet.ParameterSetName -eq "ViewSet") -or ($PSCmdlet.ParameterSetName -eq "CheckSet")) {
    Write-Verbose "Proceeding with parameter set => view"
    if ($check.IsPresent -eq $true) {
      [string]$haveVer = Get-HPBIOSVersion -Target $target
      #check should return true if local BIOS is same or newer than the latest available remote BIOS.
      return ([string]$haveVer.TrimStart("0") -ge [string]$latestVer[0].Ver)
    }

    $args = @{}
    if ($all.IsPresent) {
      $args.Property = (@{ Name = 'Ver'; expr = { $_.Ver.TrimStart("0") } },"Date","Bin",`
           (@{ Name = 'RollbackAllowed'; expr = { [bool][int]$_.RB.trim() } }),`
           (@{ Name = 'Importance'; expr = { [Enum]::ToObject([BiosUpdateCriticality],[int]$_.L.trim()) } }),`
           (@{ Name = 'Dependency'; expr = { [string]$_.DP.trim() } }))
    }
    else {
      $args.Property = (@{ Name = 'Ver'; expr = { $_.Ver.TrimStart("0") } },"Date","Bin")
    }

    # for current platform: latest should return whichever is latest, between local and remote.
    # for any other platform specified: latest should return latest entry from SystemID.XML since we don't know local BIOSVersion
    if ($latest)
    {
      if ($PSBoundParameters.ContainsKey('Platform'))
      {
        # platform specified, do not read information from local system and return latest platform published
        $args.First = 1
      }
      else {
        $retrieved = 0
        # determine the local BIOS version
        [string]$haveVer = Get-HPBIOSVersion -Target $target
        # latest should return whichever is latest, between local and remote for current system.
        if ([string]$haveVer -ge [string]$latestVer[0].Ver)
        {
          # local is the latest. So, retrieve attributes other than BIOSVersion to print for latest
          for ($i = 0; $i -lt $refined_doc.Length; $i++) {
            if ($refined_doc[$i].Ver -eq $haveVer) {
              $haveVerFromDoc = $refined_doc[$i]
              $pso = [pscustomobject]@{
                Ver = $haveVerFromDoc.Ver
                Date = $haveVerFromDoc.Date
                Bin = $haveVerFromDoc.Bin
              }
              if ($all) {
                $pso | Add-Member -MemberType ScriptProperty -Name RollbackAllowed -Value { [bool][int]$haveVerFromDoc.RB.trim() }
                $pso | Add-Member -MemberType ScriptProperty -Name Importance -Value { [Enum]::ToObject([BiosUpdateCriticality],[int]$haveVerFromDoc.L.trim()) }
                $pso | Add-Member -MemberType ScriptProperty -Name Dependency -Value { [string]$haveVerFromDoc.DP.trim }
              }
              $retrieved = 1
              if ($pso) {
                formatBiosVersionsOutputList ($pso)
                return
              }
            }
          }
          if ($retrieved -eq 0) {
            Write-Verbose "Retrieving entry from XML failed, get the information from CIM class."
            # calculating date from Win32_BIOS
            $year = (Get-CimInstance Win32_BIOS).ReleaseDate.Year
            $month = (Get-CimInstance Win32_BIOS).ReleaseDate.Month
            $day = (Get-CimInstance Win32_BIOS).ReleaseDate.Day
            $date = $year.ToString() + '-' + $month.ToString() + '-' + $day.ToString()
            Write-Verbose "Date calculated from CIM Class is: $date"

            $currentVer = Get-HPBIOSVersion
            $pso = [pscustomobject]@{
              Ver = $currentVer
              Date = $date
              Bin = $null
            }
            if ($all) {
              $pso | Add-Member -MemberType ScriptProperty -Name RollbackAllowed -Value { $null }
              $pso | Add-Member -MemberType ScriptProperty -Name Importance -Value { $null }
              $pso | Add-Member -MemberType ScriptProperty -Name Dependency -Value { $null }
            }
            if ($pso) {
              $retrieved = 1
              formatBiosVersionsOutputList ($pso)
              return
            }
          }
        }
        else {
          # remote is the latest
          $args.First = 1
        }
      }
    }
    formatBiosVersionsOutputList ($refined_doc | Sort-Object -Property ver -Descending | Select-Object @args)
  }
  else {
    $download_params = @{}

    if ($version) {
      $version = $version.TrimStart('0')
      $latestVer = $refined_doc `
         | Where-Object { $_.Ver.TrimStart("0") -eq $version } `
         | Select-Object -Property Ver,Bin -First 1
    }

    if (-not $latestVer) { throw [System.ArgumentOutOfRangeException]"Version $version was not found." }

    if (($flash.IsPresent) -and (-not $saveAs)) {
      $saveAs = Get-HPPrivateTemporaryFileName -FileName $latestVer.Bin
      $download_params.NoClobber = "yes"
      Write-Verbose "Temporary file name for download is $saveAs"
    }
    else { $download_params.NoClobber = if ($overwrite.IsPresent) { "yes" } else { "no" } }

    Write-Verbose "Proceeding with parameter set => download, overwrite=$($download_params.NoClobber)"


    $remote_file = $latestVer.Bin
    $local_file = $latestVer.Bin
    $remote_ver = $latestVer.Ver

    if ($PSCmdlet.ParameterSetName -eq "FlashSetPassword" -or
      $PSCmdlet.ParameterSetName -eq "FlashSetSigningKeyFile" -or
      $PSCmdlet.ParameterSetName -eq "FlashSetSigningKeyCert") {
      $running = Get-HPBIOSVersion
      $offlineMode = $false
      if ($running.TrimStart("0").trim() -ge $remote_ver.TrimStart("0").trim()) {
        if ($Force.IsPresent) {
          $offlineMode = $true
          Write-Verbose "Offline mode selected to downgrade BIOS"
        }
        else {
          Write-Host "This system is already running BIOS version $($remote_ver.TrimStart(`"0`").Trim()) or newer."
          Write-Host -ForegroundColor Cyan "You can specify -Force on the command line to proceed anyway."
          return
        }
      }
      if (-not $offlineMode -and $Offline.IsPresent) {
        $offlineMode = $true
        Write-Verbose "Offline mode selected"
      }
    }

    if ($saveAs) {
      $local_file = $saveAs
    }

    [Environment]::CurrentDirectory = $pwd
    #if (-not [System.IO.Path]::IsPathRooted($to)) { $to = ".\$to" }

    $download_params.url = [string]"$Url/{0}/{1}" -f $platform,$remote_file
    $download_params.Target = [IO.Path]::GetFullPath($local_file)
    $download_params.progress = ($quiet.IsPresent -eq $false)
    Invoke-HPPrivateDownloadFile @download_params -panic

    if ($PSCmdlet.ParameterSetName -eq "FlashSetPassword" -or
      $PSCmdlet.ParameterSetName -eq "FlashSetSigningKeyFile" -or
      $PSCmdlet.ParameterSetName -eq "FlashSetSigningKeyCert") {
      if (-not $yes) {
        Write-Host -ForegroundColor Cyan "Are you sure you want to flash this system with version '$remote_ver'?"
        Write-Host -ForegroundColor Cyan "Current BIOS version is $(Get-HPBIOSVersion)."
        Write-Host -ForegroundColor Cyan "A reboot will be required for the operation to complete."
        $response = Read-Host -Prompt "Type 'Y' to continue and anything else to abort. Or specify -Yes on the command line to skip this prompt"
        if ($response -ne "Y") {
          Write-Verbose "User did not confirm and did not disable confirmation - aborting."
          return
        }
      }

      Write-Verbose "Passing to flash process with file $($download_params.target)"

      $update_params = @{
        file = $download_params.Target
        bitlocker = $bitlocker
        Force = $Force
        Password = $password
      }

      Update-HPFirmware @update_params -Verbose:$VerbosePreference -Offline:$offlineMode -NoWait:$NoWait
    }
  }

}

function Get-HPPrivateBIOSFamilyNameAndVersion {
  [CmdletBinding()]
  param(
  )

  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  $params.CimSession = newCimSession -Target "."
  $obj = Get-CimInstance @params -ErrorAction stop
  $verfield = (getWmiField $obj "SMBIOSBIOSVersion").Split()

  return $verfield[0],$verfield[2]
}


<#
.SYNOPSIS
  Check and apply available BIOS updates using Windows Update packages
 
.DESCRIPTION
  This function uses an internet service to get the list of BIOS capsule updates available for a platform family, and optionally install the update in the current system. The versions available through this function may differ from Get-HPBIOSUpdate since this relies on the Microsoft capsules availability. This can be delayed due to the Windows Update in-flight processes.
 
.PARAMETER Family
  The Platform Family to check. If not specified, check the current platform family.
 
.PARAMETER Severity
  If specified, returns the available BIOS for the specified severity: Latest or LatestCritical.
 
.PARAMETER Download
  Download the BIOS file to the current directory or a path specified by saveAs.
 
.PARAMETER Flash
  Apply the BIOS update to the current system.
 
.PARAMETER Version
  The BIOS version to download. If not specified, the latest version available will be downloaded.
 
.PARAMETER SaveAs
  The filename for the downloaded BIOS file. If not specified, the remote file name will be used.
  In order to use the downloaded file with Add-HPBIOSWindowsUpdateScripts the name must follow the standard: platform family (3 digit) + underscore + BIOS version (6 digits) + .cab, for instance: R70_011200.cab
 
.PARAMETER Yes
  Answer 'yes' to the 'Are you sure you want to flash' prompt. To prevent flashing the BIOS accidentally it is recommended to not specify Yes by default.
 
.PARAMETER Force
  Force the BIOS to update, even if the target BIOS is already installed.
 
.PARAMETER Url
  Alternate Url source to provide platform's BIOS update catalog (xml).
 
.PARAMETER Quiet
  Do not display a progress bar during BIOS file download.
 
.PARAMETER List
  Display a list with all BIOS versions available for the specified platform.
 
.NOTES
  - Requires Windows group policy support
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -List -Family R70
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity Latest
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity LatestCritical
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity LatestCritical -Family R70
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity LatestCritical -Family R70 -Version "01.09.00"
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity LatestCritical -Family R70 -Version "01.09.00" -SaveAs "R70_010900.cab"
#>

function Get-HPBIOSWindowsUpdate {
  [CmdletBinding(DefaultParameterSetName = "Severity",HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSWindowsUpdate")]
  param(
    [Parameter(Mandatory = $false,Position = 0,ParameterSetName = "Severity")]
    [ValidateSet('Latest','LatestCritical')]
    [string]$Severity = 'Latest',

    [Parameter(Mandatory = $true,Position = 0,ParameterSetName = "Specific")]
    [string]$Version,

    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "Specific")]
    [Parameter(Mandatory = $false,Position = 0,ParameterSetName = "List")]
    [string]$Family,

    [Parameter(Mandatory = $false,Position = 2,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 2,ParameterSetName = "Specific")]
    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "List")]
    [string]$Url = "https://hpia.hpcloud.hp.com/downloads/capsule",

    [Parameter(Mandatory = $false,Position = 3,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 3,ParameterSetName = "Specific")]
    [switch]$Quiet,

    [Parameter(Mandatory = $false,Position = 4,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 4,ParameterSetName = "Specific")]
    [string]$SaveAs,

    [Parameter(Mandatory = $false,Position = 5,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 5,ParameterSetName = "Specific")]
    [switch]$Download,

    [Parameter(Mandatory = $false,Position = 6,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 6,ParameterSetName = "Specific")]
    [switch]$Flash,

    [Parameter(Mandatory = $false,Position = 7,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 7,ParameterSetName = "Specific")]
    [switch]$Yes,

    [Parameter(Mandatory = $false,Position = 8,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 8,ParameterSetName = "Specific")]
    [switch]$Force,

    [Parameter(Mandatory = $true,Position = 2,ParameterSetName = "List")]
    [switch]$List
  )

  if ($Family -and -not $Version) {
    $_,$biosVersion = Get-HPPrivateBIOSFamilyNameAndVersion
    $biosFamily = $Family
  }
  elseif (-not $Family -and $Version) {
    $biosFamily,$_ = Get-HPPrivateBIOSFamilyNameAndVersion
    $biosVersion = $Version
  }
  elseif (-not $Version -and -not $Family) {
    $biosFamily,$biosVersion = Get-HPPrivateBIOSFamilyNameAndVersion
  } else {
    $biosFamily = $Family
    $biosVersion = $Version
  }

  [string]$uri = [string]"$Url/{0}/{0}.json" -f $biosFamily.ToUpper()
  Write-Verbose "Retrieving $biosFamily catalog $uri"
  Write-Verbose "BIOS Version: $biosVersion"

  $ua = Get-HPPrivateUserAgent
  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  try {
    $data = Invoke-WebRequest -Uri $uri -UserAgent $ua -UseBasicParsing -ErrorAction Stop
  }
  catch {
    Write-Verbose $_.Exception
    Write-Host "Platform $biosFamily is not supported yet"
    throw [System.Management.Automation.ItemNotFoundException]"Unable to retrieve the BIOS update catalog for platform family $biosFamily."
  }

  $doc = [System.IO.StreamReader]::new($data.RawContentStream).ReadToEnd() | ConvertFrom-Json

  if ($List.IsPresent) {
    $data = $doc | Sort-Object -Property biosVersion -Descending
    return $data | Format-Table -Property biosFamily,biosVersion,severity,isLatest,IsLatestCritical
  }

  if ($PSCmdlet.ParameterSetName -eq "Specific") {
    $filter = $doc | Where-Object { $_.BiosVersion -eq $biosVersion } # specific
    Write-Verbose "Locating a specific version"
    if ($null -eq $filter) {
      throw "The version specified is not available on the $biosFamily catalog"
    }
  }
  elseif ($Severity -eq "LatestCritical") {
    $filter = $doc | Where-Object { $_.isLatestCritical -eq $true } # latest critical
    Write-Verbose "Locating the latest critical version available"
  }
  else {
    $filter = $doc | Where-Object { $_.isLatest -eq $true } # latest
    Write-Verbose "Locating the latest version available"
  }

  $sort = $filter | Sort-Object -Property biosVersion -Descending
  @{
    Family = $sort[0].biosFamily
    Version = $sort[0].BiosVersion
  }

  if ($Flash.IsPresent) {
    $running = Get-HPBIOSVersion
    if (-not $Yes.IsPresent) {
      Write-Host -ForegroundColor Cyan "Are you sure you want to flash this system with version '$($sort[0].biosVersion)'?"
      Write-Host -ForegroundColor Cyan "Current BIOS version is $running."
      Write-Host -ForegroundColor Cyan "A reboot will be required for the operation to complete."
      $response = Read-Host -Prompt "Type 'Y' to continue and anything else to abort. Or specify -Yes on the command line to skip this prompt"
      if ($response -ne "Y") {
        Write-Verbose "User did not confirm and did not disable confirmation - aborting."
        return
      }
    }
    if ((-not $Force.IsPresent) -and $running.TrimStart("0").trim() -ge $sort[0].BiosVersion.TrimStart("0").trim()) {
      Write-Host "This system is already running BIOS version $($sort[0].biosVersion) or newer."
      Write-Host -ForegroundColor Cyan "You can specify -Force on the command line to proceed anyway."
      return
    }
  }

  if ($Download.IsPresent -or $Flash.IsPresent) {
    Write-Verbose "Download from $($sort[0].url)"
    if ($SaveAs) {
      $localFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs)
    } else {
      $extension = ($sort[0].url -split '\.')[-1]
      $SaveAs = Get-HPPrivateTemporaryFileName -FileName "$($sort[0].biosFamily)_$($sort[0].biosVersion -Replace '\.').$extension"
      $localFile = [IO.Path]::GetFullPath($SaveAs)
    }
    Write-Verbose "LocalFile: $localFile"

    $download_params = @{
      NoClobber = "yes"
      url = $sort[0].url
      Target = $localFile
      progress = ($Quiet.IsPresent -eq $false)
    }
    try {
      Invoke-HPPrivateDownloadFile @download_params
    }
    catch {
      Write-Verbose $_.Exception
      throw [System.Management.Automation.ItemNotFoundException]"Unable to download the BIOS update archive from $($download_params.url)."
    }
    Write-Host "Saved as $localFile"

    $hash = (Get-FileHash $localFile -Algorithm SHA1).Hash
    $bytes = [byte[]] -split ($hash -replace '..','0x$& ')
    $base64 = [System.Convert]::ToBase64String($bytes)
    if ($base64 -eq $sort[0].digest) {
      Write-Verbose "Integrity check passed"
    }
    else {
      throw "Cab file integrity check failed"
    }
  }

  if ($Flash.IsPresent) {
    Add-HPBIOSWindowsUpdateScripts -WindowsUpdateFile $localFile
  }
}

function Get-HPPrivatePSScriptsEntries {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $false,Position = 0)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $types = '[Logon]','[Logoff]','[Startup]','[Shutdown]'
  $cmdLinesSet = @{}
  $parametersSet = @{}

  if ([System.IO.File]::Exists($Path)) {
    $contents = Get-Content $Path
    if ($contents) {
      for ($i = 0; $i -lt $contents.Length; $i++) {
        if ($types.contains($contents[$i])) {
          $t = $contents[$i]
          $cmdLinesSet[$t] = [System.Collections.ArrayList]@()
          $parametersSet[$t] = [System.Collections.ArrayList]@()
          continue
        }
        if ($contents[$i].Length -gt 0) {
          $cmdLinesSet[$t].Add($contents[$i].substring(1)) | Out-Null
          $parametersSet[$t].Add($contents[$i + 1].substring(1)) | Out-Null
          $i++
        }
      }
    }
  }

  $cmdLinesSet,$parametersSet
}

function Set-HPPrivatePSScriptsEntries {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true,Position = 0)]
    $CmdLines,

    [Parameter(Mandatory = $true,Position = 1)]
    $Parameters,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $types = '[Logon]','[Logoff]','[Startup]','[Shutdown]'
  $contents = ""
  foreach ($type in $types) {
    if ($CmdLines.contains($type)) {
      for ($i = 0; $i -lt $CmdLines[$type].Count; $i++) {
        if ($i -eq 0) {
          $contents += "$type`n"
        }
        $contents += "$($i)$($CmdLines[$type][$i])`n"
        $contents += "$($i)$($Parameters[$type][$i])`n"
      }
      $contents += "`n"
    }
  }

  if (-not [System.IO.File]::Exists($Path)) {
    New-Item -Force -Path $Path -Type File
  }
  $contents | Set-Content -Path $Path -Force
}

<#
.SYNOPSIS
  Add a PowerShell script to the group policy
 
.DESCRIPTION
  This function adds a PowerShell script to the group policy that runs at Startup or Shutdown. This function is invoked by Add-HPBIOSWindowsUpdateScripts.
 
.PARAMETER Type
  Type of the script, if it runs at Startup or Shutdown.
 
.PARAMETER CmdLine
  The command line, it is also possible to specify as CmdLine a path to a PowerShell script.
 
.PARAMETER Parameters
  The parameters to be passed to the script at the execution time.
 
.PARAMETER Path
  If needed, a custom path can be specified.
 
.EXAMPLE
  Add-PSScriptsEntry -Type 'Shutdown' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Add-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Add-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1' -Parameters 'myparam'
#>

function Add-PSScriptsEntry
{
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Add-PSScriptsEntry")]
  param(
    [ValidateSet('Startup','Shutdown')]
    [Parameter(Mandatory = $true,Position = 0)]
    [string]$Type,

    [Parameter(Mandatory = $true,Position = 1)]
    [string]$CmdLine,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$Parameters,

    [Parameter(Mandatory = $false,Position = 3)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $cmdLinesSet,$parametersSet = Get-HPPrivatePSScriptsEntries -Path $Path

  if (-not $cmdLinesSet.ContainsKey("[$Type]")) {
    $cmdLinesSet["[$Type]"] = [System.Collections.ArrayList]@()
  }
  if (-not $parametersSet.ContainsKey("[$Type]")) {
    $parametersSet["[$Type]"] = [System.Collections.ArrayList]@()
  }

  if (-not $cmdLinesSet["[$Type]"].contains("CmdLine=$CmdLine")) {
    $cmdLinesSet["[$Type]"].Add("CmdLine=$CmdLine") | Out-Null
    $parametersSet["[$Type]"].Add("Parameters=$Parameters") | Out-Null
  }

  Set-HPPrivatePSScriptsEntries -CmdLines $cmdLinesSet -Parameters $parametersSet -Path $Path
}

<#
.SYNOPSIS
  Get HP-CMSL environment configuration
 
.DESCRIPTION
  This function returns environment information to help debugging issues
 
.EXAMPLE
  Get-HPCMSLEnvironment > MyEnvironment.txt
#>

function Get-HPCMSLEnvironment {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPCMSLEnvironment")]
  param()

  Get-ComputerInfo
  $psVersionTable
  try {
    $psISE
  }
  catch {
    'Not running on Windows PowerShell ISE'
  }

  $modules = @(
    'HP.Consent',
    'HP.Private',
    'HP.Utility',
    'HP.ClientManagement',
    'HP.Firmware',
    'HP.Notifications',
    'HP.Sinks',
    'HP.Retail',
    'HP.Softpaq',
    'HP.Repo',
    'HP.SmartExperiences'
  )

  $modulesFullVersion = @{}
  foreach ($module in $modules) {
    $m = Get-Module -Name $module
    if ($null -eq $m) {
      $m = Get-Module -Name $module -ListAvailable
    }
    $path = "$($m.ModuleBase)\$module.psd1"
    $line = Select-String -Path $path -Pattern "FullModuleVersion = '(.+)'"
    if ($null -eq $line -or $line.PSobject.Properties.name -notcontains 'Matches') {
      $modulesFullVersion[$module] = $null
      continue
    }
    $lineMatch = $line.Matches.Value
    $lineMatch -match "'(.+)'" | Out-Null
    $fullModuleVersion = $Matches[1]
    $modulesFullVersion[$module] = $fullModuleVersion
  }
  $modulesFullVersion
  @{
    SystemID = Get-HPDeviceProductID
    Os = Get-HPPrivateCurrentOs
    OsVer = Get-HPPrivateCurrentDisplayOSVer
    Bitness = Get-HPPrivateCurrentOsBitness
  }
}

<#
.SYNOPSIS
  Remove a PowerShell script from the group policy
 
.DESCRIPTION
  This function removes a PowerShell script from the group policy that runs at Startup or Shutdown. Returns true if some entry was removed. This function is invoked by Add-HPBIOSWindowsUpdateScripts.
 
.PARAMETER Type
  Type of the script, if it runs at Startup or Shutdown.
 
.PARAMETER CmdLine
  The command line, it is also possible to specify as CmdLine a path to a PowerShell script.
 
.PARAMETER Parameters
  The parameters to be passed to the script at the execution time.
 
.PARAMETER Path
  If needed, a custom path can be specified.
 
.EXAMPLE
  Remove-PSScriptsEntry -Type 'Shutdown' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Remove-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Remove-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1' -Parameters 'myparam'
#>

function Remove-PSScriptsEntry {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Remove-PSScriptsEntry")]
  param(
    [ValidateSet('Startup','Shutdown')]
    [Parameter(Mandatory = $true,Position = 0)]
    [string]$Type,

    [Parameter(Mandatory = $true,Position = 1)]
    [string]$CmdLine,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$Parameters,

    [Parameter(Mandatory = $false,Position = 3)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $cmdLinesSet,$parametersSet = Get-HPPrivatePSScriptsEntries -Path $Path

  if (-not $cmdLinesSet.ContainsKey("[$Type]") -and -not $parametersSet.ContainsKey("[$Type]")) {
    # File doesn't contain the type specified. There is nothing to be removed
    return
  }

  $removed = $false
  # If a parameter is specified we remove only the scripts with the specified parameter from the file
  while ($cmdLinesSet["[$Type]"].contains("CmdLine=$CmdLine") -and
    (-not $Parameters -or $parametersSet["[$Type]"].item($cmdLinesSet["[$Type]"].IndexOf("CmdLine=$CmdLine")) -eq "Parameters=$Parameters")
  ) {
    $index = $cmdLinesSet["[$Type]"].IndexOf("CmdLine=$CmdLine")
    $cmdLinesSet["[$Type]"].RemoveAt($index) | Out-Null
    $parametersSet["[$Type]"].RemoveAt($index) | Out-Null
    $removed = $true
  }

  Set-HPPrivatePSScriptsEntries -CmdLines $cmdLinesSet -Parameters $parametersSet -Path $Path
  return $removed
}

<#
.SYNOPSIS
  Apply BIOS updates using a Windows Update package
 
.DESCRIPTION
  This function extracts the Windows Update file and prepares the system to receive a BIOS update. This function is invoked by Get-HPBIOSWindowsUpdate.
 
.PARAMETER WindowsUpdateFile
  Absolute path to the compressed CAB file downloaded with Get-HPBIOSWindowsUpdate.
  The file name must follow the standard: platform family (3 digit) + underscore + BIOS version (6 digits) + .cab, for instance: R70_011200.cab
 
.NOTES
  Requires Windows group policy support
 
.EXAMPLE
  Add-HPBIOSWindowsUpdateScripts -WindowsUpdateFile C:\R70_011200.cab
#>

function Add-HPBIOSWindowsUpdateScripts {
  [CmdletBinding(DefaultParameterSetName = "Default",HelpUri = "https://developers.hp.com/hp-client-management/doc/Add-HPBIOSWindowsUpdateScripts")]
  param(
    [Parameter(Mandatory = $true,Position = 0,ParameterSetName = "Default")]
    [string]$WindowsUpdateFile
  )

  $gpt = "${env:SystemRoot}\System32\GroupPolicy\gpt.ini"
  $scripts = "${env:SystemRoot}\System32\GroupPolicy\Machine"

  New-Item -ItemType Directory -Force -Path "$scripts\Scripts" | Out-Null
  New-Item -ItemType Directory -Force -Path "$scripts\Scripts\Startup" | Out-Null
  New-Item -ItemType Directory -Force -Path "$scripts\Scripts\Shutdown" | Out-Null

  $fileName = ($WindowsUpdateFile -split '\\')[-1]
  $directory = $WindowsUpdateFile -replace $fileName,''
  $fileName = $fileName.substring(0,$fileName.Length - 4)
  $expectedDir = "$directory$fileName.cab.dir"
  Invoke-HPPrivateExpandCAB -cab $WindowsUpdateFile -expectedFile $WindowsUpdateFile
  $inf = Get-ChildItem -Path $expectedDir -File -Filter "$fileName*.inf" -Name
  if (-not $inf) {
    Remove-Item $expectedDir -Force -Recurse
    throw "Invalid cab file, did not find .inf in contents"
  }
  $infFileName = $inf.substring(0,$inf.Length - 4)
  Remove-Item $WindowsUpdateFile -Force
  Remove-Item -Recurse -Force "$scripts\Scripts\Shutdown\wu_image" -ErrorAction Ignore
  Move-Item $expectedDir "$scripts\Scripts\Shutdown\wu_image" -Force
  $log = ".\wu_bios_update.log"

  # CMSL modules should be included at startup to use Remove-PSScriptsEntry function
  $clientManagementModulePath = (Get-Module -Name HP.ClientManagement).Path
  $privateModulePath = (Get-Module -Name HP.Private).Path

  # Move DeviceInstall service to be notified after the Group Policy shutdown script
  $preshutdownOrder = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control" -Name "PreshutdownOrder").PreshutdownOrder | Where-Object { $_ -ne "DeviceInstall" }
  $preshutdownOrder += "DeviceInstall"
  Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control" -Name "PreshutdownOrder" -Value $preshutdownOrder -Force -ErrorAction SilentlyContinue | Out-Null

  # Startup script
  '$driver = Get-WmiObject Win32_PnPSignedDriver | ? DeviceClass -eq "Firmware" | Where Manufacturer -eq "HP Inc."
$infName = $driver.InfName
if ($infName) {
  Write-Host "INF name: $infName" *>> '
 + $log + '
  '
 + ${env:SystemRoot} + '\System32\pnputil.exe /delete-driver $infName /uninstall /force *>> ' + $log + '
} else {
  Write-Host "No device to clean up" *>> '
 + $log + '
}
 
Write-Host "Clean EFI partition" *>> '
 + $log + '
$volumes = Get-Partition | Select-Object `
  @{ Name = "Path"; Expression = { (Get-Volume -Partition $_).Path } },`
  @{ Name = "Mount"; Expression = {(Get-Volume -Partition $_).DriveType } },`
  @{ Name = "Type"; Expression = { $_.Type } },`
  @{ Name = "Disk"; Expression = { $_.DiskNumber } }
$volumes = $volumes | Where-Object Mount -EQ "Fixed"
[array]$efi = $volumes | Where-Object { $_.type -eq "System" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).OperationalStatus -eq "Online" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).IsBoot -eq $true }
Remove-Item "$($efi[0].Path)EFI\HP\DEVFW\*" -Recurse -Force -ErrorAction Ignore *>> '
 + $log + '
 
Remove-Item -Force '
 + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Startup\wu_startup.ps1 *>> ' + $log + '
Remove-Item -Force '
 + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Shutdown\wu_shutdown.ps1 *>> ' + $log + '
Remove-Item -Recurse -Force '
 + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Shutdown\wu_image *>> ' + $log + '
 
if (Get-Module -Name HP.Private) {remove-module -force HP.Private }
if (Get-Module -Name HP.ClientManagement) {remove-module -force HP.ClientManagement }
Import-Module -Force '
 + $privateModulePath + ' *>> ' + $log + '
Import-Module -Force '
 + $clientManagementModulePath + ' -Function Remove-PSScriptsEntry *>> ' + $log + '
Remove-PSScriptsEntry -Type "Startup" -CmdLine wu_startup.ps1 *>> '
 + $log + '
Remove-PSScriptsEntry -Type "Shutdown" -CmdLine wu_shutdown.ps1 *>> '
 + $log + '
gpupdate /wait:0 /force /target:computer *>> '
 + $log + '
'
 | Out-File "$scripts\Scripts\Startup\wu_startup.ps1"

  # Shutdown script
  'param($wu_inf_name)
 
net start DeviceInstall *>> '
 + $log + '
$driver = Get-WmiObject Win32_PnPSignedDriver | ? DeviceClass -eq "Firmware" | Where Manufacturer -eq "HP Inc."
$infName = $driver.InfName
if ($infName) {
  Write-Host "INF name: $infName" *>> '
 + $log + '
  '
 + ${env:SystemRoot} + '\System32\pnputil.exe /delete-driver $infName /uninstall /force *>> ' + $log + '
} else {
  Write-Host "No device to clean up" *>> '
 + $log + '
}
 
Write-Host "Clean EFI partition" *>> '
 + $log + '
$volumes = Get-Partition | Select-Object `
  @{ Name = "Path"; Expression = { (Get-Volume -Partition $_).Path } },`
  @{ Name = "Mount"; Expression = {(Get-Volume -Partition $_).DriveType } },`
  @{ Name = "Type"; Expression = { $_.Type } },`
  @{ Name = "Disk"; Expression = { $_.DiskNumber } }
$volumes = $volumes | Where-Object Mount -EQ "Fixed"
[array]$efi = $volumes | Where-Object { $_.type -eq "System" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).OperationalStatus -eq "Online" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).IsBoot -eq $true }
Remove-Item "$($efi[0].Path)EFI\HP\DEVFW\*" -Recurse -Force -ErrorAction Ignore *>> '
 + $log + '
 
$volume = Get-BitLockerVolume | Where-Object VolumeType -EQ "OperatingSystem"
if ($volume.ProtectionStatus -ne "Off") {
  Suspend-BitLocker -MountPoint $volume.MountPoint -RebootCount 1 *>> '
 + $log + '
}
 
Write-Host "Invoke PnPUtil to update the BIOS" *>> '
 + $log + '
'
 + ${env:SystemRoot} + '\System32\pnputil.exe /add-driver ' + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Shutdown\wu_image\$wu_inf_name.inf /install *>> ' + $log + '
Write-Host "WU driver installed" *>> '
 + $log + '
 
$volume = Get-BitLockerVolume | Where-Object VolumeType -EQ "OperatingSystem"
if ($volume.ProtectionStatus -ne "Off") {
  Suspend-BitLocker -MountPoint $volume.MountPoint -RebootCount 1 *>> '
 + $log + '
}
'
 | Out-File "$scripts\Scripts\Shutdown\wu_shutdown.ps1"

  "[General]`ngPCMachineExtensionNames=[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}]`nVersion=65537" | Set-Content -Path $gpt -Force

  Remove-PSScriptsEntry -Type "Startup" -CmdLine "wu_startup.ps1" | Out-Null
  Remove-PSScriptsEntry -Type "Shutdown" -CmdLine "wu_shutdown.ps1" | Out-Null
  Add-PSScriptsEntry -Type "Startup" -CmdLine "wu_startup.ps1"
  Add-PSScriptsEntry -Type "Shutdown" -CmdLine "wu_shutdown.ps1" -Parameters "$infFileName"
  gpupdate /wait:0 /force /target:computer
  Write-Host -ForegroundColor Cyan "Firmware image has been deployed. The process will continue after reboot."
}

<#
 .SYNOPSIS
  Get platform name, system ID, or operating system support using either the platform name or its system ID.
 
.DESCRIPTION
  This function retrieves information about the platform, given a platform name or system id. It can be used to convert between platform name and system IDs. Note that a platform may have multiple system IDs, or a system ID may map to multiple platforms.
 
  Currently returns the following information:
 
  - SystemID - the system iD for this platform
  - FamilyID - the platform family ID
  - Name - the name of the platform
  - DriverPackSupport - this platform supports driver packs
 
  Get-HPDeviceDetails functionality is not supported in WinPE.
 
.PARAMETER Platform
  Query by platform id (a 4-digit hexadecimal number).
 
.PARAMETER Name
  Query by platform name. The name must match exactly, unless the -match parameter is also specified.
 
.PARAMETER Like
  Relax the match to a substring match. if the platform contains the substring defined by the -Name parameter, it will be included in the return. This parameter can also be specified as Match, for backwards compatibility.
 
  This parameter is now obsolete and may be removed at a future time. You can simply pass wildcards in the name field instead of using the like parameter.
  The following two examples are identical:
 
    Get-HPDeviceDetails -name '\*EliteBook\*'
 
  is the same as:
 
    Get-HPDeviceDetails -like -name 'EliteBook'
 
.PARAMETER OSList
  Return the list of supported operating systems for the specified platform.
 
.PARAMETER Url
  Specify an alternate location for the HP Image Assistant (HPIA) platform list XML. This URL must be http or https. If not specified, https://hpia.hpcloud.hp.com/ref is used by default.
 
.EXAMPLE
  Get-HPDeviceDetails -Platform 8100
 
.EXAMPLE
  Get-HPDeviceDetails -Name 'HP ProOne 400 G3 20-inch Touch All-in-One PC'
 
.EXAMPLE
  Get-HPDeviceDetails -Like -Name '840 G5'
 
#>

function Get-HPDeviceDetails {
  [CmdletBinding(
    DefaultParameterSetName = "FromID",
    HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceDetails")
  ]
  param(
    [ValidatePattern("^[a-fA-F0-9]{4}$")]
    [Parameter(Mandatory = $false,Position = 0,ParameterSetName = "FromID")]
    [string]$Platform,

    [Parameter(Mandatory = $true,Position = 1,ParameterSetName = "FromName")]
    [string]$Name,

    [Parameter(Mandatory = $false,Position = 2,ParameterSetName = "FromName")]
    [Alias('Match')]
    [switch]$Like,

    [Parameter(Mandatory = $false,ParameterSetName = "FromName")]
    [Parameter(Mandatory = $false,ParameterSetName = "FromID")]
    [switch]$OSList,

    [Parameter(Mandatory = $false,Position = 3,ParameterSetName = "FromName")]
    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "FromID")]
    [string]$Url = "https://hpia.hpcloud.hp.com/ref"
  )

  if (Test-WinPE -Verbose:$VerbosePreference) { throw "Getting HP Device details is not supported in WinPE" }

  $filename = "platformList.cab"
  $Url = "$Url/$filename"
  $try_on_ftp = $false

  try {
    $file = Get-HPPrivateOfflineCacheFiles -url $Url -FileName $filename -Expand -Verbose:$VerbosePreference
  }
  catch {
    # platformList is not reachable on AWS, try to get it from FTP
    $try_on_ftp = $true
  }

  if ($try_on_ftp)
  {
    try {
      $url = "https://ftp.hp.com/pub/caps-softpaq/cmit/imagepal/ref/platformList.cab"
      $file = Get-HPPrivateOfflineCacheFiles -url $url -FileName $filename -Expand -Verbose:$VerbosePreference
    }
    catch {
      Write-Host -ForegroundColor Magenta "platformList is not available on AWS or FTP."
      throw [System.Net.WebException]"Could not find platformList."
    }
  }

  if (-not $platform -and -not $Name) {
    try { $platform = Get-HPDeviceProductID -Verbose:$VerbosePreference }
    catch { Write-Verbose "No platform found." }
  }
  if ($platform) {
    $platform = $platform.ToLower()
  }
  if ($PSCmdlet.ParameterSetName -eq "FromID") {
    $data = Select-Xml -Path "$file" -XPath "/ImagePal/Platform/SystemID[normalize-space(.)=`"$platform`"]/parent::*"
  }
  else {
    $data = Select-Xml -Path "$file" -XPath "/ImagePal/Platform/ProductName[translate(substring(`"$($name.ToLower())`",0), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')]/parent::*"
  }

  if (-not $data) { return }

  $searchName = $Name
  if ($Like.IsPresent)
  {
    if (-not ($searchName).StartsWith('*')) { $searchName = ("*$searchName") }
    if (-not ($searchName).EndsWith('*')) { $searchName = ("$searchName*") }
  }

  $data.Node | ForEach-Object {
    $__ = $_
    $pn = $_.ProductName. "#text"

    if ($oslist.IsPresent) {
      [array]$r = ($__.OS | ForEach-Object {
        if (($PSCmdlet.ParameterSetName -eq "FromID") -or ($pn -like $searchName)) {
          $rid = $Null
          if ("OSReleaseId" -in $_.PSObject.Properties.Name) { $rid = $_.OSReleaseId }

          [string]$osv = $_.OSVersion
          if ("OSReleaseIdDisplay" -in $_.PSObject.Properties.Name -and $_.OSReleaseIdDisplay -ne '20H2') {
            $rid = $_.OSReleaseIdDisplay
          }

          $obj = New-Object -TypeName PSCustomObject -Property @{
            SystemID = $__.SystemID.ToUpper()
            OperatingSystem = $_.OSDescription
            OperatingSystemVersion = $osv
            Architecture = $_.OSArchitecture
          }
          if ($rid) {
            $obj | Add-Member -NotePropertyName OperatingSystemRelease -NotePropertyValue $rid
          }
          if ("OSBuildId" -in $_.PSObject.Properties.Name) {
            $obj | Add-Member -NotePropertyName BuildNumber -NotePropertyValue $_.OSBuildId
          }
          $obj
        }
      })
    }
    else {

      [array]$r = ($__.ProductName | ForEach-Object {
        if (($PSCmdlet.ParameterSetName -eq "FromID") -or ($_. "#text" -like $searchName)) {
          New-Object -TypeName PSCustomObject -Property @{
            SystemID = $__.SystemID.ToUpper()
            Name = $_. "#text"
            DriverPackSupport = [System.Convert]::ToBoolean($_.DPBCompliant)
            UWPDriverPackSupport = [System.Convert]::ToBoolean($_.UDPCompliant)
          }
        }
      })
    }
    return $r
  }
}



function getFormattedBiosSettingValue {
  [CmdletBinding()]
  param($obj)
  switch ($obj.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSString' } {
      $result = $obj.Value

    }
    { $_ -eq 'HPBIOS_BIOSInteger' } {
      $result = $obj.Value
    }
    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      $result = $obj.CurrentValue
    }
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      throw [System.InvalidOperationException]"Password values cannot be retrieved, it will always result in an empty string"
    }
    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      $result = $obj.Value
    }
  }
  return $result
}

function getWmiField ($obj,$fn) { $obj.$fn }


# format a setting using BCU (custom) format
function convertSettingToBCU ($setting) {
  #if ($setting.DisplayInUI -eq 0) { return }
  switch ($setting.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSString' } {
      Write-Output $setting.Name

      if ($setting.Value.contains("`n")) {
        $setting.Value.Split("`n") | ForEach-Object {
          $c = $_.trim()
          Write-Output "`t$c" }
      }
      else {
        Write-Output "`t$($setting.Value)"
      }

    }
    { $_ -eq 'HPBIOS_BIOSInteger' } {
      Write-Output $setting.Name
      Write-Output "`t$($setting.Value)"
    }
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Output $setting.Name
      Write-Output ""
    }
    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      Write-Output $setting.Name
      $fields = $setting.Value.Split(",")
      foreach ($f in $fields) {
        Write-Output "`t$f"
      }
    }
    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      Write-Output $setting.Name
      if ($null -ne $setting.Value) {
        $fields = $setting.Value.Split(",")
        foreach ($f in $fields) {
          Write-Output "`t$f"
        }
      }
      else {
        Write-Output "`t$($setting.Value)"
      }
    }
  }
}

function formatBiosVersionsOutputList ($doc) {
  switch ($format) {
    "json" { return $doc | ConvertTo-Json }
    "xml" {
      Write-Output "<bios id=`"$platform`">"
      if ($all)
      {
        $doc | ForEach-Object { Write-Output "<item><ver>$($_.Ver)</ver><bin>$($_.bin)</bin><date>$($_.date)</date><rollback_allowed>$($_.RollbackAllowed)</rollback_allowed><importance>$($_.Importance)</importance></item>" }
      }
      else {
        $doc | ForEach-Object { Write-Output "<item><ver>$($_.Ver)</ver><bin>$($_.bin)</bin><date>$($_.date)</date></item>" }
      }
      Write-Output "</bios>"
      return
    }
    "csv" {
      return $doc | ConvertTo-Csv -NoTypeInformation
    }
    "list" { $doc | ForEach-Object { Write-Output "$($_.Bin) version $($_.Ver.TrimStart("0")), released $($_.Date)" } }
    default { return $doc }
  }
}


# format a setting using HPIA (xml) format
function convertSettingToXML ($setting) {
  #if ($setting.DIsplayInUI -eq 0) { return }
  Write-Output " <BIOSSetting>"
  Write-Output " <Name>$([System.Web.HttpUtility]::HtmlEncode($setting.Name))</Name>"
  Write-Output " <Class>$($setting.CimClass.CimClassName)</Class>"
  Write-Output " <DisplayInUI>$($setting.DisplayInUI)</DisplayInUI>"
  Write-Output " <IsReadOnly>$($setting.IsReadOnly)</IsReadOnly>"
  Write-Output " <RequiresPhysicalPresence>$($setting.RequiresPhysicalPresence)</RequiresPhysicalPresence>"
  Write-Output " <Sequence>$($setting.Sequence)</Sequence>"

  switch ($setting.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Output " <Value></Value>"
      Write-Output " <Min>$($setting.MinLength)</Min>"
      Write-Output " <Max>$($setting.MaxLength)</Max>"

      Write-Output " <SupportedEncodings Count=""$($setting.SupportedEncoding.Count)"">"
      foreach ($e in $setting.SupportedEncoding) {
        Write-Output " <Encoding>$e</Encoding>"
      }
      Write-Output " </SupportedEncodings>"
    }

    { $_ -eq 'HPBIOS_BIOSString' } {
      Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($setting.Value))</Value>"
      Write-Output " <Min>$($setting.MinLength)</Min>"
      Write-Output " <Max>$($setting.MaxLength)</Max>"
    }

    { $_ -eq 'HPBIOS_BIOSInteger' } {
      Write-Output " <Value>$($setting.Value)</Value>"
      #Write-Output " <DisplayInUI>$($setting.DisplayInUI)</DisplayInUI>"
      Write-Output " <Min>$($setting.LowerBound)</Min>"
      Write-Output " <Max>$($setting.UpperBound)</Max>"
    }

    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($setting.CurrentValue))</Value>"
      Write-Output " <ValueList Count=""$($setting.Size)"">"
      foreach ($e in $setting.PossibleValues) {
        Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($e))</Value>"
      }
      Write-Output " </ValueList>"
    }

    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($setting.Value))</Value>"
      Write-Output " <ValueList Count=""$($setting.Size)"">"
      foreach ($e in $setting.Elements) {
        Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($e))</Value>"
      }
      Write-Output " </ValueList>"
    }
  }
  Write-Output " </BIOSSetting>"
}

function convertSettingToJSON ($original_setting) {

  $setting = $original_setting | Select-Object *

  if ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSInteger") {
    $min = $setting.LowerBound
    $max = $setting.UpperBound
    Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty
    Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty

    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value
  }

  if (($setting.CimClass.CimClassName -eq "HPBIOS_BIOSString") -or ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSPassword")) {
    $min = $setting.MinLength
    $max = $setting.MaxLength
    Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty -Force
    Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty -Force
    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value
  }

  if ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSEnumeration") {
    $min = $setting.Size
    $max = $setting.Size
    #Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty
    #Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty
    $setting.Value = $setting.CurrentValue
    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value,PossibleValues
  }

  if ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSOrderedList") {
    #if Elements is null, initialize it as an empty array else select the first object
    $Elements = $setting.Elements,@() | Select-Object -First 1
    $min = $Elements.Count
    $max = $Elements.Count
    Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty
    Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty
    Add-Member -InputObject $setting -Name "PossibleValues" -Value $Elements -MemberType NoteProperty
    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value,Elements
  }



  $d | ConvertTo-Json -Depth 5 | Write-Output
}

# format a setting as a CSV entry
function convertSettingToCSV ($setting) {
  switch ($setting.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"picklist`",$($setting.RequiresPhysicalPresence),$($setting.Size),$($setting.Size)"
    }
    { $_ -eq 'HPBIOS_BIOSString' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"string`",$($setting.RequiresPhysicalPresence),$($setting.MinLength),$($setting.MaxLength)"
    }
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Output "`"$($setting.Name)`",`"`",$($setting.IsReadOnly),`"password`",$($setting.RequiresPhysicalPresence),$($setting.MinLength),$($setting.MaxLength)"
    }
    { $_ -eq 'HPBIOS_BIOSInteger' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"integer`",$($setting.RequiresPhysicalPresence),$($setting.LowerBound),$($setting.UpperBound)"
    }
    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"orderedlist`",$($setting.RequiresPhysicalPresence),$($setting.Size),$($setting.Size)"
    }
  }
}

function extractBIOSVersion {
  [CmdletBinding()]
  param
  (
    [Parameter(Position = 0,Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string]$BIOSVersion

  )
  [string]$ver = $null

  # Does the BIOS version string contains x.xx[.xx]?
  [bool]$found = $BIOSVersion -match '(\d+(\.\d+){1,2})'
  if ($found) {
    $ver = $matches[1]
    Write-Verbose "BIOS version extracted=[$ver]"
  }

  $ver
}




# SIG # Begin signature block
# MIIt/gYJKoZIhvcNAQcCoIIt7zCCLesCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBJmlzcI0bk0tb+
# 9StpLrH3bEBH7tzfnwlk6gZ5/ulIfKCCE2wwggXAMIIEqKADAgECAhAP0bvKeWvX
# +N1MguEKmpYxMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV
# BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMjIwMTEz
# MDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM
# RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQD
# ExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
# DwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aa
# za57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllV
# cq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT
# +CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd
# 463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+
# EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92k
# J7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5j
# rubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7
# f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJU
# KSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+wh
# X8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQAB
# o4IBZjCCAWIwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5n
# P+e6mK4cD08wHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMH8GCCsGAQUFBwEBBHMwcTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEkGCCsGAQUFBzAC
# hj1odHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJh
# bmNlRVZSb290Q0EuY3J0MEsGA1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHAYD
# VR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQELBQADggEBAEHx
# qRH0DxNHecllao3A7pgEpMbjDPKisedfYk/ak1k2zfIe4R7sD+EbP5HU5A/C5pg0
# /xkPZigfT2IxpCrhKhO61z7H0ZL+q93fqpgzRh9Onr3g7QdG64AupP2uU7SkwaT1
# IY1rzAGt9Rnu15ClMlIr28xzDxj4+87eg3Gn77tRWwR2L62t0+od/P1Tk+WMieNg
# GbngLyOOLFxJy34riDkruQZhiPOuAnZ2dMFkkbiJUZflhX0901emWG4f7vtpYeJa
# 3Cgh6GO6Ps9W7Zrk9wXqyvPsEt84zdp7PiuTUy9cUQBY3pBIowrHC/Q7bVUx8ALM
# R3eWUaNetbxcyEMRoacwggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggbwMIIE2KADAgECAhAI+qTPsJ3byDJ7SsgX0LBUMA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjIwMzA5MDAwMDAwWhcNMjMwMzA5MjM1OTU5WjB1MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJUGFsbyBB
# bHRvMRAwDgYDVQQKEwdIUCBJbmMuMRkwFwYDVQQLExBIUCBDeWJlcnNlY3VyaXR5
# MRAwDgYDVQQDEwdIUCBJbmMuMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKC
# AYEA2KwFARbsSL8FnMdZ++xo7iVdqg+ZOY0S2KkvYQdNNcvrcfHTdNpNgf65RuIt
# VQxdJXzmZcAOXJUPjRQRduvFf/I8jqR4UwBLsNoy/sEuQIDCfezNSQz8TPredjUG
# Lr6Y9ie1vYryqJ110Mj6NtXZQidlytEneq3z73Ec7TRFKp8iiiwNpTcbhAq93pq6
# bjnc98ajFUBHJu9Gfk1Or3haR6m7YH0LRLVWm18I2OKrcPLk67hWRj6Aa7/heBkk
# F8TfGCUwGBHhblrprBVECR3M4zTnMygBfxVEzYsdyAytPy0DgqzZ7+rHY0yvgDUx
# Fi/d1SyqNDCf6FBBudNjzw7TULEBHlJjk96xhd1z4X5ctL1kW4duC7Mba6H8A1lI
# qM5qa+8Fr88IJhnl21PlkBp+XAk3lBaeJ/DVpORIv3bhUV8OLae6ElQBGvqQoEY/
# AaNerghhFjiqAhaUG3z3Y7ruhVaCmuw/SMVS79dxESj/J1qHWVnF1tn2a4liq/RY
# VeFTAgMBAAGjggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfRO
# QjAdBgNVHQ4EFgQUAjIiVx974XGZre7F5HqNCJiWZbowDgYDVR0PAQH/BAQDAgeA
# MBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6
# Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5n
# UlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz
# ODQyMDIxQ0ExLmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIB
# FhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGE
# MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUH
# MAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQC
# MAAwDQYJKoZIhvcNAQELBQADggIBAFrOPeL4ph8SmHwwcUQO7nPnapyOS0I50w70
# nVZ9CtrgyA7hiZmVm/CsC1JU8zg1dNyfH7wCDaoMAnqtybcdmhIXc4STwfcpiKOH
# nL3fRQcZ2zCCXmX5lkWYWni9Nqx603JQ8yiUSl1sMyv0Cd4RasOBHnjQuekDDKNT
# QvOiEA3NCZDGEjtIjE+TGqLW2kUEtjxzyr0mnhmidRaHry5C1GKu0mlKExwabOLW
# xGrXj4FPtKmWXZh00lMbbdeHm1Zqn9CTsO6xt8CQXSemcpb7lXY80um71wQO23ub
# tQGDe4QpShomqPmEIVxM5/B6Yih/0Lb8mt60SLfT5EOVS/Dhd86lSHcncL9JLxaq
# WwbQhIwpEa4b3MiZqyemqb0+YIBn5yG43M4oLzRPTo2mPwG19OtnMVZsrcjGEzLz
# EiBb9/YXsf8G5LAh86x2kRKDad35NNNojUJYVBtD7MGEsL37XF+6kWXsp92on2b2
# QLEL/5ZzJHmfrJ8m0TXMb4sMSI2KnHtCvEjG2MIAnjFEvNZ1ZFsKS78mwylDyHL0
# yTuv08JqDuommKgjmyvtLEeb6OYsOnSVQIcyV4XCY1kFA8mDuIsIlbWE3Nyv94Of
# N+4jNKcDzniYb5LmKlXraIM8PjPpYb34DlNpzCDN7/tJuMFsy/NwArj1SiL630mg
# Dm0fS5OgMYIZ6DCCGeQCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAI+qTPsJ3byDJ7SsgX0LBU
# MA0GCWCGSAFlAwQCAQUAoHwwEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkD
# MQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJ
# KoZIhvcNAQkEMSIEIEsE6A62XrFtUlu17zsHKEJK5IP9+Mvnp5lAQkBkCw52MA0G
# CSqGSIb3DQEBAQUABIIBgCO9vrlhlcQ8Kpl5g/wfmGq2LN2WOsvOXLBhrDbNfGjH
# NTblO0+WODfDyu/zzaG1zY14AGBBDVXuylXenw3t1qzLb1AUpuFgJzEvPYWx138D
# L5p1t36jCWlUbqfrpDQPxkyTpYeW2z6dEoeA+GEGKhm0z6MhthWDl++UmHLUzjHA
# bvTlxVabdEZIWnDWOZL3m/YYreNPZI+Uxco554acNRLsbZLla/sGN2rFtLJ1N5x3
# OOybc78CycX9bGqDimYzjEqbo4iKEPyV7TBfW9RRCZNPGHWD3ARYpj1BhAJDVz0n
# ra+NMjrbfIJ4XXBGQCRXZyGNVcLlh/G5Zg5IsqDycwWOloAOBCt2GLkhijiqXXA9
# 95yOC2N2Ls7lLkGeCTmdiMSezVTxwd9UKxa14T+QKCNqEfdFB0+UYrsSprVV1sc0
# /ltIpzYM7YPwr0KxAMkbOXhbITj+8ccjb8ChPEmCZpIYyNEFt9reRV+w0zIiqsDo
# etstouqKeVgbVr7bbq33/KGCFz4wghc6BgorBgEEAYI3AwMBMYIXKjCCFyYGCSqG
# SIb3DQEHAqCCFxcwghcTAgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQ
# AQSgaQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCCub9Ag4rXg
# K7UFXedct4KMR7mV472J2hoAkIW+AyjBGgIRAIqrRFFNNAvCEoMi6P5VB1EYDzIw
# MjIxMjAyMjAyODQ0WqCCEwcwggbAMIIEqKADAgECAhAMTWlyS5T6PCpKPSkHgD1a
# MA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy
# dCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNI
# QTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwOTIxMDAwMDAwWhcNMzMxMTIxMjM1
# OTU5WjBGMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNlcnQxJDAiBgNVBAMT
# G0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBAM/spSY6xqnya7uNwQ2a26HoFIV0MxomrNAcVR4eNm28klUM
# YfSdCXc9FZYIL2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK6aYo25BjXL2JU+A6
# LYyHQq4mpOS7eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7gL307scpTjUCDHufL
# ckkoHkyAHoVW54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo44DLannR0hCRRinr
# PibytIzNTLlmyLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5PgxeZowaCiS+nKrS
# nLb3T254xCg/oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h3cKtpX74LRsf7CtG
# GKMZ9jn39cFPcS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn88JSxOYWe1p+pSVz
# 28BqmSEtY+VZ9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g9ArmFG1keLuY/ZTD
# cyHzL8IuINeBrNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQprdhZPrZIGwYUWC6
# poEPCSVT8b876asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcTB5rBeO3GiMiwbjJ5
# xwtZg43G7vUsfHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVzHIR+187i1Dp3AgMB
# AAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFGKK
# 3tBh/I8xFO2XC809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
# U3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZU
# aW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAFWqKhrzRvN4Vzcw
# /HXjT9aFI/H8+ZU5myXm93KKmMN31GT8Ffs2wklRLHiIY1UJRjkA/GnUypsp+6M/
# wMkAmxMdsJiJ3HjyzXyFzVOdr2LiYWajFCpFh0qYQitQ/Bu1nggwCfrkLdcJiXn5
# CeaIzn0buGqim8FTYAnoo7id160fHLjsmEHw9g6A++T/350Qp+sAul9Kjxo6UrTq
# vwlJFTU2WZoPVNKyG39+XgmtdlSKdG3K0gVnK3br/5iyJpU4GYhEFOUKWaJr5yI+
# RCHSPxzAm+18SLLYkgyRTzxmlK9dAlPrnuKe5NMfhgFknADC6Vp0dQ094XmIvxwB
# l8kZI4DXNlpflhaxYwzGRkA7zl011Fk+Q5oYrsPJy8P7mxNfarXH4PMFw1nfJ2Ir
# 3kHJU7n/NBBn9iYymHv+XEKUgZSCnawKi8ZLFUrTmJBFYDOA4CPe+AOk9kVH5c64
# A0JH6EE2cXet/aLol3ROLtoeHYxayB6a1cLwxiKoT5u92ByaUcQvmvZfpyeXupYu
# hVfAYOd4Vn9q78KVmksRAsiCnMkaBXy6cbVOepls9Oie1FqYyJ+/jbsYXEP10Cro
# 4mLueATbvdH7WwqocH7wl4R44wgDXUcsY6glOJcB0j862uXl9uab3H4szP8XTE0A
# otjWAQ64i+7m4HJViSwnGWH2dwGMMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0o
# ZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln
# aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhE
# aWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIy
# MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x
# OzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGlt
# ZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1
# BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3z
# nIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZ
# Kz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald6
# 8Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zk
# psUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYn
# LvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIq
# x5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOd
# OqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJ
# TYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJR
# k8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEo
# AA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1Ud
# EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8G
# A1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjAT
# BgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGG
# GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2Nh
# Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYD
# VR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9
# bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0T
# zzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYS
# lm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaq
# T5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl
# 2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1y
# r8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05
# et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6um
# AU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSwe
# Jywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr
# 7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYC
# JtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzga
# oSv27dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcN
# AQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJl
# ZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjEL
# MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
# LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBp
# M+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR
# 0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0
# O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53
# yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4
# x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3Vd
# eGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1C
# doeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJh
# besz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz
# 0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNB
# ERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+
# CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8w
# HQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0
# ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGsw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcw
# AoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2Vy
# dC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYE
# VR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqs
# oYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPI
# TtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZ
# qPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/v
# oVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+
# cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNy
# AgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
# dGFtcGluZyBDQQIQDE1pckuU+jwqSj0pB4A9WjANBglghkgBZQMEAgEFAKCB0TAa
# BgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTIyMTIw
# MjIwMjg0NFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU84ciTYYzgpI1qZS8vY+W
# 6f4cfHMwLwYJKoZIhvcNAQkEMSIEIEOzLgCQdZ8OVICA97KgofzC73U63EOeuUdc
# Dat6bx3BMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIMf04b4yKIkgq+ImOr4axPxP
# 5ngcLWTQTIB1V6Ajtbb6MA0GCSqGSIb3DQEBAQUABIICAKyJQJnADQI72J6Vrszx
# 6zIwZQMVq/3+Ah3jzBx8RMPx/QRMuQwBkvUDx6v68+i76Bzy6g09HCPy+NN3j/Y9
# Ew3VeXE+08B/+dkBIK9GWnGgrBsHU0Ypcy0w5/a5UM6v4a/QZtsbV61vSEzrwLdO
# tXuBaMpipF/KHj29HVpG7e88mzScJmw4OOk8R7x8PtKYO63BUzfGR0cGJ6/LQcIf
# gGg5V4aBesJX+A56aDAgazxNr/hZR2wJEDzz4i2hNbg+zgdKCuHvK7EnW9iq/ZnW
# Mws2Z2XTCi/8S8CqRJDGDcHexJ3HD9XsyrzT7JM5q0Z1dBRnq4HYvqcJTRB5sRFW
# jDou7jZHiN3AHrke0veig2U4Ckgj8KKMVtu8iGi5NrDvLfnGIXL+f31RoEiblKWz
# VyEfHyJK5Pben69DN6iYbO9gISdchpLEGC5/awDHC6WYT+MTX1xOzeDbVsZRCEqx
# 92I3k5/yumJGJIiukYjGelG1HATOYBTJy6dLUH7VE2nc1hZ7LUyQkKQhVbCCgL/b
# yMxseXO1VysCQTLviejGKQ/9GqYD58uFObqJylWykAK5bhQC5DpjRoinlmW2ks2a
# hAB5h2flefqOELlhf/xSEvAbbkSWqsX6MaY5OXwEIuQrywwgd5gdGdallw8MdwIg
# HDfM2hMfWtRUzEsxqy3LRBHf
# SIG # End signature block