devlife
devlife

Reputation: 16155

Execute a one-way wcf service operation from powershell

I have a scheduled task which executes a powershell script every hour. The powershell script must make a call to a one-way WCF service operation. Essentially it just needs to kick off an operation. My question is how do I go about doing that? I thought that just executing the url would in fact kick off the request but apparently that is not correct.

Here is what I was trying to do:

$request = [System.Net.WebRequest]::Create("http://myserver.com/myservice/dosomething")
$request.GetResponse()

The operation accepts no parameters and returns void.

Upvotes: 5

Views: 10297

Answers (5)

Ambrose Leung
Ambrose Leung

Reputation: 4225

I like jdmichal's answer because it is intuitive to make it work for inputs where the parameters are objects... the code required some modifications, this is what worked for me:

[Reflection.Assembly]::LoadFrom("C:\.....\WcfService2.dll")
[Reflection.Assembly]::LoadWithPartialName("System.ServiceModel")

$httpBinding = New-Object System.ServiceModel.BasicHttpBinding
if($useNTLM){
    #passes the default creds
    $httpBinding.Security.Mode = "TransportCredentialOnly"
    $httpBinding.Security.Transport.ClientCredentialType = "Ntlm"
}
$endpointAddress = New-Object System.ServiceModel.EndpointAddress 'http://localhost:63600/Service1.svc'

$contractDescription = [System.ServiceModel.Description.ContractDescription]::GetContract([WcfService2.IService1])

$serviceEndpoint = New-Object System.ServiceModel.Description.ServiceEndpoint($contractDescription, $httpBinding, $endpointAddress)
$channelFactory = New-Object "System.ServiceModel.ChannelFactory``1[WcfService2.IService1]"($serviceEndpoint)

$webProxy = $channelFactory.CreateChannel();
$result = $webProxy.GetData(123);
Write-Output $result #prints 123
$comptype = new-object WcfService2.CompositeType
$comptype.BoolValue =$false
$comptype.StringValue = "whatever123zxcv"
$result = $webProxy.GetDataUsingDataContract($comptype);
Write-Output $result #prints whatever123zxcv

UPDATE: I've found out how to use composite objects with the New-WebServiceProxy cmdlet (thanks to http://www.sqlmusings.com/2012/02/04/resolving-ssrs-and-powershell-new-webserviceproxy-namespace-issue/)

$wsProxy = New-WebServiceProxy -uri http://localhost:63600/Service1.svc
$wsProxy.UseDefaultCredentials = $true
$namespace = $wsProxy.GetType().Namespace
$myct = New-Object "$namespace.CompositeType"
$myct.BoolValue = $true;
$myct.StringValue = "asdfasdg";

$wsProxy.GetDataUsingDataContract($myct)
$wsProxy.GetData(123,$true)

Upvotes: 1

yzorg
yzorg

Reputation: 4460

HTTP replay approach:

  1. Install Fiddler, run it and start recording requests
  2. invoke the WCF service using the 'normal' client (record the request)
  3. use Invoke-WebRequest - replay the XML SOAP request body and some headers, be sure to match the headers: Content-Type, SOAPAction, and possibly others
  4. repeat (3) and keep tweaking the body and headers until you have a valid SOAP request

You don't have to use WCF to call a WCF service, you can mimic one with 3-4 changes from HTTP defaults. Just don't try to building the SOAP envelope yourself. But it's safe to replay everything you need from Fiddler over 'plain' HTTP.

Upvotes: 0

Keith Hill
Keith Hill

Reputation: 201952

PowerShell 2.0 makes this trivial with the New-WebServiceProxy cmdlet e.g.:

$zip = New-WebServiceProxy -uri http://www.webservicex.net/uszip.asmx?WSDL
$zip.getinfobyzip(20500).table

CITY      : Washington
STATE     : DC
ZIP       : 20500
AREA_CODE : 202
TIME_ZONE : E

Upvotes: 10

jdmichal
jdmichal

Reputation: 11162

I think the problem is that the code you have is actually creating an HttpWebRequest, not a WCF request. (In other words, it's simply executing an HTTP GET request on the URL, with no SOAP or .NET Remoting information.)

You should be able to follow these instructions to create a proper endpoint:

http://msdn.microsoft.com/en-us/magazine/cc163647.aspx#S11

It should look something like this:

$httpBinding = New-Object System.ServiceModel.BasicHttpBinding
$endpointAddress = New-Object System.ServiceModel.EndpointAddress 'http://myserver.com/myservice/dosomething'
$contractDescription = [System.ServiceModel.Description.ContractDescription]::GetContract([IYourInterface], $httpBinding, $endpointAddress)
$serviceEndpoint = New-Object System.ServiceModel.Description.ServiceEndpoint $contractDescription
$channelFactory = New-Object "System.ServiceModel.ChannelFactory``1[IYourInterface]" $serviceEndpoint
$webProxy = $channelFactory.CreateChannel();
$webProxy.yourServiceMethod();

Note that you'll need to import the DLL with the IYourInterface class for this to work:

[void] [Reflection.Assembly]::LoadFrom('path/to/your.dll')

Alternatively, if you have a WSDL defined for the service, you can follow these much easier instructions to access the service:

http://blogs.technet.com/heyscriptingguy/archive/2009/11/17/hey-scripting-guy-november-17-2009.aspx

Alternatively alternatively, you can figure out what the HTTP SOAP request needs to look like, and form it yourself within the HttpWebRequest.

Upvotes: 7

ajawad987
ajawad987

Reputation: 4672

I believe that you'll need to set the HTTP method first before attempting to get a response. By default, I believe the WebRequest object defaults to POST, but you'll actually need to set it to GET.

I haven't used PowerShell scripting much, but based off of your sample it would look something like this:

$request = [System.Net.WebRequest]::Create("http://myserver.com/myservice/dosomething") 
$request.Method = "GET"
$request.GetResponse()

Hope that helps!

Upvotes: 0

Related Questions