You may have read my recent post regarding a bug with SharePoint which occurs when you create or extend a web application using an invalid directory path. I found that the fix for the bug was pretty simple and just required updating the invalid path property so that the unextendvs or deletewebapp commands could work. What I decided to do was to update the deletewebapp command with this code and create a new command which accomplishes the same function as the unextendvs command (or more specifically the “Remove SharePoint from IIS Web Site” Central Admin page) but adds in this bug fix so that invalid web applications can be unextended (something that the UI and built-in stsadm command could not do). The new command I created is called gl-unextendwebapp. The code is very similar to the deletewebapp command:

 1public static void UnExtend(SPWebApplication webApp, SPUrlZone zone, bool deleteIis)
 2{
 3    SPIisSettings settings = webApp.IisSettings[zone];
 4    try
 5    {
 6        DirectoryInfo path = settings.Path;
 7        if (!path.Exists)
 8            throw new Exception();
 9    }
10    catch (Exception)
11    {
12        settings.Path = new DirectoryInfo("c:\\");
13        webApp.Update();
14    }
15 
16    string[] serverComments = new string[] { settings.ServerComment };
17    string[] vdirs = new string[] { settings.Path.ToString() };
18 
19    // webApp.Unprovision() does not allow us to specify whether we want the site deleted so we have to use the internal version.
20    // SPWebApplication.UnprovisionIisWebSites(deleteIis, serverComments, webApp.ApplicationPool.Name);
21    Utilities.ExecuteMethod(webApp, "UnprovisionIisWebSites",
22                            new Type[] { typeof(bool), typeof(string[]), typeof(string) },
23                            new object[] { deleteIis, serverComments, webApp.ApplicationPool.Name });
24 
25 
26    // SPSolution.RetractSolutions(webApp.Farm, webApp.Id, vdirs, serverComments, true);
27    Utilities.ExecuteMethod(typeof(SPSolution), "RetractSolutions", 
28                            new Type[] { typeof(SPFarm), typeof(Guid), typeof(string[]), typeof(string[]), typeof(bool) },
29                            new object[] { webApp.Farm, webApp.Id, vdirs, serverComments, true });
30 
31 
32    if (SPFarm.Local.TimerService.Instances.Count > 1)
33    {
34        // SPIisWebsiteUnprovisioningJobDefinition is an internal class so we need to use reflection to set it.
35 
36        // SPIisWebsiteUnprovisioningJobDefinition jobDef = new SPIisWebsiteUnprovisioningJobDefinition(deleteIis, serverComments, webApp.ApplicationPool.Name, vdirs, webApp.Id, true);
37        Type sPIisWebsiteUnprovisioningJobDefinitionType = Type.GetType("Microsoft.SharePoint.Administration.SPIisWebsiteUnprovisioningJobDefinition, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
38 
39        ConstructorInfo sPIisWebsiteUnprovisioningJobDefinitionConstructor =
40            sPIisWebsiteUnprovisioningJobDefinitionType.GetConstructor(
41                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
42                null,
43                new Type[] { typeof(bool), typeof(string[]), typeof(string), typeof(string[]), typeof(Guid), typeof(bool) }, null);
44        object jobDef = sPIisWebsiteUnprovisioningJobDefinitionConstructor.Invoke(new object[] { deleteIis, serverComments, webApp.ApplicationPool.Name, vdirs, webApp.Id, true });
45 
46        // jobDef.Schedule = new SPOneTimeSchedule(DateTime.Now);
47        Utilities.SetPropertyValue(jobDef, "Schedule", new SPOneTimeSchedule(DateTime.Now));
48 
49        // jobDef.Update();
50        Utilities.ExecuteMethod(jobDef, "Update", new Type[] {}, new object[] {});
51    }
52 
53    webApp.IisSettings.Remove(zone);
54    if (zone != SPUrlZone.Default)
55    {
56        webApp.AlternateUrls.UnsetResponseUrl(zone);
57        webApp.AlternateUrls.Update();
58    }
59    webApp.Update();
60}

The syntax of the command can be seen below:

C:\>stsadm -help gl-unextendwebapp

stsadm -o gl-unextendwebapp

Deletes a web application.

Parameters:
        -url <url>
        -zone <default | intranet | internet | custom | extranet>
        [-deleteiiswebsite]

Here’s an example of how to unextend an extended web application:

stsadm -o gl-unextendwebapp -url http://newportal -zone custom -deleteiiswebsite