Showing posts with label PowerShell. Show all posts
Showing posts with label PowerShell. Show all posts

Tuesday, June 4, 2013

Monitoring HP Battery Life with PowerShell & SCCM

So you have thousands of HP laptops and need to know health your batteries? May be you purchased an extra year for battery replacement and have 2 months left.

HP offers very easy solution ... Just install HP Battery Check - part of HP Support Assistant bloatware suite, run HP Battery Check Utility and wait for pretty program output page to get the results including the serial number and error code - information that you need to file the replacement claim.

Say this to any field tech and you will have to run - fast!

To avoid this or our customer I have prepared solution which automatically install just silently install the HP Battery Check Utility, silently run it and save results in Access DB on a server. Following description is for Windows 7 x86, but you can easily modify it for Window 8 and/or x64 architecture.
Access DB connection and DB update functions are snippets from Hey Scripting Guy article.

Deploy HP Battery Check
Latest version is a bit hard to find so here is the link to HP Battery Check version 4.3.2.2

  • Unpack and create SCCM Package with installation program command line: setup.exe /S /v/qn
  • Advertise/Deploy to SCCM Collection with your laptops
  • So that was the easy part  and you can reuse this package to install the utility beforehand during imaging process.


Run HP Battery Check and collect the result
Official Battery check information - Battery Check FAQ Page
I contacted the HP level 2 support and officially there are no silent switches for running the utility (BUT there is a silent switch  /S). And it took another three calls to find out where the battery scan result file is stored.

To start the battery silently from PowerShell
Start-Process "${env:ProgramFiles(x86)}\Hewlett-Packard\HP Support Framework\Resources\hpbc.exe /S"


The above line will produce out BCResOut.xml file in ProgramData folder:
%ProgramData%\Hewlett-Packard\HP Support Framework\Resources\Logs\BCResOut.xml


That can be opened by PowerShell:
[xml]$battery = get-content $env:ProgramData"Hewlett-Packard\HP Support Framework\Resources\Logs\BCResOut.xml"

and extract the right information - that took a little bit of experimenting

Then we will need a function to connect to Access database:
Function Connect-Database($fl, $Tables)
{
  $OpenStatic = 3
  $LockOptimistic = 3
  $connection = New-Object -ComObject ADODB.Connection
  $connection.Open("Provider = Microsoft.Jet.OLEDB.4.0;Data Source=$Db" )
  Update-Records($Tables)
} #End Connect-DataBase


The whole script is here:
<#
Name: Battery Check Script
Purpose: Silently run HP Battery Check and save results in Access file on the Server
Version: 1.1 - May, 2013

This script is provided "AS IS" with no warranties, confers no rights and
is not supported by the authors or deployment4everyone.

Author - Milos Pec
Blog : http://deployment4everyone.blogspot.com
Code Snipets from: http://technet.microsoft.com/en-us/magazine/2009.05.scriptingguys.aspx
#>
##########################################################
#Change this to your Access DB file on the server
$Db = "\\yourserver\deploy\batterycheck\BatteryCheck.mdb"
##########################################################

Function Check-Path($fl)
{
 If(!(Test-Path -path (Split-Path -path $fl -parent)))
   { 
     Throw "$(Split-Path -path $fl -parent) Does not Exist"
     exit
   }
  ELSE
  { 
   If(!(Test-Path -Path $fl))
     {
      Throw "$fl does not exist"
      exit
     }
  }
} #End Check-Path

Function Connect-Database($fl, $Tables)
{
  $OpenStatic = 3
  $LockOptimistic = 3
  $connection = New-Object -ComObject ADODB.Connection
  $connection.Open("Provider = Microsoft.Jet.OLEDB.4.0;Data Source=$Db" )
  Update-Records($Tables)
} #End Connect-DataBase

Function Update-Records($Tables)
{
  $RecordSet = new-object -ComObject ADODB.Recordset
   ForEach($Table in $Tables)
     {
      $Query = "Select * from $Table"
      $RecordSet.Open($Query, $Connection, $OpenStatic, $LockOptimistic)
      Invoke-Expression "Update-$Table"
      $RecordSet.Close()
     }
   $connection.Close()
} #End Update-Records

Function Update-BatteryInfo
{
    "Updating Battery Information"
    $RecordSet.AddNew()
    $RecordSet.Fields.Item("ComputerName") = $comp
    $RecordSet.Fields.Item("Model") = $model
    $RecordSet.Fields.Item("ComputerSerial") = $comp_serial
    $RecordSet.Fields.Item("BatterySerialNumber") = $batt_serial
    $RecordSet.Fields.Item("FailureID") = $failID
    $RecordSet.Fields.Item("ResultCode") = $result_code
    $RecordSet.Fields.Item("DesignCharge") = $design_charge
    $RecordSet.Fields.Item("MaxCharge") = $max_charge
    $RecordSet.Fields.Item("ChargeCapacity") = $charge_capacity
    $RecordSet.Fields.Item("DateRun") = $run_date
    $RecordSet.Update()
} #End Update-BatteryInfo


# Checking If Log File Exists
if (Test-Path "$env:ProgramData\Hewlett-Packard\HP Support Framework\Resources\Logs\BCResOut.xml") {
    [xml]$battery = get-content $env:ProgramData"Hewlett-Packard\HP Support Framework\Resources\Logs\BCResOut.xml"
} else {
   Throw "File not found ..." 
}

#Execute Battery Scan
Check-Path -fl "${env:ProgramFiles(x86)}\Hewlett-Packard\HP Support Framework\Resources\hpbc.exe"
Start-Process "${env:ProgramFiles(x86)}\Hewlett-Packard\HP Support Framework\Resources\hpbc.exe /S" -Wait

# Script Start
$comp = $env:COMPUTERNAME
$model = $battery.BatteryTest.ComputerProductName
$comp_serial = $battery.BatteryTest.ComputerSerialNumber
$design_charge = $battery.BatteryTest.result[0].p[0].InnerText
$max_charge = $battery.BatteryTest.result[0].p[1].InnerText
$batt_serial = $battery.BatteryTest.result[0].p[14].InnerText
$failID = $battery.BatteryTest.result[0].FailureID
$charge_capacity = $battery.BatteryTest.result[0].v[9].InnerText
$run_date = $battery.BatteryTest.result[0].r[0].InnerText
$result_code = $battery.BatteryTest.result[0].ResultCode

# If uncommented following will create CVS file on your desktop - to test results
#Start Creating New file
#$batt_status = $Env:USERPROFILE + "\Desktop\" + $failID + "." + $env:COMPUTERNAME + "." +(Get-Date -uFormat "%Y%m%d-%H%M") + ".csv"
#"ComputerName,Model,ComputerSerialNumber,BatterySerialNumber,FailureID,ResultCode,DesignCharge,MaxCharge,Charge_Capacity,Date" | Out-File -FilePath $batt_status -Encoding Ascii
#Add-Content -Encoding Ascii $batt_status ($comp + "," + $model + "," + $comp_serial + "," + $batt_serial + "," + $failID + "," + $result_code + "," + $design_charge + "," + $max_charge + "," + $charge_capacity + "," + $run_date).ToString()
#End Creating New file

if($env:PROCESSOR_ARCHITECTURE -eq 'x86')
  {
    $Tables = "BatteryInfo"
    Check-Path -fl $Db
    Connect-DataBase -fl $Db -tables $Tables
   }
#End Updating Access DB 


Download the script BatteryCheck.ps1
Download the database template BatteryCheck.mdb 

Wednesday, May 8, 2013

ProCurve Switch configuration for mass deployment - without ProCurve Manager

ProCurveWe were tasked to upgrade networking infrastructure for one of our customers and that meant to setup, configure and roll-out more than 2000 edge devices and core devices, mostly HP ProCurve 2910al, some PoE deices 2610, and 6200yl.
The switches needed to be swapped overnight at each location (average 40 devices per location) replacing old existing device.
Here is a VR image how the warehouse looks like: http://tinyurl.com/lcpqpz9


The configuration was quite simple so I decided to automate the process of config loading and firmware upgrade.

One way would be to use HP ProCurve Manager, but at the time of this project ProCurve was not very stable and would require lot of "waiting" for the switches to show up.

Firmware Upgrade
I created automated way to upgrade the firmware on the switches, but I have found that fastest way is hook up the switches to our setup network, start tftp server with the recent SW update and then use laptop and connect to console and just copy series of commands like this (HP 2810-XXG)

copy tftp flash <Your_TFTP_Server_IP> N_11_25.swi primary
y
copy flash flash secondary
y
boot
y
y


Sometimes you have to do it twice because the boot ROM needs to be updated but the major advantage is that you do not have to wait for the commands to finish - they are buffered and you can move on to the next switch as soon as the text is pasted to the window. This way you can prepare the bare switches to the uniform state for the next step.

Documentation and Configuration
Since we go all the information we needed in Excel (name, IP) and the configuration on edge devices was pretty much the same, I created a macro script with config template for each model. After unpacking switch MACs were scanned into the template and IP modified based on the site network.

Configuration Template

To deploy the config in as few steps as possible I decided not to script all the configuration commands in a telnet session, but rather generate the config file for each switch based on the template and then just telnet to the switch and use tftp to transfer the right file.

Because of the specific configuration let me just show some of the logic and  behind The Config Button from the image above:

  • Individual config files are generated with names like 2800_IPADDRESS.cfg
  • Configuration file defining scopes and reservations for local DHCP is generated based on the target network information.
    This will define the scope 10.123.40.0 on server 10.10.1.9
    # ======================================================================
    #          Scope name: TEST_40
    # ======================================================================
    # Add scope for 10.123.40.0
    Dhcp Server 10.10.1.9 add scope 10.123.40.0 255.255.255.0 "TEST_40" ""
    Dhcp Server 10.10.1.9 Scope 10.123.40.0 set state 1
    # ======================================================================
    #  Add Ipranges to the Scope 10.123.40.0, Server 10.10.1.9
    Dhcp Server 10.10.1.9 Scope 10.123.40.0 Add iprange 10.123.40.1 10.123.40.100
    # ======================================================================
    #  Add Excluderanges to the Scope
    Dhcp Server 10.10.1.9 Scope 10.123.40.0 add excluderange 10.123.40.1 10.123.40.1
    # ======================================================================
    #  Add OptionValues to the Scope
    Dhcp Server 10.10.1.9 Scope 10.123.40.0 set optionvalue 51 DWORD "3600"
    Dhcp Server 10.10.1.9 Scope 10.123.40.0 set optionvalue 3 IPADDRESS "10.123.40.1"


    This will add a reservation to a scope defined above:
    Dhcp Server 10.10.1.9 Scope 10.123.40.0 Add reservedip 10.123.40.10 005056C00008 "XXX-IDFXX-sALXX" "" "BOTH"
  • no the script will remotely execute commands for DHCP configuration
    netsh exec "TEST_scopes.txt"
    netsh exec "TEST_reservations.txt"
  • Next step is to configure the the vlan on the core to ensure connectivity to the dhcp server
    from the configuration vlan:
    I used powershell to script the telnet session to the core using Windows Automation Snapin for Powershell
    <#
    Name: Scripted telnet session 
    Description: Configures VLAN, 
    Version: 1.0 - Jan, 2010
    
    This script is provided "AS IS" with no warranties and
    is not supported by the authors or deployment4everyone.
    WARNING if you do not fully understand this - it will break your network.
    Feel free to use and modify for your needs. 
    
    Author - Milos Pec
    Blog: http://deployment4everyone.blogspot.com
    #>
    cls
    Import-Module "c:\scripts\wasp.dll"
    
    ########################################################
    # Annoyance fix for x64 version of windows - lost path to telnet.exe ...
    # More info: http://www.skyeagle.net/blog/post/Missing-64-bit-native-commands-in-Powershell-%28x86%29.aspx
    if ( (Test-Path "$($env:systemroot)\sysnative") -and (($env:path).split(';') -notcontains "$($env:systemroot)\sysnative") ) {
        $env:path = "$($env:path);$($env:systemroot)\sysnative"
        Write-Host "Path updated to include: $($env:systemroot)\sysnative"
    } 
    ########################################################
    
    
    $switch = "COREIP"
    $user = "ADMINUSER"
    $pwd = ""
    
    #get arguments for the script
    if ($args[0]) {
            $netw = $args[0]
        }else{
            $netw = read-host "Enter second octet network number [10.XXX.40.0]"
    }
    
    if ($netw -eq "") {
        write-host "Panic - network number is needed ..." 
        } else {
    $_switch = read-host "Confirm switch IP [$switch]"
        if($_switch -eq ""){#Default
            }
        elseif($_switch -eq $null) {#Default
            }
        else{$switch = $_switch}
    
    $_user = read-host "Enter username [$user]"
        if($_user -eq ""){#Default
            }
        elseif($_user -eq $null) {#Default
            }
        else{$user = $_user}
    
    $pwd = read-host "Enter password" -AsSecureString
    $pwdenc = [Runtime.InteropServices.Marshal]
    $pwd = $pwdenc::PtrToStringAuto( $pwdenc::SecureStringToBSTR($pwd))
    Start telnet.exe $switch
    Start-Sleep -s 1
    Select-Window telnet | send-keys "~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "$user~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "$pwd~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "config ~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "vlan 40 ~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "no ip add ~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "ip add 10.$netw.40.1 255.255.255.0 ~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "untag c1-c24,d1-d24 ~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "write mem ~"
    Start-Sleep -s .2
    Select-Window telnet | send-keys "logout ~"
    Start-Sleep -s 1
    }
  • The switches can be turned on and they should get the correct IP address
  • The macro is setup to automatically start the TFTP server (I am using opensource TFTPD32) configured with source directory where all the switch configs are stored and then using again PowerShell scripted session to run simple commands to download the right configuration:
    copy tftp startup-config TFTP_IP 2600_SWITCHIP_ip.cfg
Next missing step is 3 hours on site replacing switches ... but that can be fun to :-).