I was tasked with getting our new Silverlight application building and deploying using Team Build. The application has been in pre-development stages for awhile now and the team was happy enough with their progress that they wanted to get the code into Team System. One of the requirements was that the application had to be secure, so SSL was a must, but our spiking developers were only testing on their local development environment, so no SSL. I knew that I was going to get hit with a few "it works on my machine" issues when I tried to deploy their existing code to our application server under SSL. This was one time when I wasn't happy about being right. To make matters even more challenging, the changes that I made had to be build specific, since I didn't want to force the developers to work with SSL on their local machines. Oh Joy!
First thing I did was to just get the application deploying and running using basic http. I encountered my first set of issues here. The team had followed the excellent post from Brad Abrams on how to configure the ASP.Net services in Silverlight. In the development environment, the WCF services for authentication, profile and role were running on the local machine and the team had hard coded the endpoint addresses in the ServiceReferences.ClientConfig. Obviously, this had to change. So my first step was to get my build to swap out the local urls for the urls on the server. The XmlUpdate task from MSBuild Community Tasks to the rescue. I just used this task to locate and change the 3 urls and this was all that was required to get the application working in our deployment environment. But still no SSL.
Here's what that ended up looking like...
<Target Name="UpdateServiceReferences" >
<Message Text="Executing UpdateServiceReferences" />
<ItemGroup>
<ServiceReferences Include="$(SolutionRoot)\Source\WebApp\ServiceReferences.ClientConfig" />
</ItemGroup>
<Attrib Files="@(ServiceReferences)"
ReadOnly="false"/>
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/client/endpoint[@name='BasicHttpBinding_AuthenticationService']/@address"
Value="http://webserver/webapp/Services/Application%20Services/AuthenticationService.svc" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/client/endpoint[@name='BasicHttpBinding_ProfileService']/@address"
Value="http://webserver/webapp/Services/Application%20Services/ProfileService.svc" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/client/endpoint[@name='BasicHttpBinding_RoleService']/@address"
Value="http://webserver/webapp/Services/Application%20Services/RoleService.svc" />
</Target>
Now that I had basic http working, I changed the web site to support ssl and then tried to connect. Well, not surprisingly, KaBoom as soon as the application tried to access the authentication service.
OK, the first fix was easy. I adjusted the code above to point at https and not http, hoping that would just fix it, but alas no...
To get it to work, I actually had to adjust the web.config to get SSL support for the asp.net WCF services. I followed the example here on the local copy of the web.config to see if that would fix the issue, but still no love.
I then noticed that in my ServiceReferences.ClientConfig, I had a line there that describes the security mode. Didn't I see the same line in the MSDN sample? So I did... So, in the ServiceReferences.ClientConfig, I changed the mode to Transport to match what I had done in the web.config, rebuilt and re-deployed the app and success! Unfortunately for me, the changes I made to both the ServiceReferences.ClientConfig and the web.config have to happen during the build, so I was back using XmlUpdate to get all the proper values swapped out. Here's what I ended up with...
<Target Name="UpdateServiceReferences" >
<Message Text="Executing UpdateServiceReferences" />
<ItemGroup>
<ServiceReferences Include="$(SolutionRoot)\Source\SilverlightApp\ServiceReferences.ClientConfig" />
</ItemGroup>
<Attrib Files="@(ServiceReferences)"
ReadOnly="false"/>
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/client/endpoint[@name='BasicHttpBinding_AuthenticationService']/@address"
Value="https://WebServer/WebApp/Services/Application%20Services/AuthenticationService.svc" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/bindings/basicHttpBinding/binding[@name='BasicHttpBinding_AuthenticationService']/security/@mode"
Value="Transport" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/client/endpoint[@name='BasicHttpBinding_ProfileService']/@address"
Value="https://WebServer/WebApp/Services/Application%20Services/ProfileService.svc" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/bindings/basicHttpBinding/binding[@name='BasicHttpBinding_ProfileService']/security/@mode"
Value="Transport" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/client/endpoint[@name='BasicHttpBinding_RoleService']/@address"
Value="https://WebServer/WebApp/Services/Application%20Services/RoleService.svc" />
<XmlUpdate XmlFileName="%(ServiceReferences.FullPath)"
XPath="//configuration/system.serviceModel/bindings/basicHttpBinding/binding[@name='BasicHttpBinding_RoleService']/security/@mode"
Value="Transport" />
</Target>
<Target Name="UpdateWebConfig" >
<Message Text="Executing UpdateWebConfig" />
<ItemGroup>
<WebConfig Include="$(SolutionRoot)\Source\WebApp\Web.Config" />
</ItemGroup>
<Attrib Files="@(WebConfig)"
ReadOnly="false"/>
<XmlUpdate XmlFileName="%(WebConfig.FullPath)"
XPath="//configuration/system.serviceModel/services/service[@name='System.Web.ApplicationServices.AuthenticationService']/endpoint/@bindingConfiguration"
Value="userHttps" />
<XmlUpdate XmlFileName="%(WebConfig.FullPath)"
XPath="//configuration/system.serviceModel/services/service[@name='System.Web.ApplicationServices.ProfileService']/endpoint/@bindingConfiguration"
Value="userHttps" />
<XmlUpdate XmlFileName="%(WebConfig.FullPath)"
XPath="//configuration/system.serviceModel/services/service[@name='System.Web.ApplicationServices.RoleService']/endpoint/@bindingConfiguration"
Value="userHttps" />
<XmlUpdate XmlFileName="%(WebConfig.FullPath)"
XPath="//configuration/system.serviceModel/bindings/basicHttpBinding/binding/security/@mode"
Value="Transport" />
<XmlUpdate XmlFileName="%(WebConfig.FullPath)"
XPath="//configuration/system.serviceModel/bindings/basicHttpBinding/binding/@name"
Value="userHttps" />
<XmlUpdate XmlFileName="%(WebConfig.FullPath)"
XPath="//configuration/system.web.extensions/scripting/webServices/authenticationService/@requireSSL"
Value="true" />
</Target>
<Target Name="BeforeCompile" DependsOnTargets="UpdateServiceReferences;UpdateWebConfig" />
I didn't show the target that actually deploys the site, but if you want to see what that looks like, drop me a comment and I'll pass that along.
Good Luck!
Ta.
Steve Porter