Cannot make Powershell work for windows notification - powershell

I want to test notification in Powershell
I tried solution found on SO with https://gist.github.com/altrive/72594b8427b2fff16431
no error but no notification (I checked Background apps is on).
Then I tried this snippet (taken here https://michael-casey.com/2019/05/12/schedule-windows-notifications-with-powershell/) but I get error Exception calling "LoadXml" with "1" argument(s): "Exception from HRESULT: 0xC00CE558" and can't see how to fix it :
function test {
$app = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
$Template = [Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText01
$ToastTemplate = ([Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($Template).GetXml())
$ToastTemplate = #"
<toast launch="app-defined-string">
<visual>
<binding template="ToastGeneric">
<text>Prevent Eye Strain</text>
<text>Take a 20 second break</text>
</binding>
</visual>
</toast>
"#
$ToastXml = New-Object -TypeName Windows.Data.Xml.Dom.XmlDocument
$ToastXml.LoadXml($ToastTemplate.OuterXml)
$notify = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($app)
$notify.Show($ToastXml)
#source: https://gist.github.com/Windos/9aa6a684ac583e0d38a8fa68196bc2dc
}

Related

EWS and AutoDiscoverURL error using Azure AD Certificate with Powershell

I've tried with and without Secret ID, and now with a self-signed Certificate and I keep getting the same error:
Exception calling "AutodiscoverUrl" with "2" argument(s): "The
expected XML node type was XmlDeclaration, but the actual type is
Element."
My PowerShell script:
$TenantId = "blahblah"
$AppClientId="blahblah"
$EDIcertThumbPrint = "blahblah"
$EDIcert = get-childitem Cert:\CurrentUser\My\$EDIcertThumbPrint
$MsalParams = #{
ClientId = $AppClientId
TenantId = $TenantId
ClientCertificate = $EDIcert
Scopes = "https://outlook.office.com/.default"
}
$MsalResponse = Get-MsalToken #MsalParams
$EWSAccessToken = $MsalResponse.AccessToken
Import-Module 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'
#Provide the mailbox id (email address) to connect via AutoDiscover
$MailboxName ="email#myemaildomain.com.au"
$ews = [Microsoft.Exchange.WebServices.Data.ExchangeService]::new()
$ews.Credentials = [Microsoft.Exchange.WebServices.Data.OAuthCredentials]$EWSAccessToken
$ews.Url = "https://outlook.office365.com/EWS/Exchange.asmx"
$ews.AutodiscoverUrl($MailboxName,{$true})
I've searched that error message everywhere, and I am not getting anywhere. The error doesn't make sense, because I am not referring to XML in any way - unless it's embedded inside the EWS?
The only time this works is when I don't use either a Secret ID nor a Certificate, but the Token only lasts 1 hour! I need to make this automatic, so I can get into my mailbox and extract files from emails.
Thanks
UPDATE
So I've removed the AutoDiscoverUrl() and I now getting another error:
Exception calling "FindItems" with "2" argument(s): "The request
failed. The remote server returned an error: (403) Forbidden."
Trace log:
The token contains not enough scope to make this call.";error_category="invalid_grant"
But why when I have an Oauth token!?
My code in trying to open the "Inbox":
$results = $ews.FindItems(
"Inbox",
( New-Object Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList 100 )
)
$MailItems = $results.Items | where hasattachments
AutoDiscoverv1 doesn't support the client credentials flow so you need to remove the line
$ews.AutodiscoverUrl($MailboxName,{$true})
It's redundant anyway because your already setting the EWS endpoint eg
$ews.Url = "https://outlook.office365.com/EWS/Exchange.asmx"
The only time that endpoint would change is if you had mailbox OnPrem in a hybrid environment and there are other ways you can go about detecting that such as autodiscoverv2.

Calling AppDomain.DoCallback from Powershell

This is based on the Stack Overflow question: How to load an assembly as reflection-only in a new AppDomain?
I am attempting to determine the runtime version of an assembly, but that assembly could be loaded multiple times as I traverse through nested folders. Loading the assembly directly using
[Reflection.Assembly]::ReflectionOnlyLoadFrom($assembly)
will therefore not work, as the assembly can only be loaded once in the app-domain.
Given the following function to load an assembly in a separate AppDomain:
function Load-AssemblyInNewAppDomain($assembly)
{
Write-Host $assembly.FullName
$domain = [AppDomain]::CreateDomain([Guid]::NewGuid())
$domain.DoCallback
({
$loaded = [Reflection.Assembly]::Load($assembly)
$runtime = $loaded.ImageRuntimeVersion
Write-Host $runtime
})
}
This outputs the contents of the delegate to the console, rather than executing it:
OverloadDefinitions
-------------------
void DoCallBack(System.CrossAppDomainDelegate callBackDelegate)
void _AppDomain.DoCallBack(System.CrossAppDomainDelegate theDelegate)
$loaded = [Reflection.Assembly]::Load($assembly)
$runtime = $loaded.ImageRuntimeVersion
Write-Host $runtime
Note that the results are the same, whether I use PowerShell 4 or 5
Any help/guidance appreciated
First thought: don't muck around with AppDomains at all and use a completely separate process. Those are (relatively) easily launched from PowerShell, at least. The drawback is that it's potentially much slower if you're doing this for lots of files.
$myAssemblyPath = "C:\..."
$getImageRuntimeVersion = {
[Reflection.Assembly]::ReflectionOnlyLoadFrom($input).ImageRuntimeVersion
}
$encodedCommand = [Convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes($getImageRuntimeVersion)
)
$imageRuntimeVersion = $myAssemblyPath | powershell -EncodedCommand $encodedCommand
So, is there no way at all to do this with AppDomains in PowerShell? Well, there is, but it's not pretty. You can't use AppDomain.DoCallBack because, as you've discovered, PowerShell can't remote delegates that way (because, under the covers, it produces dynamic methods).
However, it's easy to host the PowerShell runtime, and all PowerShell objects know how to serialize (a requirement for cross-domain remoting), so invoking a PowerShell script in another AppDomain is fairly simple (but still ugly):
$scriptInvokerAssembly = [System.IO.Path]::GetTempFileName() + ".dll"
Add-Type -OutputAssembly $tempAssembly -TypeDefinition #"
using System;
using System.Reflection;
using System.Collections.Generic;
using System.Management.Automation;
public class ScriptInvoker : MarshalByRefObject {
public IEnumerable<PSObject> Invoke(ScriptBlock scriptBlock, PSObject[] parameters) {
using (var powerShell = PowerShell.Create()) {
powerShell.Commands.AddScript(scriptBlock.ToString());
if (parameters != null) {
powerShell.AddParameters(parameters);
}
return powerShell.Invoke();
}
}
}
"#
[Reflection.Assembly]::LoadFile($scriptInvokerAssembly) | Out-Null
Function Invoke-CommandInTemporaryAppDomain([ScriptBlock] $s, [object[]] $arguments) {
$setup = New-Object System.AppDomainSetup
$setup.ApplicationBase = Split-Path ([ScriptInvoker].Assembly.Location) -Parent
$domain = [AppDomain]::CreateDomain([Guid]::NewGuid(), $null, $setup)
$scriptInvoker = $domain.CreateInstanceAndUnwrap(
[ScriptInvoker].Assembly.FullName, [ScriptInvoker]
);
$scriptInvoker.Invoke($s, $arguments)
[AppDomain]::Unload($domain)
}
And now you can do
Invoke-CommandInTemporaryAppDomain {
[Reflection.Assembly]::ReflectionOnlyLoadFrom($args[0]).ImageRuntimeVersion
} $myAssemblyPath
Note that we have to generate a temporary assembly on disk and have AppDomain load it from there. This is ugly, but you can't have Add-Type produce an in-memory assembly, and even if you do end up with a byte[] getting that to load in another AppDomain is anything but trivial because you can't hook AppDomain.AssemblyResolve in PowerShell. If this command was packaged in a module, you'd compile the assembly containing the ScriptInvoker ahead of time, so I don't see working around this as a priority.
You can't run DoCallback via powershell alone. But DoCallBack does work with some inline C#. As Jeroen says it's ugly, but this works:
$assm = "C:\temp\so\bin\dynamic-assembly.dll"
Add-Type -TypeDefinition #"
using System.Reflection;
using System;
namespace Example
{
public class AppDomainUtil
{
public void LoadInAppDomain(AppDomain childDomain, string assemblyName)
{
childDomain.SetData("assemblyName", assemblyName);
childDomain.DoCallBack( new CrossAppDomainDelegate(LoadAssembly)) ;
}
public static void LoadAssembly()
{
string assemblyName = (string)AppDomain.CurrentDomain.GetData("assemblyName");
// console not available from another domain
string log = "c:\\temp\\hello.txt";
System.IO.File.WriteAllText(log, string.Format("Hello from {0}\r\n",AppDomain.CurrentDomain.FriendlyName));
System.IO.File.AppendAllText(log, string.Format("Assembly to load is {0}\r\n",assemblyName));
Assembly loaded = Assembly.Load(assemblyName);
System.IO.File.AppendAllText(log, string.Format("Assemblyloaded: {0}\r\n",loaded.FullName));
}
}
}
"# -OutputAssembly $assm -OutputType Library # must set output assembly otherwise assembly generated in-memory and it will break with Type errors.
Add-Type -Path $assm
function Load-AssemblyInNewAppDomain([string]$assembly) {
Write-Host "Parent domain: $([AppDomain]::CurrentDomain.FriendlyName)"
$util = New-Object Example.AppDomainUtil
$ads = New-Object System.AppDomainSetup
$cd = [AppDomain]::CurrentDomain
# set application base
$ads.ApplicationBase = [IO.path]::GetDirectoryName( $assm )
[System.AppDomain]$newDomain = [System.AppDomain]::CreateDomain([System.Guid]::NewGuid().ToString(), $null, $ads);
Write-Host "Created child domain: $($newDomain.FriendlyName)"
$util.LoadInAppDomain($newDomain, $assembly)
}
Testing it out:
PS C:\WINDOWS\system32> Load-AssemblyInNewAppDomain "".GetType().Assembly.FullName
Parent domain: PowerShell_ISE.exe
Created child domain: 61ab2dbb-8b33-4e7e-84db-5fabfded53aa
PS C:\WINDOWS\system32> cat C:\temp\hello.txt
Hello from 61ab2dbb-8b33-4e7e-84db-5fabfded53aa
Assembly to load is mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Assemblyloaded: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Project collection get service returning no value when trying to create a new work item via PowerShell

i was trying to create a new work item type using powershell and tfs api.
here is the script which i am trying to run to create a new work item type
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Common")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client")
[string] $tfsCollectionUrl = "http://r2-09-tfs:8080/tfs/DefaultCollection"
$teamProjectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsCollectionUrl)
$ws = $teamProjectCollection.GetService([type]"Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore")
$proj = $ws.Projects["TestTeamProject"]
$wit = $proj.WorkItemTypes["Task"]
$workitem = $wit.NewWorkItem()
$workItem.Title = "Sample Task Title 2"
$workItem.Description = "Sample Description"
$workitem.AreaPath = "TestTeamProject"
$workitem.IterationPath = "TestTeamProject"
$workItem.Save()
Write-Host "The TFS work item number is: " $workItem.Id
when i run this script, getting an error which says Cannot index into a null array. At $proj = $ws.Projects["TestTeamProject"]
my guess is that the workitemstore variable $ws is null, am i missing anything in that line?
[void]System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Common")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client")
[string] $tfsCollectionUrl = "http://r2-09-tfs:8080/tfs/DefaultCollection"
$teamProjectCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsCollectionUrl)
[Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]$ws = $teamProjectCollection
$proj = $ws.Projects["TestTeamProject"]
$wit = $proj.WorkItemTypes["Task"]
$workitem = $wit.NewWorkItem()
$workItem.Title = "Sample Task Title 2"
$workItem.Description = "Sample Description"
$workitem.AreaPath = "TestTeamProject"
$workitem.IterationPath = "TestTeamProject"
$workItem.Save()
Write-Host "The TFS work item number is: " $workItem.Id
changed $ws type to Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore type and took off GetService, works fine now...

Hash entry sometime is $NULL (error), and sometimes isn't?

I have my little 3-sided socket-server. Each server has its own hash with its key-values.
The very first is:
$Local = #{ID="Local"; ...}
$RemtA = #{ID="RemtA"; ...}
$RemtB = #{ID="RemtB"; ...}
I start for all of the the listeners - no problem.
If now a client wants to connect, I want to add the ID to $ProgressHead, which is defined like this:
$ProgressHead = "Value-Broadcast, Connected: "
and used in my function to accept a client called with its hash as $h:
if ( !$h.Connected ) {
#Write-Host "$($h.ID) not connected, pending: $($h.listener.Pending())"
if ( $h.listener.Pending()) {
$h.client = $h.listener.AcceptTcpClient() # will block here until connection
$h.stream = $h.client.GetStream();
$h.writer = New-Object System.IO.StreamWriter $h.stream
$h.writer.AutoFlush = $true
$h.Connected = $true
Write-Host $Global:ProgressHead # fails - empty ??
Write-Host $h.ID # prints oh depit it is marked as error
if ( !$Global:ProgressHead.Contains( $h.ID ) ) { # <= HERE IS THE ERROR ????
$Global:ProgressHead= "$($Global:ProgressHead)$($h.ID) "
}
}
}
The Error message says:
Can't call a method for an expression which is NULL and $h.ID is underlined
BUT I DO NOT get any error if I start this either in the cmd-console when running powershell -file ThreeServer.ps1 or within the ISE in debug mode. The ID is written correctly to $ProgressHead!
Only if I start this in the Powershell console (PS C:\...> ./ThreeServer.ps1) I get this error, but all the other keys in the lines above are valid ONLY $h.ID on ONLY the PS-console throws this error?

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.