[PowerShell] / Get-HpProductInfo.ps1 Repository:
ViewVC logotype

View of /Get-HpProductInfo.ps1

Parent Directory Parent Directory | Revision Log Revision Log


Revision 34 - (download) (annotate)
Tue Aug 19 12:39:12 2014 UTC (6 years, 2 months ago) by ian
File size: 44382 byte(s)
cleaned up a lot of the commented out unneeded code;
have not yet fully tested in PS 2.0 but I'm almost positive it needs some tweaking to run on it;
changed main program logic so that info is always read from XML even if it was just downloaded due to some issue passing back the object for devices where the serial number matches multiple regions;
<#



.NOTES

 Written by Ian Cammarata  http://ian.cammarata.us

 Get-HpProductInfo.ps1 v0.10

 

This will probably only work for products sold in the U.S. unless you change the country codes in $hpWarrantyQueryURL and the $htClient.Headers.set(...Cookie...



To Do:

 -Multi-threading the http downloads and multiple retry attemps (partsufer is so unreliable lately)

 -Generate a proper object (this will allow tab completion and easier programmatic use)



SNs for testing: CND120CJH0, CNU305BP9N, CNFKF52058, JPBCD3R1H3, CNU349BDTD





.SYNOPSIS

Query by SN to retreive HP product info from PartSufer and HP Business warranty lookup.





.EXAMPLE

PS> Get-HpProductInfo JPBCD3R1H3



#>



PARAM (

    #The serial number to query.

    [Parameter( Mandatory = $True, Position = 0, ValueFromPipeline= $True )]

    [Alias( "SN" )]

    [String[]]

    $SerialNumber,



    #Search string for parts listing.

    [Parameter( Position = 1 )]

    [Alias( "Part" )]

    $PartQuery,

    

    #Output an object instead of an plain text.

    [Switch]

    [Alias( "Object" )]

    $OutputObject,



    #Purge cash for supplied SN(s) and fetch fresh data

    [Switch]

    [Alias( "Purge" )]

    $Refresh

)





BEGIN {

    Set-StrictMode -Version Latest

    IF ( $Script:PSBoundParameters.ContainsKey("Debug") ) { $DebugPreference = "Continue" } ### For some reason I was getting first 3 debug lines output in the Process block even with using -Debug, but none afterwards



    ###### Set Constants

    IF ( -not ( Test-Path Variable:\\fullRegionName ) ) { #This prevents errors from being generated when running from ISE

        Set-Variable fullRegionName -Option Constant -value "United States"

        Set-Variable abbrRegionName -Option Constant -Value "US"

        Set-Variable regionSuffix -Option Constant -Value "#ABA"

        Set-Variable ForeignPartSuffixes -Option Constant -Value "-031|-041|-051|-061|-071|-081|-091|-131|-141|-161|-171|-201|-211|-251|-261|-271|-281|-291|-A41|-AB1|-AD1|-B31|-B71|-BA1|-BB1|-BG1|-D61|-DB1|-DD1|-DJ1|-FL1"

        Set-Variable OtherPartRejectionStrings -Option Constant -value "no longer supplied|Not Available|Hebrew|Cyrillic|Greek|Arabic|Bulk|220V|- AR|- CS|- DA|- NL|- FI|- DE|- EL|- HE|- HU|- KO|- NO|- PL|- ZHCN|- ZHTW|- TH|PCID|SWID| CH | CH<|CHINARUSS"

        Set-Variable productObjectVersion -option Constant -value 0.4

        Set-Variable partSurferQueryUrl -option Constant -value "http://partsurfer.hp.com/Search.aspx?searchText="

        Set-Variable hpWarrantyBaseUrl -option Constant -value "https://h20565.www2.hp.com"

        Set-Variable hpWarrantyStartPath -option Constant -value "/portal/site/hpsc/public/wc/home/"

        Set-Variable hpWarrantyQueryUrl -option Constant -value "https://h20566.www2.hp.com/portal/site/hpsc/template.PAGE/action.process/public/wc/home/?javax.portlet.action=true&javax.portlet.sync=d549c6d9a9acda7f8405adb432a15c01&javax.portlet.tpst=c4efedb99acca32ea782977bb053ce01&javax.portlet.prp_c4efedb99acca32ea782977bb053ce01=wsrp-interactionState%3Daction%253Dfind&javax.portlet.begCacheTok=com.vignette.cachetoken&javax.portlet.endCacheTok=com.vignette.cachetoken"

        Set-Variable cacheDir -option Constant -value (Join-Path $env:APPDATA "HpProductInfo_Cache")

        Set-Variable maxCacheAge -option Constant -value 90 #In Days

    }



    ###### Prep

    IF ( !( Test-Path $cacheDir ) ){ $null = New-Item -Type "Directory" -Path $cacheDir }



### This was causing issues with other PowerShell scripts.  It will remain disabled till I learn to fix it across all versions of PowerShell or I no longer need to support PS 1.0.

<#    ###### Default display property set #Maybe this will be added back in future, not really necessary anyway

    $objectTemplateFile = Join-Path $cacheDir "HpProductObjectTemplate_v$($productObjectVersion).ps1xml"

    IF ( !( Test-Path $objectTemplateFile ) ) {

        $template = "<?xml version='1.0' encoding='utf-8' ?>"

        $template += "<Types><Type><Name>System.Management.Automation.PSCustomObject</Name><Members><MemberSet>"

        $template += "<Name>PSStandardMembers</Name><Members><PropertySet><Name>DefaultDisplayPropertySet</Name><ReferencedProperties>"

        $template += "<Name>ProductNumber</Name>"

        $template += "<Name>SerialNumber</Name>"

        $template += "<Name>Description</Name>"

        $template += "<Name>WarrantyOnsite</Name>"

        $template += "<Name>Parts</Name>"

        $template += "</ReferencedProperties></PropertySet></Members></MemberSet></Members></Type></Types>"

        Out-File $objectTemplateFile -Encoding "UTF8" -InputObject $template -Force

    }

    IF ( $PSVersionTable.PSVersion.Major -gt 2 ) {

        Update-TypeData $objectTemplateFile

    }

    elseif ( ! ( $host.Runspace.RunspaceConfiguration.Types | ? { $_.FileName -eq $objectTemplateFile } ) ) {

        Update-TypeData $objectTemplateFile

    }

#>

}



PROCESS {

    Function CleanHtml($htData) {

        RETURN $htData -replace "(\r|\f|\n)*" -replace "\t", " " -replace "  " -replace "> <", "><" -replace "&nbsp;" -replace "<a .*?>" -replace "</a>"

    }

    Foreach ($SerialNumber in $SerialNumber) {

        $SerialNumber = $SerialNumber.Trim().ToUpper()



        Write-Debug ">> Check cache for XML copy of data"

        $useCache = $false

        IF ( $cacheHit = ls $cacheDir | ? { $_.Name -like "$($SerialNumber)_*" } ) {

            Write-Debug "Cache Hit!!! ($cacheHit)"

            $useCache = $true

            IF ( $Refresh ) {

                $useCache = $false

            }

            elseif ( $cacheHit -notlike "*_v$($productObjectVersion).xml" ) {

                Write-Debug "Version Mismatch; Deleting... :-("

                $useCache = $false

            }

            elseif ( ( Get-Date ) -gt $cacheHit.CreationTime.AddDays(90) ) {

                Write-Debug "Stale Cache; Deleting... :-("

                $useCache = $false

            }



            IF ( ! $useCache ) { Remove-Item ( Join-Path $cacheDir $cacheHit ) }

        }

        Write-Debug "Using Cache: $($useCache)"

        Write-Debug "<< End Check Cache"





        function downloadProductData () {

            Write-Debug "Load initial warranty lookup page to get ID cookies, run ahead of time for the session to settle on the server or w/e"

            $cookieContainer = New-Object System.Net.CookieContainer



            [net.httpWebRequest] $webReq = [net.webRequest]::create("$($hpWarrantyBaseURL)$($hpWarrantyStartPath)")

            $webReq.CookieContainer = $cookieContainer

            [net.httpWebResponse] $webResp = $webReq.GetResponse()

            $strRdr = New-Object IO.StreamReader($webResp.GetResponseStream())

            $htData = $strRdr.ReadToEnd()

            $webResp.Close()





            Write-Debug "Parse out POST action"

            $null = $htData -match "<form .*? name=`"warrantyCheck`" .*? action=`"([^`"]*)`""

            $hpWarrantyQueryPath = $Matches[1]





            Write-Debug "Create WebClient and cookie needed for PartSurfer"

            $htClient = New-Object System.Net.Webclient

            $htClient.Headers.Set([System.Net.HttpRequestHeader]::Cookie, "Country=$($fullRegionName)")



            Write-Debug "/!\Begin critical section/!\ Any errors here break the entire script."

            $ErrorActionPreference = "Stop"





            IF ( $Script:PSBoundParameters.ContainsKey("Debug") ) {

                Write-Debug "Debugging is enabled, using cached raw html if available"



                IF ( Test-Path ( Join-Path $cacheDir "$($SerialNumber).raw.html" ) ) {

                    Write-Debug "Raw HTML read from cache"

                    $htData = Get-Content (Join-Path $cacheDir "$($SerialNumber).raw.html")

                }

                ELSE {

                    Write-Debug "Downloading PartSurfer HTML data"

                    [net.httpWebRequest] $webReq = [net.webRequest]::create("$($partSurferQueryUrl)$($SerialNumber)")

                    [net.httpWebResponse] $webResp = $webReq.GetResponse()

                    $strRdr = New-Object IO.StreamReader($webResp.GetResponseStream())

                    $htData = $strRdr.ReadToEnd()

                    $webResp.Close()



                    $htData = CleanHtml( $htData )



                    Write-Debug "Raw HTML written to cache"

                    Set-Content (Join-Path $cacheDir "$($SerialNumber).raw.html") $htData

                }

            }

            ELSE {

                Write-Debug "Downloading PartSurfer HTML data"

                [net.httpWebRequest] $webReq = [net.webRequest]::create("$($partSurferQueryUrl)$($SerialNumber)")

                $webReq.CookieContainer = $cookieContainer

                [net.httpWebResponse] $webResp = $webReq.GetResponse()

                $strRdr = New-Object IO.StreamReader($webResp.GetResponseStream())

                $htData = $strRdr.ReadToEnd()

                $webResp.Close()



                $htData = CleanHtml( $htData )

            }

           

            Write-Debug "Check if page is asking to choose which region the product is from due to multiple serial number matches."

            IF ( $htData -match 'type="radio"' ) {

                Write-Debug "Attempting to select the correct product based on region suffix."

                $fullSku = ""

                IF ( $Script:PSBoundParameters.ContainsKey("Debug") ) {

                    Write-Debug "Debugging is enabled, renaming original cache file to ...please.select.raw.html."

                    Move-Item -Force ( Join-Path $cacheDir "$($SerialNumber).raw.html" ) ( Join-Path $cacheDir "$($SerialNumber).please.select.raw.html" )

                }



                $htData -match "`".......$($regionSuffix)`""

                IF ( $Matches.Count -eq 1 ) {

                    $fullSku = $Matches[0] -replace '"', ""

                    $partSurferRetries = 0

                    DO {

                        $partSurferRetries++

                        Write-Debug "Create POST request to search PartSurfer again with SKU ($($fullSku)). Attempt# $($partSurferRetries)"

                        Sleep 8



                        [net.httpWebRequest] $webReq = [net.webRequest]::create("$($partSurferQueryUrl)$($SerialNumber)")

                        $webReq.CookieContainer = $cookieContainer

                        $webReq.Method = "POST"

                        $webReq.Host = "partsurfer.hp.com"

                        $webReq.Referer = "http://partsurfer.hp.com/Search.aspx?SearchText=$($SerialNumber)"





                        Write-Debug "Looking for all form fields to include in POST header..."

                        $postData = ""

                        $postInputs = Select-String "<input.*?>" -inputObject $htData -AllMatches

                        $postInputs.Matches | ? { $_ -notmatch ".*(submit|image|radio).*" } | % {

                          $null = $_ -match ".*?name=`"(.*?)`".*?>"

                          $newData = "$($Matches[1])="



                          IF ( $_ -match ".*?value=`"(.*?)`".*?>" ) {

                            Write-Debug "$($newData) `"$( IF ( $Matches[1].Length -gt 65 ) { "$($Matches[1].Substring(0,55))...(truncated)" } ELSE { $Matches[1] } )`""

                            $newData += $Matches[1]

                          }



                          $postData += "$($newData)&"

                        }

                        $postData += "ctl00`$BodyContentPlaceHolder`$radProd=$($fullSku)"

                        $postData += "&ctl00`$BodyContentPlaceHolder`$btnProdSubmit= View Selected Product Details "

                        $postData += "&ctl00`$BodyContentPlaceHolder`$ddlCountry=$($abbrRegionName)"





                        Write-Debug "Post header complete.  Submitting..."

                        $postBuffer = [Text.Encoding]::Ascii.GetBytes($postData)

                        $webReq.ContentLength = $postBuffer.Length

                        $webReq.ContentType = "application/x-www-form-urlencoded"

                        $reqStr = $webReq.GetRequestStream()

                        $reqStr.Write( $postBuffer, 0, $postBuffer.Length)

                        $reqStr.Flush()

                        $reqStr.Close()



                        [net.httpWebResponse] $webResp = $webReq.GetResponse()

                        $strRdr = New-Object IO.StreamReader($webResp.GetResponseStream())

                        $htData = $strRdr.ReadToEnd()

                        $webResp.Close()



                        $htData = CleanHtml( $htData )



                        IF ( $Script:PSBoundParameters.ContainsKey("Debug") ) {

                            Write-Debug "Raw HTML written to cache"

                            Set-Content (Join-Path $cacheDir "$($SerialNumber).raw.html") $htData

                        }

                    } WHILE ( $htData -match 'type="radio"' )

                }

                ELSE { Write-Error "Multiple serial# matches, and we weren't able to determine the correct one." }                

            }



            Write-Debug "Create psObject representing the product"

            $hpProduct = New-Object psObject



            Write-Debug "Load generic sku data into the object"

            Write-Debug "Load SerialNumber"

            $hpProduct | Add-Member NoteProperty SerialNumber $SerialNumber



            Write-Debug "Load ProductNumber"

            $null = $htData -match "<span id=`"ctl00_BodyContentPlaceHolder_lblProductNumber`">(.*?)</span>"

            $hpProduct | Add-Member NoteProperty ProductNumber $Matches[1]



            Write-Debug "Load Description"

            $null = $htData -match "<span id=`"ctl00_BodyContentPlaceHolder_lblDescription`">(.*?)</span>"

            $hpProduct | Add-Member NoteProperty Description $Matches[1]



            $ErrorActionPreference = "Continue"

            Write-Debug "/!\End critical section/!\"







            Write-Debug "Add Spare and OEM parts to psObject (Check boxes are no longer supplied by the site to determine part availability)"

            $hpProduct | Add-Member NoteProperty OemParts (New-Object psObject)

            $hpProduct | Add-Member NoteProperty Spares (New-Object psObject)



            $trMatches = Select-String "<tr class=`"[^`"]*RowStyle`">.*?</tr>" -inputObject $htData -AllMatches

            foreach ( $trMatch in $trMatches.Matches ){

                IF ( $trMatch.Value -notmatch ".*($($ForeignPartSuffixes)|$($OtherPartRejectionStrings)).*" )

                {

                    $spanMatches = Select-String "<span.*?>(.*?)</span>" -inputObject $trMatch.Value -AllMatches

                    $partObject = New-Object psObject

                    $partObject | Add-Member NoteProperty PartNumber $spanMatches.Matches[0].Groups[1].Value

                    $partObject | Add-Member NoteProperty Description $spanMatches.Matches[1].Groups[1].Value

                    

                    $BomType = "OemParts"

                    IF ( $trMatch.Value -match ".*_gridSpareBOM_.*" ) { $BomType = "Spares" }



                    $ErrorActionPreference = "SilentlyContinue"

                    $hpProduct.$BomType | Add-Member NoteProperty $partObject.PartNumber $partObject

                    $ErrorActionPreference = "Continue"

                    Write-Debug "$($BomType): $($partObject.PartNumber) - $($partObject.Description)"

                }

                ELSE { Write-Debug "Discarding invalid or no longer supplied part." }

            }

            

            $warrAttempts = 0

            $trMatches = ""

            DO {

                sleep ( 10 + $warrAttempts * 15 )

                Write-Debug "Create POST request to retrieve warranty data. Attempt #$($warrAttempts)"

                [net.httpWebRequest] $webReq = [net.webRequest]::create("$($hpWarrantyBaseURL)$($hpWarrantyQueryPath)")

                $webReq.CookieContainer = $cookieContainer

                $webReq.Method = "POST"



                $postData = "rows[0].item.serialNumber=$($SerialNumber)&rows[0].item.countryCode=$($abbrRegionName)&rows[1].item.serialNumber=&rows[1].item.countryCode=US&rows[2].item.serialNumber=&rows[2].item.countryCode=US&rows[3].item.serialNumber=&rows[3].item.countryCode=US&rows[4].item.serialNumber=&rows[4].item.countryCode=US&rows[5].item.serialNumber=&rows[5].item.countryCode=US&rows[6].item.serialNumber=&rows[6].item.countryCode=US&rows[7].item.serialNumber=&rows[7].item.countryCode=US&rows[8].item.serialNumber=&rows[8].item.countryCode=US&rows[9].item.serialNumber=&rows[9].item.countryCode=US&submitButton=Submit"

                $postBuffer = [Text.Encoding]::Ascii.GetBytes($postData)

                $webReq.ContentLength = $postBuffer.Length

                $webReq.ContentType = "application/x-www-form-urlencoded"

                $reqStr = $webReq.GetRequestStream()

                $reqStr.Write( $postBuffer, 0, $postBuffer.Length)

                $reqStr.Flush()

                $reqStr.Close()



                [net.httpWebResponse] $webResp = $webReq.GetResponse()

                $strRdr = New-Object IO.StreamReader($webResp.GetResponseStream())

                $htData = $strRdr.ReadToEnd()

                $webResp.Close()



                $htData = CleanHtml( $htData )

                $warrAttempts++

                $trMatches = Select-String "<tr class='hpui-(normal|alternate)-row'.*?</tr>" -inputObject $htData -AllMatches

            }

            WHILE ( $warrAttempts -lt 6 -and -not $trMatches )



            Write-Debug "Parse warranty data and add to the object"

            $hpProduct | Add-Member NoteProperty Warranties (New-Object psObject)

            

            FOREACH ( $trMatch in $trMatches.Matches ) {

                $tdMatches = Select-String "<td((?!rowspan|color).)*?</td>" -inputObject $trMatch -AllMatches

  

                $warrData = $tdMatches.Matches | % {

                    $_.value -replace "</?td[^>]*>" -replace "Wty: " -replace "HP " -replace "HW Maintenance ", "HWM " -replace "Support ?(for )?"

                }



                IF ( $warrData[1].Length -or $warrData[2].Length ) {

                    $warrObject = New-Object psObject

                    $warrObject | Add-Member NoteProperty Start ( '{0:yyyy-MM-dd}' -f ( Get-Date $warrData[1] ) )

                    $warrObject | Add-Member NoteProperty End ( '{0:yyyy-MM-dd}' -f ( Get-Date $warrData[2] ) )

                    $hpProduct.Warranties | Add-Member NoteProperty $warrData[0] $warrObject

                    Write-Debug "Added Warranty: $($warrData[0]) - $( '{0:yyyy-MM-dd}' -f ( Get-Date $warrData[1] ) ) to $( '{0:yyyy-MM-dd}' -f ( Get-Date $warrData[2] ) )"

                }

                ELSE { $hpProduct.Warranties | Add-Member NoteProperty $warrData[0] "" }

            }

          



            $hpProduct | Add-Member NoteProperty __QueryDate (Get-Date)

            $hpProduct | Add-Member NoteProperty __productObjectVersion $productObjectVersion



            $hpProduct | Export-Clixml (Join-Path $cacheDir "$($hpProduct.SerialNumber)_$($hpProduct.ProductNumber)_v$($productObjectVersion).xml")

        }





        #Import line is potentially error prone, make it more specific later on

        IF ( -not $useCache ) { downloadProductData }

        $hpProduct = Import-Clixml ( Join-Path $cacheDir "$($SerialNumber)_*" )



        IF ( $OutputObject ) {

            $hpProduct

        }

        ELSE {

            $lineLen = 75



            "%" * $lineLen



            "Serial Number:`t$($hpProduct.SerialNumber)"

            "Product Number:`t$($hpProduct.ProductNumber -replace '#ABA' )"

            "Description:`t$($hpProduct.Description)"

            ""

            



            $partsTxt = @()

            $warranties = $hpProduct.Warranties | get-member -MemberType "NoteProperty"

            foreach ( $warranty in $warranties | % { $_.Name } ) {

                IF ( ($hpProduct.Warranties.$warranty | Get-Member | % { $_.Name } ) -contains "Start" ) {

                    $partsTxt += "$($warranty)  `t$($hpProduct.Warranties.$warranty.Start)`t$($hpProduct.Warranties.$warranty.End)"

                }

                ELSE { $partsTxt += $warranty }

            }

            "Warranties: $($partsTxt.Length)"

            "%" * $lineLen

            $partsTxt



            ""

            $parts = $hpProduct.OemParts | get-member -MemberType "NoteProperty"

            $partsTxt = @()

            foreach ( $part in $parts | % { $_.Name } ) {

                IF ( $PartQuery ) {

                    IF ( $hpProduct.OemParts.$part.Description -match ".*$($PartQuery).*" ) {

                        $partsTxt += "$($hpProduct.OemParts.$part.PartNumber) - $($hpProduct.OemParts.$part.Description)"

                    }

                }

                ELSE { $partsTxt += "$($hpProduct.OemParts.$part.PartNumber) - $($hpProduct.OemParts.$part.Description)" }

            }

            "OEM Parts$(IF ( $PartQuery ) {" matching query '$($PartQuery)'"}): $($partsTxt.Length)"

            "%" * $lineLen

            $partsTxt





            ""

            $parts = $hpProduct.Spares | get-member -MemberType "NoteProperty"

            $partsTxt = @()

            foreach ( $part in $parts | % { $_.Name } ) {

                IF ( $PartQuery ) {

                    IF ( $hpProduct.Spares.$part.Description -match ".*$($PartQuery).*" ) {

                        $partsTxt += "$($hpProduct.Spares.$part.PartNumber) - $($hpProduct.Spares.$part.Description)"

                    }

                }

                ELSE { $partsTxt += "$($hpProduct.Spares.$part.PartNumber) - $($hpProduct.Spares.$part.Description)" }  

            }

            "Spares$(IF ( $PartQuery ) {" matching query '$($PartQuery)'"}): $($partsTxt.Length)"

            "%" * $lineLen

            $partsTxt

            

            "%"*$lineLen

        }

    }

}

Contact
ViewVC Help
Powered by ViewVC 1.0.4