Solved

New-WebServiceProxy passing sessionId to SOAP header

Posted on 2013-12-05
2
2,815 Views
Last Modified: 2016-02-26
Ok, I'm dieing here.  Somebody help me out please.  Here's what I have going on:

I am trying to design a Powershell script that will connect to salesforce and retrieve information.  The script I have so far will allow me to connect to the salesforce API and login, and it returns a sessionId in "$sf.loginResponse.SessionId".  I want to take this session ID and insert it into the session header so that I can run API calls.  Here is what I have so far:

$URI = "[insert-full-path-to-file]\salesforce-api-partner.wsdl"
$username = "michael1@desktopninjas.com"
$password = "testtest171XKd4g57k4KFgXHk7QbK8Kc7"

$sf = New-WebServiceProxy -Uri $URI -Namespace sf -UseDefaultCredential
$sf
$LoginResponse = $sf.login($username, $password)
$LoginResponse
$newSession = $LoginResponse.sessionId
$newSession
$newURL = $LoginResponse.serverUrl
$sf.Url = $newURL
$sf.Url
$sf.SessionHeaderValue.sessionId = $newSession
$soapQuery = $sf.query("SELECT Name FROM Lead")
$sf.logout()

Open in new window


Yes, I know there a password in there.  This is just a developer account.  There's no sensitive info in it and I'll change it once we fix this.  

This script gives me the following errors in Powershell ISE 3.0:

The property 'sessionId' cannot be found on this object. Verify that the property exists and can be set.
At C:\Users\mlucas\Dropbox\Desktop Ninjas\Clients\CCC\powershell-dev\0.1\soap-request2.ps1:20 char:1
+ $sf.SessionHeaderValue.sessionId = $newSession
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound
 
Exception calling "query" with "1" argument(s): "INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session"
At C:\Users\mlucas\Dropbox\Desktop Ninjas\Clients\CCC\powershell-dev\0.1\soap-request2.ps1:21 char:1
+ $soapQuery = $sf.query("SELECT Name FROM Lead")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SoapException
 
Exception calling "logout" with "0" argument(s): "INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session"
At C:\Users\mlucas\Dropbox\Desktop Ninjas\Clients\CCC\powershell-dev\0.1\soap-request2.ps1:22 char:1
+ $sf.logout()
+ ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : SoapException

Open in new window


I've tried to add-member but I'll not very good at it and I can't figure it out.  If anyone can help me with this step I will reward generously.  I've attached the script and WSDL file (in text format) for your review.
salesforce-api-partner.wsdl.txt
soap-request2.ps1.txt
0
Comment
Question by:orther
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
2 Comments
 

Author Comment

by:orther
ID: 39699422
It seems like using the defined namespace "#sf.Url" or #loginResponse.sessionId" I am able to add them to variables and process them, but anything to do with the header doesn't stick.  If you look in the SOAP envelope for query(), you'll see that the envelope is not expecting the sessionId in the body, but in the header.  There's something I'm missing here.

This envelope actually works from SOAP-UI to pull data:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:partner.soap.sforce.com">
   <soapenv:Header>
      <urn:SessionHeader>
         <urn:sessionId>00Di0000000iRH2!AQoAQL2N1aUx8D8j3ViQ.7lV0qcxIAZmsG3SEy9wfIz9saCKw4DXWZIeD5Yl8V52_2.7cBNMyw6bvlycbB4r0VxIZ5VVfT9H</urn:sessionId>
      </urn:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:query>
         <urn:queryString>SELECT Email FROM Lead WHERE Name = 'Michael Lucas'</urn:queryString>
      </urn:query>
   </soapenv:Body>
</soapenv:Envelope>

Open in new window


Comes back as this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.partner.soap.sforce.com">
   <soapenv:Header>
      <LimitInfoHeader>
         <limitInfo>
            <current>789</current>
            <limit>5000</limit>
            <type>API REQUESTS</type>
         </limitInfo>
      </LimitInfoHeader>
   </soapenv:Header>
   <soapenv:Body>
      <queryResponse>
         <result xsi:type="QueryResult">
            <done>true</done>
            <queryLocator xsi:nil="true"/>
            <records xsi:type="sf:sObject">
               <sf:type>Lead</sf:type>
               <sf:Id xsi:nil="true"/>
               <sf:Email>michael@desktopninjas.com</sf:Email>
            </records>
            <size>1</size>
         </result>
      </queryResponse>
   </soapenv:Body>
</soapenv:Envelope>

Open in new window

0
 

Accepted Solution

by:
orther earned 0 total points
ID: 39703965
OK, great bunch of help you guys are.  ;-)  Just kidding.  Here's what I came up with:

Instead of trying to add the sessionID into the object pipeline for New-WebServiceProxy, I decided to go a different route and use Invoke-WebRequest to send pre-built SOAP envelopes stored in here-strings.  The full code is below.  For those of you who are trying to connect powershell to Salesforce, this is the best way, trust me.  Powershell handles XML like a champ!  Just pipes the XML response right into an Object collection and you can start working with it.

# Set some variables (need to pipe these in from config.xml later in development)
$URI = "C:\Users\mlucas\Dropbox\Desktop Ninjas\Clients\CCC\powershell-dev\0.1\salesforce-api-partner.wsdl"
$username = "[omitted]"
$password = "[omitted]

# New-WebServiceProxy to call login() through salesforce API
write-progress -activity "Connecting to SalesForce API" -status "10% Complete:" -percentcomplete 10;
$service = New-WebServiceProxy -Uri $URI -Namespace service -UseDefaultCredential

write-progress -activity "Pulling Variables" -status "25% Complete:" -percentcomplete 25;
# grab LoginResponse and pull sessionId and serverUrl for the next calls
$LoginResponse = $service.login($username, $password)
$sessionID = $LoginResponse.sessionId
$newURL = $LoginResponse.serverUrl

write-progress -activity "Building Functions" -status "50% Complete:" -percentcomplete 50;
# build function to call query() in salesforce API
function Call-SFQuery([string]$sessionID, [string]$query1, [string]$newURL)
{
$queryBody = [xml] @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:partner.soap.sforce.com">
   <soapenv:Header>
      <urn:SessionHeader>
         <urn:sessionId>$sessionID</urn:sessionId>
      </urn:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:query>
         <urn:queryString>$query1</urn:queryString>
      </urn:query>
   </soapenv:Body>
</soapenv:Envelope>
"@

Invoke-WebRequest $newURL -Method Post -Headers @{SOAPAction=$newURL} -ContentType "text/xml" -Body $queryBody
}

# build function to call logout() in salesforce API
function Call-SFLogout([string]$sessionID)
{
$queryBody = [xml] @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:partner.soap.sforce.com">
   <soapenv:Header>
      <urn:SessionHeader>
         <urn:sessionId>$sessionID</urn:sessionId>
      </urn:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:logout/>
   </soapenv:Body>
</soapenv:Envelope>
"@

Invoke-WebRequest $newURL -Method Post -Headers @{SOAPAction=$newURL} -ContentType "text/xml" -Body $queryBody
}

# Set query and pipe to XML enabled variable so it sets it up in object collection
write-progress -activity "Executing Query" -status "80% Complete:" -percentcomplete 80;
$query1 = "SELECT Name, Email From Lead"
[xml]$response = Call-SFQuery $sessionID $query1 $newURL

# set some variable from 
$records = $response.Envelope.Body.queryResponse.result.records
$names = $response.Envelope.Body.queryResponse.result.records.name
$emails = $response.Envelope.Body.queryResponse.result.records.email

# call logout (optional at this point)
write-progress -activity "Logging Out" -status "100% Complete:" -percentcomplete 100;
$logoutReturn = Call-SFLogout $sessionID

# one way to work with the data
$names | Format-Table -Property name,email

# for each loop using object collections as data.
$records | ForEach-Object {
$_.name
$_.email
}

Open in new window

0

Featured Post

Online Training Solution

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action. Forget about retraining and skyrocket knowledge retention rates.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

You may have a outside contractor who comes in once a week or seasonal to do some work in your office but you only want to give him access to the programs and files he needs and keep privet all other documents and programs, can you do this on a loca…
IT certifications are a concrete representation of continual learning on the part of the candidate.  Continual learning is necessary for the long term success of an IT professional, but are IT certifications the right path for you?
This video teaches viewers how to create their own website using cPanel and Wordpress. Tutorial walks users through how to set up their own domain name from tools like Domain Registrar, Hosting Account, and Wordpress. More specifically, the order in…
This video teaches users how to migrate an existing Wordpress website to a new domain.

742 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question