Wednesday, April 08, 2015

Resolving "Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'" in IIS with WCF web services

If you get an error that looks like "Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'" in IIS while testing WCF web services, it most likely means that you haven't correctly specified the certificate for your service in IIS. If you're certain you've correctly specified the certificate, it most likely means that the IIS user doesn't have access to it, so you'll have to go in and manually grant permissions for it.

Removing a certificate binding from a port in Windows

As many people don't know, in Windows certificates can be bound to ports for securing content transferred over those ports. IIS happens to be particularly negatively affected by this if another program has a certificate bound to a port that you want to use, e.g. 443 for serving web pages.

Use the information at the following page to find the certificate binding and delete it :

https://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx

The short version:

Find the port: netsh http show sslcert | grep -C 5 443

This command will show all the SSL certificates that are bound to ports on your machine.

Delete the port: netsh http delete sslcert ipport=0.0.0.0:443

This should help deal with some of the more annoying (and less verbose) errors when doing things like trying to configure WCF services to use SSL.

Friday, March 20, 2015

Sunday, March 15, 2015

Using Ninject in an IIS-hosted environment to create a WCF service instance that's in a shared assembly (i.e. not the web application assembly)

Recently I wanted to create several WCF services for some of our internal applications and host these services in multiple different servers. There'd be no difference in the code between the locations where they'd be hosted. In order to avoid code duplication, I wanted to make these WCF services common and then just host them in the IIS applications. However, it's not as simple as creating a new (shared) assembly, creating the WCF service in that assembly and then sharing it between the different services. When I tried this, I ran into the problem where even though I specified the service in the config section:

<system.serviceModel>
         ....
</system.serviceModel> 
 
... it still wouldn't find my service and load it (I'm using Ninject as a dependency injection container for my WCF services).  They key to getting the shared services to be found and loaded properly was to create a .svc file in each of the services where the WCF services were shared, and it looked something like this in each case:

<%@ ServiceHost Language="C#" Debug="false" Service="MyWcfSharedServices.MySharedWebService" Factory="Ninject.Extensions.Wcf.NinjectServiceHostFactory" %>

After adding the .svc file and using the path to it in the system.serviceModel configuration in the configuration file, everything worked perfectly.

Wednesday, January 28, 2015

Resolving "The agent process was stopped while the test was running" on a TFS build agent

I'm currently working on a project that uses TFS for automated builds. Recently, our builds started failing with the error "The agent process was stopped while the test was running" while running unit tests. It wouldn't always happen during the same test, though it did frequently.

After inspecting the Event Viewer on the build machines, we found an error in the .NET Runtime that was taking down the build agent:

Application: QTAgent32_40.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: Moq.MockException
Stack:
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__5(System.Object)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
The exception that was being thrown on a ThreadPoolThread (always extremely bad, never good) was a MockException: our unit testing framework was throwing an exception that was taking down the build agent.  But where?

How We Solved It

By some miracle, one of our developers decided to run our problematic tests with mstest on the command line on his local machine. As it turns out, this was great because it showed the stack traces for the loose threads on the command line. Turns out we had a lot more loose threads than just the one that was taking down our test agent on the build machine. The command the developer used was (similar to) the following:

mstest.exe /testcontainer:"C:\path\to\my\test.dll" /noisolation

After auditing all of the errors by rooting them out with command line runs of mstest, the solutions to our problems all boiled down to one thing:

*ALWAYS* wrap the contents of 'async void' methods with a try-catch block!

Thursday, January 22, 2015

Implementing a WCF service on an Active Directory domain using a ServiceHost in a Windows Service with Windows authentication and a non-system user service account

So, as it turns out, when you want to implement a WCF service on an Active Directory domain using a ServiceHost in a Windows Service with Windows authentication and a non-system user service account, you have to jump through a few hoops for the configuration. I kept getting the error message "A call to SSPI failed. see inner exception". The inner exception is "The target principal name is incorrect.". Like many other people, I kept thinking it had to do with authentication of the *client*. As it turns out, like those other people, I was wrong. It was to do with *verification of the service account running the service*. This is presumably because the *service user is a domain user service account*. To get this scenario working, you have to specify the name of the service user account as the 'userPrincipalName' in the 'identity' element of the 'endpoint' element for your service, like so:

<configuration>
<system.serviceModel>
<services>
<service name="MyProject.MyService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:12345/MyService"/>
</baseAddresses>
</host>
<endpoint name="MyServiceTcpEndpoint"
 address=""
 binding="netTcpBinding"
 bindingConfiguration="MyServiceTcpBinding"
 contract="MyProject.IMyService">
<identity>
<userPrincipalName value="MyDomainName\MyServiceUserName"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="MyServiceTcpBinding"
transferMode="Buffered"
maxReceivedMessageSize="65535">
<security mode="Transport">
<!-- Use Windows authentication to ensure that we at least have authentication if not encryption -->
<transport clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
Now you'll get proper connections and authentication via Windows.

Adding files generated at build time to your ClickOnce deployment

So, it used to be that I didn't really like ClickOnce deployment, but it turns out it has a lot of benefits:

  • It provides infrastructure that makes it *exceedingly* easy for your average lay-user to upgrade. All they have to do is start the program as normal, and it automatically updates for them (when configured properly)
  • It bundles everything up nicely and is easy to install and uninstall (if you're a real person. If you're a script, not so much)
I recently encountered the problem of including files generated at build time (e.g. transformed configuration files) in the ClickOnce deployment package. I found the following that I could include in my ClickOnce project's .csproj MSBuild file:

 <Content Include="Path\To\My.file" Condition=" Exists('Path\To\My.file') ">
<Visible>false</Visible>
 </Content>
 The key part to getting it to work properly is that your build has to do a Build target first, then a Publish target (usually with two separate invocations of MSBuild via a build script, rather than building with a Solution). Fortunately, I was already set up for that, so getting the extra files included was a dream.

Friday, January 16, 2015

Querying XML with namespaces in PowerShell

Apparently it's ridiculously easy to parse XML in PowerShell once you have the right objects set up:

[xml] $xml = Get-Content .\DeploymentProfile.PRODUCTION.xml
$ns = new-object Xml.XmlNamespaceManager $xml.NameTable
$ns.AddNamespace('dns', 'http://www.mycompany.com/Some/Namespace/Deployment/DeploymentProfiles.xsd')
$xml.SelectNodes("//@dns:ServerHostName", $ns) | ForEach-Object { $_.Value } | sort | Get-Unique

In this example, I query a deployment configuration for attributes called "ServerHostName" within the XML namespace "http://www.mycompany.com/Some/Namespace/Deployment/DeploymentProfiles.xsd". This was exceptionally useful today when I quickly needed to figure out what servers my system was using and send that information to others to configure those servers.

Implementing Code Analysis with Team Foundation Server 2012 and later

I've been looking to get Code Analysis going on our team project at work. I found this helpful MSDN article.

Adding a computer to the local machine's list of PowerShell TrustedHosts

Retrieve the current list:

$curValue = (get-item wsman:\localhost\Client\TrustedHosts).value

Set the new list, appending the new host onto the old list:

set-item wsman:\localhost\Client\TrustedHosts -value "$curValue, Server01.Domain01.Fabrikam.com"

Tuesday, December 09, 2014

Things I've learned (forcibly) on 2014-12-09


  1. There are database systems that are actually implemented using the Julian (sort of) calendar. Why ?!
  2. It is perfectly legitimate to use script types other than type="text/javascript" in a