How can Powershell consume WCF REST Service through IIS?

c#, iis-7.5, powershell-3.0, wcf

Solution

The New-WebServiceProxy creates a "web service" proxy to web services (such as .asmx in .NET). These kinds of services are WS-I-Basic Profile 1.1 services. In other words, there's an expectation of WSDL.

Your WCF service has only one endpoint, which is a WebHttpBinding endpoint. This kind of endpoint is used for HTTP clients (non-soap) and are typically consumed by REST clients because they only use protocols of the web (HTTP and appropriate verbs such GET, POST, ...). When you tested this using your browser, it worked because browsers are just HTTP clients. When you tested it using your proxy from New-WebServiceProxy, it is trying to interpret the response as WSDL instead of just a string because that is the kind of client New-WebServiceProxy creates.

To make your WCF Service work for Web Service clients (such as New-WebServiceProxy), you need to add another endpoint for those clients using a binding they understand, which is the basicHttpBinding.

      <endpoint address="basic" binding="basicHttpBinding" contract="TrendDB.Web.ITrendRestService" />

This will at least get it working for the two types of clients (minus the security).

For the security, create a BindingConfiguration for the basicHttpBinding and one for the webHttpBinding. In there, you can specify the client credential type to be Windows.

Problem

Intent: I need to submit a series of test reports from an automated server group to a database. The test scripts are in python, and rather than open that can of worms I want to setup a Powershell script to push the completed tests to a WCF Service. I have an existing ASP.NET website that manages the reports and to build off of that I would like to add a simple WCF Service that can be accessed via the automation. I seem to be running into walls though and feel like I am very close to getting this working. I have been working on this issue for a few days now and my eyes are starting to bleed reading websites while trying to find relevant help. Service Code: ``` namespace TrendDB.Web { [ServiceContract] public interface ITrendRestService { [OperationContract] [WebGet] string GetTestMessage(); } [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class TrendRestService : ITrendRestService { public string GetTestMessage() { return "Success!"; } } } ``` Web.config (only pertinent info shown): ``` <system.web> ... <authentication mode="Windows" /> <authorization> <deny users="?" /> </authorization> ... </system.web> ... <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="restfulBehavior"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> <services> <service name="TrendDB.Web.TrendRestService"> <endpoint address="" behaviorConfiguration="restfulBehavior" binding="webHttpBinding" bindingConfiguration="" contract="TrendDB.Web.ITrendRestService"> </endpoint> </service> </services> </system.serviceModel> ``` I tested this as working in my browser as `http://localhost/trenddb/TrendRestService.svc/GetTestMessage` and the message that came through was: ``` <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Success!</string> ``` IIS 7.5 was set up using BKMs from MS for MVC usage of role authorization for intranet. The site has a specific App Pool set up and the server is set up in a domain using Windows Authentication. My first calls to Powershell started as: ``` $trend = New-WebServiceProxy -uri http://localhost/trenddb/TrendRestService.svc New-WebServiceProxy : The request failed with HTTP status 401: Unauthorized. ``` That is good, since at least that is the canned response to anonymous access, so I added `-UseDefaultCredential` to the command and got: ``` $trend = New-WebServiceProxy -uri http://localhost/trenddb/TrendRestService.svc -UseDefaultCredential New-WebServiceProxy : Object reference not set to an instance of an object. ``` I tried using the `GetTestMessage` WebInvoke just to see what happened: ``` $trend = New-WebServiceProxy -uri http://localhost/trenddb/TrendRestService.svc/GetTestMessage -UseDefaultCredential New-WebServiceProxy : The document at the url http://localhost/trenddb/TrendRestService.svc/GetTestMessage was not recognized as a known document type. The error message from each known type may help you fix the problem: - Report from 'WSDL Document' is 'There is an error in XML document (1, 2).'. - <string xmlns='http://schemas.microsoft.com/2003/10/Serialization/'> was not expected. - Report from 'XML Schema' is 'The root element of a W3C XML Schema should be <schema> and its namespace should be 'http://www.w3.org/2001/XMLSchema'.'. - Report from 'DISCO Document' is 'Discovery document at the URL http://localhost/trenddb/TrendRestService.svc/GetTestMessage could not be found.'. - The document format is not recognized. ``` From my browser call to the WCF I know that the `<string xmlns='http://schemas.microsoft.com/2003/10/Serialization/'>` was exactly what I am expecting... so I now need to figure out the powershell required to connect to this WCF REST Service. Edit: Using the svcutil.exe utility creates a proxy, but skips the config file. Does the line `serviceMetadata httpGetEnabled="true"` mean that I am producing metadata usable for a WCF Client? Would this effect PS?

Original source