Best way to handle large amount of nested data in powershell? - powershell

I'm trying to automate the logfiles verification of our daily batch.
I want to check around 20 servers that may have up to 30 batch, each batch can generate one or more logfiles and I want to validate them using several criteria.
So this ends up with a quite huge amount of data.
My first though was to use nested arrays and hashtables then create psobject like this :
$servers=#(
#{
"name"="server1";
"credential"="domain\user";
"batch"=#(
#{"batchName"="test";"path"="e:\cit\sauvegarde\batch\recup.cmd";"comment"="batch de test";"schedule"="lundi,mardi ";
"validations"=#(
#{"name"="log exist";"path"="\\smacweb\e$\cit\test.log";"filter"=#("NotNull";"NotOlderThan,2") };
#{"name"="no erros";"path"="\\smacweb\CIT\sauvegarde\logs\*.log";"filter"=#("NotContains,'error'") };
)};
#{"batchName"="mysql";"comment"="dump des bases mysql";"schedule"="lundi,mardi,vendredi";
"validations"=#(
#{"name"="log exist";"path"="\\smacweb\e$\mysqldump\dump.zip";"filter"=#("NotNull";"NotOlderThan,2") };
#{"name"="zipOK";"path"="\\smacweb\e$\mysqldump\dump.zip";"filter"=#("Test-Zip") };
)};
)
};
# #{
# "name"="server2";
# "credential"="domain\user2";
# "batch"=#(
# #{"batchName"=.....};
# )};
)
$srv=#()
$servers | % {
$srv+= New-Object -TypeName psobject -Property $_;
}
This is a small example but I guess it will quickly become hardly readable.
So what would be a better way to do this? Use xml (not familiar to me), use external database, other method?

Caution: I'm a noob when it comes to xml, but I gave it a try for fun. This is just an example of how you could navigate through the xml-file. :)
XML file (test.xml):
<?xml version="1.0" encoding="utf-8"?>
<servers>
<server>
<servername>server1</servername>
<credential>domain\user</credential>
<batches>
<batch>
<batchname>test</batchname>
<batchpath>e:\cit\sauvegarde\batch\recup.cmd</batchpath>
<comment>batch de test</comment>
<schedule>
<day>lundi</day>
<day>mardi</day>
</schedule>
<validations>
<validation>
<name>log exist</name>
<path>\\smacweb\e$\cit\test.log</path>
<filters>
<filter>NotNull</filter>
<filter>NotOlderThan,2</filter>
</filters>
</validation>
<validation>
<name>no erros</name>
<path>\\smacweb\CIT\sauvegarde\logs\*.log</path>
<filters>
<filter>NotContains,'error'</filter>
</filters>
</validation>
</validations>
</batch>
<batch>
<batchname>mysql</batchname>
<comment>dump des bases mysql</comment>
<schedule>
<day>lundi</day>
<day>mardi</day>
<day>vendredi</day>
</schedule>
<validations>
<validation>
<name>log exist</name>
<path>\\smacweb\e$\mysqldump\dump.zip</path>
<filters>
<filter>NotNull</filter>
<filter>NotOlderThan,2</filter>
</filters>
</validation>
<validation>
<name>zipOK</name>
<path>\\smacweb\e$\mysqldump\dump.zip</path>
<filters>
<filter>Test-Zip</filter>
</filters>
</validation>
</validations>
</batch>
</batches>
</server>
</servers>
Powershell-script to loop through servers and batches:
function test {
$xml = [xml](Get-Content C:\Users\Frode\Desktop\test.xml)
$servers = $xml.SelectNodes("/servers/server")
foreach ($server in $servers) {
$batches = $server.SelectNodes("batches/batch")
Write-Host "Server: $($server.servername)"
foreach ($batch in $batches) {
Write-Host "Checking batch: $($batch.batchname)"
}
}
}
Output:
PS-ADMIN C:\Windows\system32> test
Server: server1
Checking batch: test
Checking batch: mysql

Related

Not able to Get the Last node of an xml node properly

I am having 2 diffrent xml file with. Currently im trying to get the Last xml node. In the 1st ouput im getting because empty or Null value since i have a commented line. Please help how to handle in this case.
#Below line Run firstfile.xml
$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\firstfile.xml'
#Below line to Run secondfile.xml
#$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\secondfile.xml'
$xmllastsitenode = [xml]::new()
$xmllastsitenode.Load($xmlFile)
$lastsite_id = $xmllastsitenode.fccconfig.fccdefaults.LastChild.id
write-host 'Print1'
write-host $lastsite_id
if (!$lastsite_id) { Write-Host "variable is null" }
if ($lastsite_id) { Write-Host "variable is NOT null" }
write-host 'Print2'
Below is the xml files im trying to run
1st XML file
<?xml version="1.0" encoding="UTF-8"?>
<fccconfig version="1.3.2">
<fccdefaults>
<property name="CacheLocation" value="C:/Users/Public/" overridable="true"/>
<site id="-1940805554" overridable="true">
<parentfsc address="http://abcdefgh:1234/" priority="0" />
</site>
<!--__ANT_MARK__-->
</fccdefaults>
<!-- default parentfsc - this is a marker that will be overwritten by the installer -->
<parentfsc address="xyzlmnopq:10010" priority="0" transport="lan"/>
</fccconfig>
Output:-
Print1
variable is null
Print2
2nd XML file
<?xml version="1.0" encoding="UTF-8"?>
<fccconfig version="1.3.2">
<fccdefaults>
<property name="CacheLocation" value="C:/Users/Public/" overridable="true"/>
<site id="-1940805554" overridable="true">
<parentfsc address="http://abcdefgh:1234/" priority="0" />
</site>
</fccdefaults>
<!-- default parentfsc - this is a marker that will be overwritten by the installer -->
<parentfsc address="xyzlmnopq:10010" priority="0" transport="lan"/>
</fccconfig>
Output:-
Print1
-1940805554
variable is NOT null
Print2
Latest Code which have tried meantime
#Below line Run firstfile.xml
$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\firstfile.xml'
#Below line to Run secondfile.xml
#$xmlfile = 'C:\Programs\MyTasks\TCPROD-29658\Test_1309\D_SimulationTest\secondfile.xml'
$xmllastsitenode = [xml]::new()
$xmllastsitenode.Load($xmlFile)
$lastsite_id = $xmllastsitenode.fccconfig.fccdefaults.LastChild.id
write-host 'Print1'
write-host $lastsite_id
if (!$lastsite_id)
{
Write-Host "variable is null"
# New logic to handle firt xml file cases
#$somesitetag ---> Having some xml tag content here
$newSiteNode = $xmlcontent.ImportNode($somesitetag, $true)
$antmarkline = (Get-Content $xmlfile) | select-string -Pattern $antmark
$xmlcontent.fccconfig.fccdefaults.InsertAfter($newSiteNode,$antmarkline)
}
if ($lastsite_id)
{
Write-Host "variable is NOT null"
#Insterting some new nodes
#This Working as second file conetent works here
}
write-host 'Print2'
You need to ignore the comments. Check the output of this for the first XML file.
$settings = [System.Xml.XmlReaderSettings]::new()
$settings.IgnoreComments = $true
$xmlreader = [System.Xml.XmlReader]::Create('./Desktop/Test.xml', $settings)
$xmldoc = [System.Xml.XmlDocument]::new()
$xmldoc.Load($xmlreader)
$xmldoc.fccconfig.fccdefaults.LastChild

pass json as parameter in power shell script

I have made a function for creating new xml node.there are two parameters in my function on is a existing xml file reference and second one is element value.while running the script its showing an error
code
function createProviderNode($xmlData,$propertyValue){
Write-Host 'inside createProviderNode'
Write-Host ($propertyValue)
#[xml]$xmlData = get-content E:\powershell\data.xml
$newProviderNode = $xmlData.CreateNode("element","provider","")
$newProviderNode.SetAttribute("name",$propertyValue)
$xmlData.SelectSingleNode('providers').AppendChild($newProviderNode)
$xmlData.save("E:\powershell\data.xml")
}
did i miss anything in this code?
The error message implies that while you expected $xmlData to contain an object of type [xml] (System.Xml.XmlDocument) - i.e., an XML document - in reality it was a string ([string]).
In other words: When you called your createProviderNode function, the 1st argument you passed was a string, not an XML document (of type [xml]).
Typing your $xmlData parameter variable as [xml] solves this problem, as that will implicitly covert even a string argument to an XML document on demand - if possible.
A simplified example, using a script block in lieu of a function:
$xmlString = #'
<?xml version="1.0"?><catalog><book id="bk101"><title>De Profundis</title></book></catalog>
'#
# Note how $xmlData is [xml]-typed.
& { param([xml] $xmlData) $xmlData.catalog.book.title } $xmlString
The above yields De Profundis, demonstrating that the string argument was converted to an [xml] instance (which - thanks to PowerShell's type adaptation magic - makes the element names available as direct properties).
It is then safe to call the .CreateNode() method on $xmlData.
Well, you don't show your original XML format.
Why did you comment out that Get-Content? it will not work without it.
So, if we take the below example, it works as expected.
# Simple XML version
$SimpleXml = $null
$SimpleXml = #"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<name>Apple</name>
<size>1234</size>
</configuration>
"#
# New node code
[xml]$XmlDoc = Get-Content -Path variable:\SimpleXml
$runtime = $XmlDoc.CreateNode("element","runtime","")
$generated = $XmlDoc.CreateNode("element","generatePublisherEvidence","")
$generated.SetAttribute("enabled","false")
$runtime.AppendChild($generated)
$XmlDoc.configuration.AppendChild($runtime)
$XmlDoc.save("$pwd\SimpleXml.xml")
Get-Content -Path "$pwd\SimpleXml.xml"
# Which creates this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<name>Apple</name>
<size>1234</size>
<runtime>
<generatePublisherEvidence enabled="false" />
</runtime>
</configuration>
Also Write-Host is never needed unless you are coloring screen output.
Write-Output is the default and automatically write to the screen, whether you specify Write-Output or not.
So, all of these to the same thing - output to the screen.
$SomeString = 'hello'
Write-Host $SomeString
Write-Output $SomeString
'hello'
"hello"
$SomeString
"$SomeString"
($SomeString)
("$SomeString")
$($SomeString)
# Results
hello
hello
hello
hello
hello
hello
hello
… yet, it's your choice.

PowerShell: XML to Object

I read out data from a XML-File and want to get an object like this:
TaskSequenceNumber TaskSequenceName
------------------ ----------------
1 01_Base
2 02_ABC
The XML-File Looks like this:
$xml = [xml]#"
<?xml version="1.0" encoding="utf-8"?>
<DeploymentScript>
<Settings>
...
</Settings>
<TaskSequences>
<Sequence1>
<TaskSequenceNumber>1</TaskSequenceNumber>
<TaskSequenceName>01_Base</TaskSequenceName>
</Sequence1>
<Sequence2>
<TaskSequenceNumber>2</TaskSequenceNumber>
<TaskSequenceName>02_ABC</TaskSequenceName>
</Sequence2>
</TaskSequences>
</DeploymentScript>
"#
$xml.DeploymentScript.TaskSequences
The code above is prepared for easy copy & paste into ISE, if anybody wants to try it.
About the name of the nodes Sequence1 and Sequence2 I am not sure yet. Maybe they could be renamed to $TaskSequenceName or $TaskSequenceNumber.
.
EDIT:
I am really sorry, but I need to reopen my request about it.
I was trying to implement the posted solution of Martin, but I came into trouble. Look at the following example:
Clear-Host
Function Simple-Test {
Param
(
[Parameter(Mandatory=$true,position=0)]
[string]$ControlObject
)
$ControlObject | Format-Table
}
$xml = [xml]#"
<?xml version="1.0" encoding="utf-8"?>
<DeploymentScript>
<Settings>
...
</Settings>
<TaskSequences>
<Sequence1>
<TaskSequenceNumber>1</TaskSequenceNumber>
<TaskSequenceName>01_Base</TaskSequenceName>
</Sequence1>
<Sequence2>
<TaskSequenceNumber>2</TaskSequenceNumber>
<TaskSequenceName>02_ABC</TaskSequenceName>
</Sequence2>
</TaskSequences>
</DeploymentScript>
"#
Simple-Test -ControlObject $xml.DeploymentScript.TaskSequences.GetEnumerator()
The script won't work as I would expect. I get the result:
System.Xml.XmlElement System.Xml.XmlElement
How to correct this?
Thank you!
You will get the output by calling the GetEnumerator() method:
$xml.DeploymentScript.TaskSequences.GetEnumerator()
Output:
TaskSequenceNumber TaskSequenceName
------------------ ----------------
1 01_Base
2 02_ABC

HP Warranty Lookup using PowerShell SOAP

Up to recently the web scraping of HP warranty information worked fine, however in the last couple of weeks they have changed the web page and it seems they are using POST rather than GET now on the new page, meaning I can't really just pass the information to the URL.
I did find some hope in a solution on this page:
http://ocdnix.wordpress.com/2013/03/14/hp-server-warranty-via-the-isee-api/
But I don't understand the example script as I have not worked with Python before.
I also found this web page that shows me a good example for Dell warranty, But HP don't have a WSDL that I could work with. (I would post the link but don't have enough rep)
Using these functions I can form the request:
http://www.iislogs.com/steveschofield/execute-a-soap-request-from-powershell
I think everything will work as expected but I am struggling to form the request to register the client, I keep getting 500 errors which either means their server is broken or my request is causing an internal error.
I am for the first time completely at a loss with this.
Anybody got this to work in PowerShell or having similar issues?
Updated 22-10-13
I now have the envelope, I managed to get the Python script to run but that failed because of my proxy, so extracted the generated XML which is what I wanted as I wasn't sure how it should be formed, this looks like:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iseeReg="http://www.hp.com/isee/webservices/">
<SOAP-ENV:Body>
<iseeReg:RegisterClient2>
<iseeReg:request><isee:ISEE-Registration xmlns:isee="http://www.hp.com/schemas/isee/5.00/event" schemaVersion="5.00">
<RegistrationSource>
<HP_OOSIdentifiers>
<OSID>
<Section name="SYSTEM_IDENTIFIERS">
<Property name="TimestampGenerated" value="2013/10/22 09:40:35 GMT Standard Time"/>
</Section>
</OSID>
<CSID>
<Section name="SYSTEM_IDENTIFIERS">
<Property name="CollectorType" value="MC3"/>
<Property name="CollectorVersion" value="T05.80.1 build 1"/>
<Property name="AutoDetectedSystemSerialNumber" value="10"/>
<Property name="SystemModel" value="HP ProLiant"/>
<Property name="TimestampGenerated" value="2013/10/22 09:40:35 GMT Standard Time"/>
</Section>
</CSID>
</HP_OOSIdentifiers>
<PRS_Address>
<AddressType>0</AddressType>
<Address1/>
<Address2/>
<Address3/>
<Address4/>
<City/>
<Region/>
<PostalCode/>
<TimeZone/>
<Country/>
</PRS_Address>
</RegistrationSource>
<HP_ISEECustomer>
<Business/>
<Name/>
</HP_ISEECustomer>
<HP_ISEEPerson>
<CommunicationMode>255</CommunicationMode>
<ContactType/>
<FirstName/>
<LastName/>
<Salutation/>
<Title/>
<EmailAddress/>
<TelephoneNumber/>
<PreferredLanguage/>
<Availability/>
</HP_ISEEPerson>
</isee:ISEE-Registration></iseeReg:request>
</iseeReg:RegisterClient2>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I then built a Powershell script that executes like this:
function Execute-SOAPRequest {
Param (
[Xml]$SOAPRequest,
[String]$URL,
[switch]$UseProxy
)
write-host "Sending SOAP Request To Server: $URL"
$soapWebRequest = [System.Net.WebRequest]::Create($URL)
$soapWebRequest.Headers.Add("SOAPAction",'"http://www.hp.com/isee/webservices/RegisterClient2"')
$soapWebRequest.ContentType = 'text/xml; charset=utf-8'
$soapWebRequest.Accept = "text/xml"
$soapWebRequest.Method = "POST"
$soapWebRequest.UserAgent = 'RemoteSupport/A.05.05 - gSOAP/2.7'
#$soapWebRequest.ServicePoint.Expect100Continue = $False
#$soapWebRequest.ServicePoint.MaxIdleTime = 2000
$soapWebRequest.ProtocolVersion = [system.net.httpversion]::version10
if($UseProxy){
$soapWebRequest.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
}
write-host "Initiating Send."
$requestStream = $soapWebRequest.GetRequestStream()
$SOAPRequest.Save($requestStream)
$requestStream.Close()
write-host "Send Complete, Waiting For Response."
$resp = $soapWebRequest.GetResponse()
$responseStream = $resp.GetResponseStream()
$soapReader = [System.IO.StreamReader]($responseStream)
$ReturnXml = [Xml]$soapReader.ReadToEnd()
$responseStream.Close()
write-host "Response Received."
return $ReturnXml
}
$SOAPRequest = [Xml](Get-Content 'C:\Temp\SoapEnv.xml')
$URL = 'https://services.isee.hp.com/ClientRegistration/ClientRegistrationService.asmx'
Execute-SOAPRequest $SOAPRequest $URL -UseProxy
But now I am getting additional errors rather than the 500 I was getting so getting closer?? Error details are:
Exception calling "GetRequestStream" with "0" argument(s): "The server committed a protocol violation. Section=ResponseStatusLine"
At C:\Temp\HP Register Client.ps1:29 char:54
+ $requestStream = $soapWebRequest.GetRequestStream <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
try this. . works for me just fine: Note: Only the request tag needs to be escaped.
$SOAPRequest= [xml]#"
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iseeReg="http://www.hp.com/isee/webservices/">
<SOAP-ENV:Body>
<iseeReg:RegisterClient2>
<iseeReg:request><isee:ISEE-Registration xmlns:isee="http://www.hp.com/schemas/isee/5.00/event" schemaVersion="5.00">
<RegistrationSource>
<HP_OOSIdentifiers>
<OSID>
<Section name="SYSTEM_IDENTIFIERS"&g`enter code here`t;
<Property name="TimestampGenerated" value="2013/12/06 14:04:24 EST"/>
</Section>
</OSID>
<CSID>
<Section name="SYSTEM_IDENTIFIERS">
<Property name="CollectorType" value="MC3"/>
<Property name="CollectorVersion" value="T05.80.1 build 1"/>
<Property name="AutoDetectedSystemSerialNumber" value="10"/>
<Property name="SystemModel" value="HP ProLiant"/>
<Property name="TimestampGenerated" value="2013/12/06 14:04:24 EST"/>
</Section>
</CSID>
</HP_OOSIdentifiers>
<PRS_Address>
<AddressType>0</AddressType>
<Address1/>
<Address2/>
<Address3/>
<Address4/>
<City/>
<Region/>
<PostalCode/>
<TimeZone/>
<Country/>
</PRS_Address>
</RegistrationSource>
<HP_ISEECustomer>
<Business/>
<Name/>
</HP_ISEECustomer>
<HP_ISEEPerson>
<CommunicationMode>255</CommunicationMode>
<ContactType/>
<FirstName/>
<LastName/>
<Salutation/>
<Title/>
<EmailAddress/>
<TelephoneNumber/>
<PreferredLanguage/>
<Availability/>
</HP_ISEEPerson>
</isee:ISEE-Registration></iseeReg:request>
</iseeReg:RegisterClient2>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
"#
You'll have to do the same thing for the warranty xml like so then just re-run the soap web request.
$hpgdid = $ReturnXml.envelope.body.RegisterClient2Response.RegisterClient2Result.Gdid
$hptoken = $ReturnXml.envelope.body.RegisterClient2Response.RegisterClient2Result.registrationtoken
$warrantyxml = [xml]#"
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:isee="http://www.hp.com/isee/webservices/">
<SOAP-ENV:Header>
<isee:IseeWebServicesHeader>
<isee:GDID>$hpgdid</isee:GDID>
<isee:registrationToken>$hptoken</isee:registrationToken>
<isee:OSID/>
<isee:CSID/>
</isee:IseeWebServicesHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<isee:GetOOSEntitlementList2>
<isee:request>
<isee:ISEE-GetOOSEntitlementInfoRequest
xmlns:isee="http://www.hp.com/schemas/isee/5.00/entitlement"
schemaVersion="5.00">
<HP_ISEEEntitlementParameters>
<CountryCode>ES</CountryCode>
<SerialNumber>CZ10130050</SerialNumber>
<ProductNumber>519841-425</ProductNumber>
<EntitlementType></EntitlementType>
<EntitlementId></EntitlementId>
<ObligationId></ObligationId>
</HP_ISEEEntitlementParameters>
</isee:ISEE-GetOOSEntitlementInfoRequest>
</isee:request>
</isee:GetOOSEntitlementList2>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
"#
$xmlstring = [string]$ReturnXml.Envelope.Body.GetOOSEntitlementList2Response.GetOOSEntitlementList2Result.Response
$warranty_info = [xml]#"
$xmlstring
"#
$warranty_info."ISEE-GetOOSEntitlementInfoResponse"
I have built this into a PowerShell Module, I moved the SOAP Requests to separate XML files, and used CDATA to make them much more readable. It works pretty well for me. it can be found on GitHub:
https://github.com/dotps1/HPWarranty
Thanks for all of the info on this page, really helped!
You need to escape the XML being transferred like below. Also make sure that the TimestampGenerated is formatted properly as well.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:iseeReg="http://www.hp.com/isee/webservices/">
<SOAP-ENV:Body>
<iseeReg:RegisterClient2>
<iseeReg:request><isee:ISEE-Registration xmlns:isee="http://www.hp.com/schemas/isee/5.00/event" schemaVersion="5.00">
<RegistrationSource>
<HP_OOSIdentifiers>
<OSID>
<Section name="SYSTEM_IDENTIFIERS">
<Property name="TimestampGenerated" value="2013/12/05 19:24:58 GMT"/>
</Section>
</OSID>
<CSID>
<Section name="SYSTEM_IDENTIFIERS">
<Property name="CollectorType" value="MC3"/>
<Property name="CollectorVersion" value="T05.80.1 build 1"/>
<Property name="AutoDetectedSystemSerialNumber" value="10"/>
<Property name="SystemModel" value="HP ProLiant"/>
<Property name="TimestampGenerated" value="2013/12/05 19:24:58 GMT"/>
</Section>
</CSID>
</HP_OOSIdentifiers>
<PRS_Address>
<AddressType>0</AddressType>
<Address1/>
<Address2/>
<Address3/>
<Address4/>
<City/>
<Region/>
<PostalCode/>
<TimeZone/>
<Country/>
</PRS_Address>
</RegistrationSource>
<HP_ISEECustomer>
<Business/>
<Name/>
</HP_ISEECustomer>
<HP_ISEEPerson>
<CommunicationMode>255</CommunicationMode>
<ContactType/>
<FirstName/>
<LastName/>
<Salutation/>
<Title/>
<EmailAddress/>
<TelephoneNumber/>
<PreferredLanguage/>
<Availability/>
</HP_ISEEPerson>
</isee:ISEE-Registration></iseeReg:request>
</iseeReg:RegisterClient2>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Another option instead of escaping the XML yourself would be to place it within a CDATA so that it isn't parsed: http://www.w3schools.com/xml/xml_cdata.asp
Combining all the information I found all over the web, I've created the following function that does it all based on the Serial Number. Special thanks to dotps1 for his hard work.
For HP:
Function Get-HPAssetInformationHC {
[CmdletBinding()]
Param (
[Parameter(Mandatory,ValueFromPipeline)]
[String]$SerialNumber
)
Begin {
Function Invoke-HPIncSOAPRequest {
Param (
[Parameter(Mandatory)]
[Xml]$SOAPRequest,
[String]$Url = 'https://api-uns-sgw.external.hp.com/gw/hpit/egit/obligation.sa/1.1'
)
$soapWebRequest = [System.Net.WebRequest]::Create($URL)
$soapWebRequest.Headers.Add('X-HP-SBS-ApplicationId','hpi-obligation-hpsa')
$soapWebRequest.Headers.Add('X-HP-SBS-ApplicationKey','ft2VGa2hx9j$')
$soapWebRequest.ContentType = 'text/xml; charset=utf-8'
$soapWebRequest.Accept = 'text/xml'
$soapWebRequest.Method = 'POST'
try {
$SOAPRequest.Save(($requestStream = $soapWebRequest.GetRequestStream()))
$requestStream.Close()
$responseStream = ($soapWebRequest.GetResponse()).GetResponseStream()
[XML]([System.IO.StreamReader]($responseStream)).ReadToEnd()
$responseStream.Close()
}
catch {
throw $_
}
}
}
Process {
foreach ($S in $SerialNumber) {
$request = #"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:int="http://interfaces.obligation.sbs.it.hp.com/">
<soapenv:Header />
<soapenv:Body>
<int:retrieveServiceObligationResponsesByServiceObligationRequests>
<context>
<appContextName>HPSF</appContextName>
<userLocale>en-US</userLocale>
</context>
<obligationRequests>
<lnkServiceObligationDepthFilter>
<includeProductObjectOfServiceInstance>true</includeProductObjectOfServiceInstance>
<includeServiceObligation>true</includeServiceObligation>
<includeServiceObligationHeaderOffer>true</includeServiceObligationHeaderOffer>
<includeServiceObligationMessage>true</includeServiceObligationMessage>
<maxNumberOfProductObjectOfServiceInstance>100</maxNumberOfProductObjectOfServiceInstance>
</lnkServiceObligationDepthFilter>
<lnkServiceObligationEnrichment>
<iso2CountryCode>US</iso2CountryCode>
</lnkServiceObligationEnrichment>
<lnkServiceObligationProductObjectOfServiceIdentifier>
<hpSerialNumber>$S</hpSerialNumber>
</lnkServiceObligationProductObjectOfServiceIdentifier>
</obligationRequests>
</int:retrieveServiceObligationResponsesByServiceObligationRequests>
</soapenv:Body>
</soapenv:Envelope>
"#
Try {
[XML]$entitlement = Invoke-HPIncSoapRequest -SOAPRequest $request -ErrorAction Stop
}
Catch {
$P = $_
$Global:Error.RemoveAt(0)
throw "Failed to invoke SOAP request: $P"
}
Try {
if ($entitlement) {
$HPAsset = $entitlement.Envelope.Body.retrieveServiceObligationResponsesByServiceObligationRequestsResponse.return
[PSCustomObject][Ordered]#{
SerialNumber = $S
ProductNumber = $HPAsset.lnkProductObjectOfServiceInstance.ProductNumber
SalesOrderNumber = $HPAsset.lnkServiceObligations.salesOrderNumber | where {$_}
ProductDescription = $HPAsset.lnkProductObjectOfServiceInstance.productDescription
ProductLineDescription = $HPAsset.lnkProductObjectOfServiceInstance.productLineDescription
ActiveEntitlement = $HPAsset.lnkServiceObligations.serviceObligationActiveIndicator
OfferDescription = $HPAsset.lnkServiceObligationHeaderOffer | where serviceQuantity -GE 1 | Select-Object -ExpandProperty offerDescription
StartDate = $HPAsset.lnkServiceObligations.serviceObligationStartDate | ForEach-Object {[DateTime]$_}
EndDate = $HPAsset.lnkServiceObligations.serviceObligationEndDate | ForEach-Object {[DateTime]$_}
}
Write-Verbose "HP asset '$($HPAsset.lnkProductObjectOfServiceInstance.productDescription)' with serial number '$S'"
}
else {
Write-Warning "No HP asset information found for serial number '$S'"
continue
}
}
Catch {
$P = $_
$Global:Error.RemoveAt(0)
throw "Failed to invoke SOAP request: $P"
}
}
}
}
For Dell:
Function Get-DellAssetInformationHC {
[CmdletBinding()]
Param(
[Parameter(Mandatory, ValueFromPipeline)]
[String[]]$SerialNumber
)
Begin {
# Possible API keys
# 1adecee8a60444738f280aad1cd87d0e
# d676cf6e1e0ceb8fd14e8cb69acd812d
# 1adecee8a60444738f280aad1cd87d0e
# 849e027f476027a394edd656eaef4842
$APIKey = '849e027f476027a394edd656eaef4842'
}
Process {
foreach ($S in $SerialNumber) {
Try {
$DellURL = "https://api.dell.com/support/v2/assetinfo/warranty/tags.xml?svctags=$S&apikey=$APIKey"
$XML = New-Object System.Xml.XmlDocument
$XML.Load($DellURL)
$DellAsset = $XML.GetAssetWarrantyResponse.GetAssetWarrantyResult.Response.DellAsset
if ($DellAsset) {
[PSCustomObject][Ordered]#{
SerialNumber = $S
CustomerNumber = $DellAsset.CustomerNumber
OrderNumber = $DellAsset.OrderNumber
MachineDescription = $DellAsset.MachineDescription
ShipDate = $DellAsset.ShipDate
ServiceLevelDescription = $DellAsset.Warranties.Warranty.ServiceLevelDescription
StartDate = $DellAsset.Warranties.Warranty.StartDate | ForEach-Object {[DateTime]$_}
EndDate = $DellAsset.Warranties.Warranty.EndDate | ForEach-Object {[DateTime]$_}
}
Write-Verbose "Dell asset '$($DellAsset.MachineDescription)' with serial number '$S'"
}
else {
Write-Warning "No Dell asset information found for serial number '$S'"
}
}
Catch {
$P = $_
$Global:Error.RemoveAt(0)
throw "Failed retrieving Dell asset information for serial number '$S': $P"
}
}
}
}
Any reason why we cannot just do a http POST against the publicly available URL: http://h20564.www2.hpe.com/hpsc/wc/public/find
We can do an http POST with curl like this (put this in a script, for example):
/usr/bin/curl 'http://h20564.www2.hpe.com/hpsc/wc/public/find' \
--compressed \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Connection: keep-alive' \
-H 'Host: h20564.www2.hpe.com' \
-H 'Referer: (missing https: here) h20564.www2.hpe.com/hpsc/wc/public/home' \
-H 'Upgrade-Insecure-Requests: 1' \
-H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-binary '#/tmp/data' | grep hpui-standard-table
The only extra thing it needs the -H 'Cookie: ' header. I can copy the same cookie from a web lookup, but I am not sure how long that can be used.
The data file is in /tmp/data, formatted like this:
rows[0].item.countryCode=US rows[0].item.serialNumber=XXXXXXXXX
rows[1].item.countryCode=US rows[1].item.serialNumber=YYYYYYYYY
rows[2].item.countryCode=US rows[2].item.serialNumber=
rows[3].item.countryCode=US rows[3].item.serialNumber=
rows[4].item.countryCode=US rows[4].item.serialNumber=
rows[5].item.countryCode=US rows[5].item.serialNumber=
rows[6].item.countryCode=US rows[6].item.serialNumber=
rows[7].item.countryCode=US rows[7].item.serialNumber=
rows[8].item.countryCode=US rows[8].item.serialNumber=
rows[9].item.countryCode=US rows[9].item.serialNumber=
submitButton=Submit
Now HP has developed api for getting machines product warranty. It is in testing phase however we can try it. Hope it will be moved to production As Soon As Possible. You have to go through the document here and fill the details they required. You will have your own api key to work with.
https://developers.hp.com/css-enroll
Thanks,
Prabha.

How to use InsertAfter with PowerShell

I have some xml files where I want to insert the contents of one xml file into another. I thought I'd use LastChild and the InsertAfter method to accomplish this. So far it's not working for me.
Here is the parent.xml file:
<manifest>
<manifestExecution>
<assetDetail>
<fileAsset fileAssetGuid="parentguid1">
<parentfile1 />
</fileAsset>
<fileAsset fileAssetGuid="parentguid2">
<parentfile2 />
</fileAsset>
</assetDetail>
</manifestExecution>
</manifest>
And here is the child.xml file:
<manifest>
<manifestExecution>
<assetDetail>
<fileAsset fileAssetGuid="childguid1">
<childfile1 />
</fileAsset>
</assetDetail>
</manifestExecution>
</manifest>
What I want to do is select the fileAsset node(s) from child.xml and insert into parent.xml after the last fileAsset node in parent.xml.
Here is my test code:
$parent = [xml] (Get-Content d:\temp\parent.xml)
$parentnode = $parent.manifest.manifestExecution.assetDetail
$child = [xml] (Get-Content d:\temp\child.xml)
$childnode = $child.manifest.manifestExecution.assetDetail.InnerXml
$parentnode.InsertAfter($childnode, $parentnode.LastChild)
Here is the error msg I'm getting:
Cannot convert argument "0", with value: "<fileAsset fileAssetGuid="childguid1"> <childfile1 /></fileAsset>", for "InsertAfter" to type "System.Xml.XmlNode": "Cannot conver
t the "<fileAsset fileAssetGuid="childguid1"><childfile1 /></fileAsset>" value of type "System.String" to type "System.Xml.XmlNode"."
At line:5 char:24
+ $parentnode.InsertAfter <<<< ($childnode, $parentnode.LastChild)
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
What am I doing wrong?
You need to iterate through $childnode's children, remove them from their parent, and import them into the new document context ($child and $parent are different XmlDocument instances) before appending to $parentnode.
This will append all fileAsset nodes from $childnode into $parentnode.
$parent = [xml](get-content d:\temp\parent.xml)
$parentnode = $parent.manifest.manifestexecution.assetdetail
$child = [xml](get-content d:\temp\child.xml)
$childnode = $child.manifest.manifestexecution.assetdetail
while ($childnode.haschildnodes) {
$cn = $childnode.firstchild
$cn = $childnode.removechild($cn)
$cn = $parentnode.ownerdocument.importnode($cn, $true)
$parentnode.appendchild($cn)
}
Fortunately, most of these methods return the same XmlNode or a new version of it, so the body of the while loop could chained together like this:
$parentnode.appendchild( $parentnode.ownerdocument.importnode( $childnode.removechild( $childnode.firstchild ), $true ))
InsertAfter(newChild,referenceChild) could also work, but would be done a little differently since it also needs a reference to the the node that it will be inserted after.
your first problem is that you're not getting an XML element, but a string. You need to get an XML node from your XML document, but the shorthand method you're using is guessing you want a string. Usually you can force it by explicitly casting it over to [System.Xml.XmlElement], but that doesn't always work. You can reliably get an element using "SelectSingleNode".
You've not hit your second problem yet, but it's just around the corner. Once you've got XML, it still won't work because it's from a different XML document, so you need to "Import" the node. You'll want to tweak this to get the XML to align the way you envision, but the code works.
$parentString = #"
<manifest>
<manifestExecution>
<assetDetail>
<fileAsset fileAssetGuid="parentguid1">
<parentfile1 />
</fileAsset>
<fileAsset fileAssetGuid="parentguid2">
<parentfile2 />
</fileAsset>
</assetDetail>
</manifestExecution>
</manifest>
"#
$childString = #"
<manifest>
<manifestExecution>
<assetDetail>
<fileAsset fileAssetGuid="childguid1">
<childfile1 />
</fileAsset>
</assetDetail>
</manifestExecution>
</manifest>
"#
$parent = [xml] ($parentString)
$parentnode = $parent.manifest.manifestExecution.assetDetail
$child = [xml] ($childString)
$xpath = '/manifest/manifestExecution/assetDetail'
$childnode = $child.SelectSingleNode($xpath)
Write-Host("So the child is $($childnode.OuterXML)")
$importedNode = $parent.ImportNode($childNode,$true)
Write-Host("And after importing: $($importedNode.OuterXML)")
$parentnode.InsertAfter($importednode, $parentnode.LastChild)
Write-Host("To finally yield: $($parent.OuterXML)")
Also, you may find you can use something like your original code if you cast it to XmlElement properly.
$childnode = [System.Xml.XmlElement]$child.manifest.manifestExecution.assetDetail.InnerXml