Deploying a WCF Service using a Web Setup Project

By jpsmit
March 29, 2010
0

With this blog post I hope I can save others some time and besides that it is also nicely stored for me in case I need it again.

For my current project I’m building some WCF services. One of the requirements is to have a MSI to hand-over to the system engineers for deployment.

The setup should:

  1. support deployment of the WCF artifacts
  2. be able to be deployed to whatever website is available on the server
  3. be able to set ASP.NET security to NTLM
  4. be able to gather service settings during setup
  5. be able to change custom AppSettings in the web.config according to the settings

While I’m familiar with Visual Studio Setup projects, I came across some interesting challenges.

Requirement 1 and 2 are easy. The regular Web Setup project is capable of doing that out of the box. If you add a Web Setup project to your solution and specify the “File System on Target Machine”. Put the svc and web.config in the “Web Application Folder” and the output of the WCF project to the “bin” folder. This will deploy your WCF service to the website you specify in the setup wizard.

filesystem[1]

One caveat however is to set the value of the Virtual Directory as it is shown by default when you run the setup. This is by default the name of your WebSetup project. In my case this was <company>.<division>.<Project>.<Subproject>.<Setup>

This is definitely not the virtual directory name you want to see in IIS. This cannot be changed in Visual Studio because it is not shown as a property, you need a text editor like Notepad. So open the project file of the WebSetup project: the ‘vdproj’ file. Search for “VirtualDirectory” and change the value to something suitable.

The 3rd requirement is a bit more difficult. By default the web services are installed with anonymous authentication so to change that, a step in the setup should be appended. After some searching I found out I had to add a so called custom installer. That is a class that inherits the System.Configuration.Install.Installer class. You can override the ‘Install’ and/or ‘Uninstall’ methods to add custom code to the installer.

So I added a CustomInstaller Class Library project to host the CustomInstaller class. Next step was how to modify the authentication of the just installed web service? This can be done using WMI or ADSI and I decided to go for WMI. I already did some things with WMI but now I needed to do more. Unfortunately the documentation is not very ‘user friendly’ and after some trying I decided to look for a ‘WMI query analyzer’ or something like that to get at least an overview of the possible queries. I was very happy to find the WMI Explorer by KS-Soft. It is a free download that exactly does what I needed: give an overview of what is out there regarding classes, instances and properties. A very valuable tool.

I needed a query to access the properties of the WCF service. Therefore I needed to query the “IIsWebVirtualDirSetting” class, based on the virtual directory of my WCF service. But how to get the virtual directory name? The system engineer can change that value so we need to ask the runtime to get it.

By default the runtime keeps some variables that you can retrieve in your code. For WebSetup projects that are:

  • TARGETVDIR – virtual directory to be created
  • TARGETSITE – website where the virtual directory is to be created (site must exist), example: "/LM/W3SVC/1
  • TARGETAPPPOOL – application pool to use (must exist, it won’t be created)

If you supply the parameters (to which I will come back later), you can access the runtime context to get the variables using Context.Parameters. The code below does that and more.

wmicode[1]

I create a ManagementScope for “\localhostrootMicrosoftIISv2” to connect to the local IIS instance. Next I query for the IISWebVirtualDirSettings of my WCF service to get access to the properties. This query is based on the website name and the virtual directory. If there are multiple websites in IIS, we have to get the correct one.

With the following code we can iterate over the result (which should only contain one record) and set the property.

wmicode2[1]

So now the code is in place to set the authentication, but how do these parameters get into the Context object? You have to specify a custom action to your project and the custom installer is the action you need to add.

setupcustomaction[1]

Then you can specify properties on the custom installer and they will end up in the Context object.

setupcustomaction2[1]

As you can see the parameter starts with ‘/’ and the value is in double quotes and square brackets if it is a variable which is gathered during the setup process in the wizard. You can also add plain text like I did with ‘localhost’ in this case. Warning: do think about the double quotes, if forgotten nothing works and there are no messages to tell you why!

Requirement 4 is simply extending the user interface. You can find a very good blog post here so I won’t cover that in detail.

Finally the 5th requirement: set some AppSettings values in the web.config

The blog post I just mentioned also describes how to modify web.config settings from a setup project. There is one difference to my situation and that is that I have to change the configuration file for a process I haven’t loaded the host of. So I cannot use the OpenExeConfiguration method of the ConfigurationManager class. Also the web.config specific method OpenWebConfiguration of the WebConfigurationManager class cannot be used.

Via WMI I was able to find the physical path of the web.config and I was almost thinking about using some XmlDocument class to read and process the settings when I found this forum topic.

It describes exactly what I need. I have to use the OpenMappedWebConfiguration method of the WebconfigurationManager class. Then I can still use the capabilities of this class to modify the settings without having to foul around with XML tags myself. With this in place it’s a piece of cake to change the settings.

modifywebconfig[1]

Looking back everything is very easy, as always, but I had a hard time finding the right information. Also I have my doubts about WMI over ADSI. It seems like the ADSI queries are more intuitively but I’m not sure about that.

If you have a better, easier, quicker or fancier solution, please let me know!

Comments: 0

Leave a Reply

Your email address will not be published. Required fields are marked *