IIS Configuration Woes with ADSI, WMI, and VBScript

You know, configuration of IIS can be quite confusing, with the various interfaces, paradigms, and programming languages. However, if you keep your wits about you, you can avoid the common pitfalls...

Question:

I need help with the following code. I'm trying to create an application pool to set as the default pool when I make a website. When I create my web site with this code the application pool is set to <Invalid App Pool>, but then in the drop down choice box the <DefaultAppPool> and my <MyCrazyAppPool> exist. I need to have <MyCrazyAppPool> set as the default though when the site is created, not <InvalidAppPool>.

All help is greatly appreciated. Thank you for your time.

 Set objSite = objWMIService.Get("IIsWebServerSetting='" & strSitePath & "'")
Set objVirtualDirectory = objWMIService.Get("IIsWebVirtualDirSetting='" & strSitePath & "/ROOT'")

' Application Pool Creation

strAppPool = "MyCrazyAppPool"
Set objAppPools = GetObject("IIS://localhost/W3SVC/AppPools")
Set objAppPool = objAppPools.Create("IIsApplicationPool", strAppPool)
objAppPool.SetInfo

' Assign the Pool to the Site
objVirtualDirectory.AppPoolID = strAppPool
objVirtualDirectory.AppFriendlyName = "me app"
objVirtualDirectory.SetInfo

Answer:

Actually, your code illustrates several common problems and misconceptions. Let me dissect them all and hopefully you see what you need to do to fix them.

Problem 1: ADSI vs WMI

Your script snippet actually uses two completely different programming paradigms, with non-interchangeable features and syntax, to configure IIS - ADSI and WMI.

 strAppPool = "MyCrazyAppPool"
Set objAppPools = GetObject("IIS://localhost/W3SVC/AppPools")
Set objAppPool = objAppPools.Create("IIsApplicationPool", strAppPool)
objAppPool.SetInfo

The above snippet uses ADSI, as evidenced by the GetObject() function call using the IIS:// namespace. It is using generic ADSI Create() syntax to create a new Application Pool and then save it with the generic ADSI SetInfo() syntax. One reason ADSI modifications are not immediately persisted but allow batched persistence by SetInfo() is to give the programmer fine-control over the balance between performance and immediacy. If you want immediate persistence, then call SetInfo() yourself as frequently as you want; if you want batched behavior, batch and then call SetInfo().

 Set objSite = objWMIService.Get("IIsWebServerSetting='" & strSitePath & "'")
Set objVirtualDirectory = objWMIService.Get("IIsWebVirtualDirSetting='" & strSitePath & "/ROOT'")

' Assign the Pool to the Site
objVirtualDirectory.AppPoolID = strAppPool
objVirtualDirectory.AppFriendlyName = "me app"
objVirtualDirectory.SetInfo

The above code snippet uses WMI, as evidenced through the objWMIService being created against the root/MicrosoftIISv2 namespace (which you did not show but must have done). It is using the generic WMI Get() syntax to retrieve a vdir definition for the website. However, it is attempting to use the ADSI SetInfo() syntax to save the changes, which is not supported. WMI uses Put_() to save the changes.

The syntax error prevents your customization of AppPoolId from ever persisting.

  • One way to correct it is to use the correct WMI function to commit the change:
 objVirtualDirectory.Put_()
  • The other way is to use ADSI (where strSitePath looks like "w3svc/1"):
 objVirtualDirectory = GetObject( "IIS://localhost/" + strSitePath + "/ROOT" )

Problem 2: Error Checking

Now, we know that objVirtualDirectory is a WMI object, SetInfo() is an ADSI syntax, and the WMI object does not support the SetInfo() syntax and its execution should trigger an error that breaks the VBScript. However, your description indicates that you did not notice this syntax error at all - you noticed that the AppPoolId was not customized (because the syntax error prevented the change from persisting). Where did the syntax error go?

Well, you must have specified something like: On Error Resume Next somewhere earlier in the script in the applicable scope, which you did not show. This directive tells VBScript to silently ignore all errors, including the syntax error which prevented AppPoolId customization from persisting, and simply continue execution at the following statement. This leads to the script seemingly run to completion, yet the expected change did not happen. This is the classic problem of using "On Error Resume Next".

Thus, it should be clear that if you EVER use "On Error Resume Next", you need to diligently Clear and check the return code of the Err object after every function invocation to detect errors and handle errors yourself  Yes, it can be a hassle and make the code look ugly and complicated, but you have no choice - you need to write code without glaring bugs and flaws. You can make it look prettier and simpler by writing better error handling abstractions (or just using a language with better error handling semantics).

Sure, by adding "On Error Resume Next", your VBScript stopped bailing execution due to errors, but just because code continues running does NOT mean that everything worked and the fatal errors masked by "On Error Resume Next" were ok. In reality, computers simply don't know how to fix them, so they simply stay silently masked until you DO notice the problem (like the AppPoolId change failing to show up).

Conclusion

Yeah, this nasty masking of errors and general hassle in handling errors really bug me about VBScript. It's all nice and easy assuming nothing bad happens, but when you need to do something REAL like handle real-world errors, you need to use "On Error Resume Next". And at that point, I end up writing VBScript code that exactly mirrors defensive C/C++ style code... by which point I am really not scripting... and then I get annoyed with the language itself. But that's another topic for another day. :-)

//David