Microsoft.Web.Administration is very useful providing read and write access to the IIS configuration in a simple way.
However, I stumbled across a peculiar issue when trying to add a SSL binding to a website located on another machine. Here’s my code (*asterisk denotes code I’ve hidden on purpose):
using (ServerManager mgr = ServerManager.OpenRemote(serverName)) { // Code to create the website* // Adding bindings // this webSite variable is of a custom type I've created to simplify things for (int i = 0; i < webSite.Bindings.Count; i++) { var binding = webSite.Bindings[i]; string bindingInformation = string.Format("{0}:{1}:{2}", binding.IpAddress, binding.TcpPort, binding.HostName); // iisSite is of type Microsoft.Web.Administration.Site iisSite.Bindings.Add(bindingInformation, binding.CertificateHash, binding.CertificateStore); } mgr.CommitChanges(); webSite.Id = iisSite.Id; }
I kept getting this exception:
NotSupportedException: The specified operation is not supported when a server name is specified.
Upon investigating the BindingCollection class, I’ve found the culprit:
Setting BindingInformation also loads the Endpoint. Curiously, this wouldn’t be a problem, however both CertificateHash and CertificateStore also have some additional logic to set their values:
When its underlying variable is null OR the EndPoint is null, it just sets the value. Otherwise, it sets the value and adds/modifies the associated binding transaction. Further investigating this method, I’ve noticed it doesn’t work when the ServerManager is open remotely:
Check inside the EnsureLocal method:
So instead of adding the Binding through the BindingCollection.Add method, I’ve changed my code to this:
using (ServerManager mgr = ServerManager.OpenRemote(serverName)) { // Code to create the website* // Adding bindings // this webSite variable is of a custom type I've created to simplify things for (int i = 0; i < webSite.Bindings.Count; i++) { var binding = webSite.Bindings[i]; // Binding class = Microsoft.Web.Administration.Binding Binding iisBinding = iisSite.Bindings.CreateElement(); iisBinding.Protocol = protocol; if (binding.Type == Business.IIS.Binding.BindingType.Https) { try { iisBinding.CertificateHash = binding.CertificateHash; iisBinding.CertificateStoreName = binding.CertificateStoreName; } catch (Exception) { // Error treatment* } } iisBinding.BindingInformation = string.Format("{0}:{1}:{2}", binding.IpAddress, binding.TcpPort, binding.HostName); // iisSite is of type Microsoft.Web.Administration.Site iisSite.Bindings.Add(iisBinding); } mgr.CommitChanges(); webSite.Id = iisSite.Id; }
By creating the Binding manually and setting the BindingInformation at last (rather than at the beginning), the problem was gone!
Must confess, though. I have no clue why that method in particular had to “ensure it’s the local server”.