locked
PowerShell V2 to SOAP - Trouble Sending Parameters RRS feed

  • Question

  • I am trying to make a PowerShell V2 script to work with an API.  And I am very new to both.   I keep having trouble getting the format of at least one of the parameters required for the API when I try to use the WebserviceProxy method.  So I thought I would try PowerShell to SOAP.  And I am limited to PowerShell V2 on the servers I am working with.  They are not connected to the Internet, and I cannot upgrade them.

    This is what the API gives as the example to send it the SOAP POST:

    SOAP 1.1
    The following is a sample SOAP 1.1 request and response. The placeholders shown need to be replaced with actual values.
    
    POST /ossNotificationsWS/UI.asmx HTTP/1.1
    Host: localhost
    Content-Type: text/xml; charset=utf-8
    Content-Length: length
    SOAPAction: "http://tempuri.org/LaunchClientApplication"
    
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <LaunchClientApplication xmlns="http://tempuri.org/">
          <externalId />
          <applicationUri>string</applicationUri>
        </LaunchClientApplication>
      </soap:Body>
    </soap:Envelope>
    

    The "externalId" is the one I am having trouble with.   I pull that down from SQL, and can get it as a string. 

    When I have tried the a WebserviceProxy object, I get the error:

    New-Object : Constructor not found. Cannot find an appropriate constructor for type Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy3ssNotifi
    cationsWS_UI_asmx_WSDL.ExternalId.
    At line:1 char:26
    + $deviceValue = New-Object <<<<  ($ns + ".ExternalId")
        + CategoryInfo          : ObjectNotFound: (:) [New-Object], PSArgumentException
        + FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand
     

    This is my code that I get the above error from:

    $uri = "http://STAN01BMGMT101/ossNotificationsWS/UI.asmx"
    
    $proxy = New-WebServiceProxy $uri -Credential $Credential
    
    Try
        {
            Write-Host "Setting up WebProxy using URI and Credentials"
            $script:Proxy = New-WebServiceProxy -Uri $URI -Credential $Credential -ErrorAction Stop
            Write-Host "Successfully connected proxy"
        }
    Catch
        {
            Write-Host "Connect-MFASession: $($_.ErrorDetails)"
            Break
        }
    $script:Namespace = $Proxy.GetType().Namespace
    Write-Host "Set up Namespace for future use"
    
    $ns = $proxy.getType().namespace
    
    $applicationUri = "page:http://stan01mfapplb2.br1.mr.ftc.com/FTCPFApps/STBScrollingMessages/MediaroomPage.aspx"
    
    #Device ExternalID Object 
    $ExtID= "002374E4DBEC"
    
    #Add the device value to the device and notify 
    #Create the deviceValue object 
    $deviceValue = New-Object ($ns + ".ExternalId")
    

    I think I am running into some limitations with PowerShell V2, but not sure.   I would like to use the WebserviceProxy object, but I cannot seem to be able to create it.   I cannot get by the "Constructor not found" error.   

    So, any advice or help with this would be greatly appreciated.  I'm stuck.   

    Tuesday, January 22, 2019 8:51 PM

All replies

  • PowerShell V2 only has access to Net 2.0 and lower.  The object you are trying to create likely I not supported in V2.

    PowerShell V2 should not be used anymore and will be out of support by November.  It does not do most of the things you may find on the Internet.

    The New-Object appears to be odd.  You need to look at the WSDL or returned object to discover how to create objects if the service requires this.  Without any knowledge of the service we cannot be of much help.


    \_(ツ)_/

    Tuesday, January 22, 2019 10:04 PM
  • Noted about V2.  I will see if I can get PowerShell upgraded.  

    The service is for our commercial IPTV middleware platform, Mediaroom.   It is an API called LaunchClientApplication.   There is little to no documentation or mentions on the Internet.

    Below is copy and pasted from the WSDL:

      <?xml version="1.0" encoding="utf-8" ?> 
    - <wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:s1="http://microsoft.com/wsdl/types/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    - <wsdl:types>
    - <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
      <s:import namespace="http://microsoft.com/wsdl/types/" /> 
    - <s:element name="LaunchClientApplication">
    - <s:complexType>
    - <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="externalId" type="tns:ExternalId" /> 
      <s:element minOccurs="0" maxOccurs="1" name="applicationUri" type="s:string" /> 
      </s:sequence>
      </s:complexType>
      </s:element>
      <s:complexType name="ExternalId" abstract="true" /> 
    - <s:complexType name="StringExternalId">
    - <s:complexContent mixed="false">
    - <s:extension base="tns:ExternalId">
      <s:attribute name="Id" type="s:string" /> 
      </s:extension>
      </s:complexContent>
      </s:complexType>
    - <s:complexType name="DeviceExternalId">
    - <s:complexContent mixed="false">
      <s:extension base="tns:StringExternalId" /> 
      </s:complexContent>
      </s:complexType>
    - <s:element name="LaunchClientApplicationResponse">
      <s:complexType /> 
      </s:element>
      </s:schema>
    - <s:schema elementFormDefault="qualified" targetNamespace="http://microsoft.com/wsdl/types/">
    - <s:simpleType name="guid">
    - <s:restriction base="s:string">
      <s:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" /> 
      </s:restriction>
      </s:simpleType>
      </s:schema>
      </wsdl:types>
    
      <wsdl:part name="parameters" element="tns:SendMessageToDevicesResponse" /> 
      </wsdl:message>
    - <wsdl:message name="LaunchClientApplicationSoapIn">
      <wsdl:part name="parameters" element="tns:LaunchClientApplication" /> 
      </wsdl:message>
    - <wsdl:message name="LaunchClientApplicationSoapOut">
      <wsdl:part name="parameters" element="tns:LaunchClientApplicationResponse" /> 
      </wsdl:message>
    
    - <wsdl:port name="UINotificationsWSSoap" binding="tns:UINotificationsWSSoap">
      <soap:address location="http://localhost/ossNotificationsWS/UI.asmx" /> 
      </wsdl:port>
    - <wsdl:port name="UINotificationsWSSoap12" binding="tns:UINotificationsWSSoap12">
      <soap12:address location="http://localhost/ossNotificationsWS/UI.asmx" /> 
      </wsdl:port>
      </wsdl:service>
      </wsdl:definitions>

    Also, ...

    $proxy.LaunchClientApplication
    
    
    MemberType          : Method
    OverloadDefinitions : {System.Void LaunchClientApplication(Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1ssNotificationsWS_UI_asmx_WSDL.ExternalId externalId, string applicationUri)}
    TypeNameOfValue     : System.Management.Automation.PSMethod
    Value               : System.Void LaunchClientApplication(Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1ssNotificationsWS_UI_asmx_WSDL.ExternalId externalId, string applicationUri)
    Name                : LaunchClientApplication
    IsInstance          : True
    

    And....

    $proxy.LaunchClientApplication | Get-Member
    
    
       TypeName: System.Management.Automation.PSMethod
    
    Name                MemberType Definition                                                                                                                                       
    ----                ---------- ----------                                                                                                                                       
    Copy                Method     System.Management.Automation.PSMemberInfo Copy()                                                                                                 
    Equals              Method     bool Equals(System.Object obj)                                                                                                                   
    GetHashCode         Method     int GetHashCode()                                                                                                                                
    GetType             Method     type GetType()                                                                                                                                   
    Invoke              Method     System.Object Invoke(Params System.Object[] arguments)                                                                                           
    ToString            Method     string ToString()                                                                                                                                
    IsInstance          Property   System.Boolean IsInstance {get;}                                                                                                                 
    MemberType          Property   System.Management.Automation.PSMemberTypes MemberType {get;}                                                                                     
    Name                Property   System.String Name {get;}                                                                                                                        
    OverloadDefinitions Property   System.Collections.ObjectModel.Collection`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] Over...
    TypeNameOfValue     Property   System.String TypeNameOfValue {get;}                                                                                                             
    Value               Property   System.Object Value {get;set;}                                                                                                                   
    
    Much thanks for the help.   


    Wednesday, January 23, 2019 12:36 PM
  • Seems to be:

    #call
    $proxy.LaunchClientApplication($externalID,$applicationID)
    #response
    $proxy.LaunchClientApplicationResponse


    \_(ツ)_/


    • Edited by jrv Wednesday, January 23, 2019 2:28 PM
    Wednesday, January 23, 2019 2:27 PM
  • Seems to be:

    #call
    $proxy.LaunchClientApplication($externalID,$applicationID)
    #response
    $proxy.LaunchClientApplicationResponse


    \_(ツ)_/


    But the $externalID cannot be a "System.String" and needs to be type:  "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1ssNotificationsWS_UI_asmx_WSDL.ExternalId"."

    That is my problem.  Getting the $externalID into the correct type.

    Wednesday, January 23, 2019 8:29 PM
  • Pretty sure that's because New-WebServiceProxy cmdlet isn't present in Powershell V2. You may have use .Net and System.Net.WebRequest.

    This is from 2011, and that System.Net.WebRequest is present in all .Net releases:

    https://social.technet.microsoft.com/Forums/windowsserver/en-US/b56e6a6a-9798-4f89-839d-74535469c974/execute-soap-in-powershell


    --- Rich Matheisen MCSE&I, Exchange Ex-MVP (16 years)

    Wednesday, January 23, 2019 8:50 PM
  • Your WSDL is broken and incomplete.  It has missing tags.  The only way to post WDL is to save to a txt file and copy from that.  Copying fromIE will not copy correctly.


    \_(ツ)_/

    Wednesday, January 23, 2019 8:57 PM
  • Pretty sure that's because New-WebServiceProxy cmdlet isn't present in Powershell V2. You may have use .Net and System.Net.WebRequest.

    This is from 2011, and that System.Net.WebRequest is present in all .Net releases:

    https://social.technet.microsoft.com/Forums/windowsserver/en-US/b56e6a6a-9798-4f89-839d-74535469c974/execute-soap-in-powershell


    --- Rich Matheisen MCSE&I, Exchange Ex-MVP (16 years)


    New-WebServiceProxy was introduced in PS2.

    \_(ツ)_/

    Wednesday, January 23, 2019 8:58 PM
  • Much thanks for all the help.

    It is looking like the problem is that I do not have the assembly info needed to create the type of object that will work.   So I am trying to go with creating the SOAP coding in PowerShell, as in the link Rick sent. (Much thanks, Rick)

    Following that code, I appear to have two problems.   One, I don't think I am getting the variables populated in the XML, and the website the API is at requires authentication.  I get an unauthorized error.

    Here is my code:

    clear 
    
    function Execute-SOAPRequest 
    ( 
            [Xml]    $SOAPRequest, 
            [String] $URL 
    ) 
    { 
            write-host "Sending SOAP Request To Server: $URL" 
            $soapWebRequest = [System.Net.WebRequest]::Create($URL) 
            
            $soapWebRequest.Headers.Add("SOAPAction","`"http://tempuri.org/LaunchClientApplication`"")
    
            $soapWebRequest.ContentType = "text/xml;charset=`"utf-8`"" 
            $soapWebRequest.Accept      = "text/xml" 
            $soapWebRequest.Method      = "POST" 
            
            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 
    }
    
    $ExtID = "002374E4DBEC"
    $applicationUri = "page:http://stan01mfapplb2.br1.mr.ftc.com/FTCPFApps/STBScrollingMessages/MediaroomPage.aspx"
    
    $url = 'http://172.31.9.2/ossNotificationsWS/UI.asmx'
    $soap = [xml]@'
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
      <soap:Body>
        <tem:LaunchClientApplication>
         <tem:externalId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="tem:DeviceExternalId" Id="$ExtID"/>
          <tem:applicationUri>$applicationUri</tem:applicationUri>
        </tem:LaunchClientApplication>
      </soap:Body>
    </soap:Envelope>
    '@
    
    $soap.save("C:\temp\temp.xml")
    
    $ret = Execute-SOAPRequest $soap $url
    

    And here is what I am getting back....

    Sending SOAP Request To Server: http://172.31.9.2/ossNotificationsWS/UI.asmx
    Initiating Send.
    Send Complete, Waiting For Response.
    Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (401) Unauthorized."
    At C:\PowerShellScripts\Test-03.ps1:24 char:44
    +         $resp = $soapWebRequest.GetResponse <<<< () 
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : DotNetMethodException
     
    You cannot call a method on a null-valued expression.
    At C:\PowerShellScripts\Test-03.ps1:25 char:50
    +         $responseStream = $resp.GetResponseStream <<<< () 
        + CategoryInfo          : InvalidOperation: (GetResponseStream:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull
     
    You cannot call a method on a null-valued expression.
    At C:\PowerShellScripts\Test-03.ps1:27 char:49
    +         $ReturnXml = [Xml] $soapReader.ReadToEnd <<<< () 
        + CategoryInfo          : InvalidOperation: (ReadToEnd:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull
     
    You cannot call a method on a null-valued expression.
    At C:\PowerShellScripts\Test-03.ps1:28 char:30
    +         $responseStream.Close <<<< () 
        + CategoryInfo          : InvalidOperation: (Close:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull
     
    Response Received.
    
    

    The XML file I save as C:\temp\temp.xml (below) does not show the variables populated.  What am I missing there?

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
      <soap:Body>
        <tem:LaunchClientApplication>
          <tem:externalId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="tem:DeviceExternalId" Id="$ExtID" />
          <tem:applicationUri>$applicationUri</tem:applicationUri>
        </tem:LaunchClientApplication>
      </soap:Body>
    </soap:Envelope>

    Thursday, January 24, 2019 8:22 PM
  • Well, I cannot seem to get the right assembly file, so I am failing back and trying to use SOAP commands more directly.   And I am still limited to Powershell v2.

    I have the following code, but I keep getting (401) Unauthorized.

    function Execute-SOAPRequest
    (
        [Xml]    $SOAPRequest,
        [String] $URL
    )
    {
        Write-Host "Sending SOAP Request To Server: $URL"
        $soapWebRequest = [System.Net.WebRequest]::Create($URL)
        
        $uname = 'Domain\Username'
        $Password = 'password'
               
        [string]$authInfo = $uname + ":" + $Password
        [string]$authInfo = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($uname + ':' + $Password))
        
        $soapWebRequest.Headers.Add("AUTHORIZATION","Basic $authInfo")   
        $soapWebRequest.Headers.Add("SOAPAction","`"http://tempuri.org/LaunchClientApplication`"")
        $soapWebRequest.ContentType = "text/xml;charset=`"utf-8`""
        $soapWebRequest.Method = "POST"
        
        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
        
    }
    
    $URL = 'http://172.31.9.2/ossNotificationsWS/UI.asmx'
    
    $soap = [xml]@"
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
      <soap:Body>
        <tem:LaunchClientApplication>
          <tem:externalId xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="tem:DeviceExternalId" Id="002374E4DBEC" />
          <tem:applicationUri>http://stan01mfapplb2.br1.mr.ftc.com/FTCPFApps/STBScrollingMessages/TestScroll.aspx</tem:applicationUri>
        </tem:LaunchClientApplication>
      </soap:Body>
    </soap:Envelope>
    "@
    
    $ret = Execute-SOAPRequest $soap $url; $ret | Export-Clixml "C:\PowerShellScripts\LogFiles\SOAPTest-PS2.0-$(Get-date -f yyy-MM-dd-mm-ss).xml";
    
    How do I pass the username and password correctly?  

    Tuesday, March 19, 2019 5:10 PM
  • "Unauthorized means you are not authenticated with an account that has access to the resource.

    Passing username and password depends on the SOAP requirements and authentication mechanisms of the service.  Contact the service owners to get the correct requirements.  If they have C or C# examples that should give you the information you need.


    \_(ツ)_/


    • Edited by jrv Tuesday, March 19, 2019 7:30 PM
    Tuesday, March 19, 2019 7:29 PM
  • "Unauthorized means you are not authenticated with an account that has access to the resource.

    Passing username and password depends on the SOAP requirements and authentication mechanisms of the service.  Contact the service owners to get the correct requirements.  If they have C or C# examples that should give you the information you need.


    \_(ツ)_/


    I can get the API to work with SoapUI.  I have the needed Username and Password, so the question is not about what being unauthorized means but rather how to pass that username and password via code in PowerShell.
    Tuesday, March 19, 2019 7:36 PM
  • And that depends on the service.  Normally a header is used or and authorization request the returns a token that you pass back to the service on subsequent calls.  If the service uses standard web methods then the "Credential" parameter can be sued to send the credentials.

    Windows based services can use CHAPv2 or Windows integrated security and many other authentication methods.  Public facing web servers may only use things like OAuth or Json methods.  It is your task too learn how the service requires you to authenticate.


    \_(ツ)_/

    Tuesday, March 19, 2019 8:09 PM
  • And that depends on the service.  Normally a header is used or and authorization request the returns a token that you pass back to the service on subsequent calls.  If the service uses standard web methods then the "Credential" parameter can be sued to send the credentials.

    Windows based services can use CHAPv2 or Windows integrated security and many other authentication methods.  Public facing web servers may only use things like OAuth or Json methods.  It is your task too learn how the service requires you to authenticate.


    \_(ツ)_/


    Windows domain authentication.   I need to provide the domain, username, and password.  
    Tuesday, March 19, 2019 8:20 PM
  • Use Get-Credential.

    $cred = Get-Credential

    Or:

    $soapWebRequest.Credentials = Get-Credential domain/user


    \_(ツ)_/


    • Edited by jrv Tuesday, March 19, 2019 8:40 PM
    Tuesday, March 19, 2019 8:38 PM
  •     Write-Host "Sending SOAP Request To Server: $URL"
        $soapWebRequest = [System.Net.WebRequest]::Create($URL)
        
        $soapWebRequest.Credentials = Get-Credential DM\UserName
        $soapWebRequest.Headers.Add("SOAPAction","`"http://tempuri.org/LaunchClientApplication`"")
        $soapWebRequest.ContentType = "text/xml;charset=`"utf-8`""
        $soapWebRequest.Method = "POST"

    Okay, that works, kind of.  It pops up the window to finish, enter the password.   But I need to have this as a Scheduled Task so cannot have that prompt window popping up every time.

    Is there a way to hard code the "DM\UserName" and password?

    Tuesday, March 19, 2019 8:58 PM
  • You can run the scheduled task under the account that has access to the service and then you won't need credentials.


    \_(ツ)_/

    Tuesday, March 19, 2019 9:01 PM
  • That won't work for what I am trying to do.

    Is there a way to put the credentials into the code?

    Tuesday, March 19, 2019 9:04 PM
  • That won't work for what I am trying to do.

    Is there a way to put the credentials into the code?

    If you have the domain/user and password why won't it work?


    \_(ツ)_/

    Tuesday, March 19, 2019 9:48 PM
  • If you have the domain/user and password why won't it work?


    \_(ツ)_/

    I'm not really sure.  Something with how the domains and firewalls are setup.   I have tried it and the scheduled task on the server I need this to run won't save with the username that the API needs, for access to another server in a different domain.

    Wednesday, March 20, 2019 11:19 AM
  • I have gotten this to work now.   I was able to take the code from Example 7 the Get-Credential help site.   I understand that this is not the most secure method.  The servers that I am using are not connected to the internet and behind their own set of firewalls.   

    Now to work on getting the script scheduled and working with SQL.   

    Much thanks, everyone.

    Wednesday, March 20, 2019 12:56 PM