I had just finished creating the gl-createwebapp command and was about to go and delete the test web applications that I had created while testing the command and I discovered that there is no built in stsadm command for deleting a web application. I took a look at the code that is called when deleting via the browser and decided that it was simple enough that I could recreate it as a command – can’t have a create without a delete :). The command I created is called gl-deletewebapp.

The only tricky spot I had was that there was a couple of pieces of code that required the use of internal classes – fortunately I’m getting pretty good at making calls to these internal classes so it didn’t really slow me down much. The main hang up was that the Unprovision() method of the SPWebApplication object doesn’t allow you to specify whether you want to delete the IIS web site or not – it just defaults to deleting it.

I wanted this command to function like the browser equivalent so I had to use the internal UnprovisionIisWebSites() method along with the RetractSolutions method of the SPSolutions class. And to make sure that I was mirroring the code executed when using the browser I had to also set a timer job using an internal only class (there’s gotta be a way around this one). The code to do all this is below:

  1public override int Run(string command, System.Collections.Specialized.StringDictionary keyValues, out string output)
  2{
  3    output = string.Empty;
  4
  5    InitParameters(keyValues);
  6
  7    string url = Params["url"].Value;
  8    bool deleteContent = Params["deletecontentdb"].UserTypedIn;
  9    bool deleteIis = Params["deleteiiswebsite"].UserTypedIn;
 10
 11    SPWebApplication webApp = SPWebApplication.Lookup(new Uri(url));
 12    if (webApp.IisSettings.Count <= 0 && deleteContent)
 13    {
 14        DeleteContentDBs(webApp);
 15        webApp.Delete();
 16        return 1;
 17    }
 18
 19    int index = 0;
 20    string[] serverComments = new string[webApp.IisSettings.Count];
 21    string[] vdirs = new string[webApp.IisSettings.Count];
 22    foreach (SPIisSettings iisSetting in webApp.IisSettings.Values)
 23    {
 24        vdirs[index] = iisSetting.Path.ToString();
 25        serverComments[index] = iisSetting.ServerComment;
 26        index++;
 27    }
 28
 29    // webApp.Unprovision() does not allow us to specify whether we want the site deleted so we have to use the internal version.
 30    // SPWebApplication.UnprovisionIisWebSites(deleteIis, serverComments, webApp.ApplicationPool.Name);
 31    MethodInfo unprovisionIisWebSites = webApp.GetType().GetMethod("UnprovisionIisWebSites",
 32            BindingFlags.NonPublic | BindingFlags.Public |
 33            BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Static,
 34            null, new Type[] {typeof(bool), typeof(string[]), typeof(string)}, null);
 35
 36    unprovisionIisWebSites.Invoke(webApp, new object[] { deleteIis, serverComments, webApp.ApplicationPool.Name });
 37
 38
 39    // SPSolution.RetractSolutions(webApp.Farm, webApp.Id, vdirs, serverComments, true);
 40    MethodInfo retractSolutions = typeof(SPSolution).GetMethod("RetractSolutions",
 41        BindingFlags.NonPublic | BindingFlags.Public |
 42        BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Static,
 43        null, new Type[] {typeof(SPFarm), typeof(Guid), typeof(string[]), typeof(string[]), typeof(bool)}, null);
 44
 45    retractSolutions.Invoke(null, new object[] { webApp.Farm, webApp.Id, vdirs, serverComments, true });
 46
 47
 48    if (SPFarm.Local.TimerService.Instances.Count > 1)
 49    {
 50        // SPIisWebsiteUnprovisioningJobDefinition is an internal class so we need to use reflection to set it.
 51
 52        // SPIisWebsiteUnprovisioningJobDefinition jobDef = new SPIisWebsiteUnprovisioningJobDefinition(deleteIis, serverComments, webApp.ApplicationPool.Name, vdirs, webApp.Id, true);
 53        Type sPIisWebsiteUnprovisioningJobDefinitionType = Type.GetType("Microsoft.SharePoint.Administration.SPIisWebsiteUnprovisioningJobDefinition, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
 54
 55        ConstructorInfo sPIisWebsiteUnprovisioningJobDefinitionConstructor =
 56        sPIisWebsiteUnprovisioningJobDefinitionType.GetConstructor(
 57        BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
 58        null,
 59        new Type[] {typeof(bool), typeof(string[]), typeof(string), typeof(string[]), typeof(Guid), typeof(bool)}, null);
 60        object jobDef = sPIisWebsiteUnprovisioningJobDefinitionConstructor.Invoke(new object[] { deleteIis, serverComments, webApp.ApplicationPool.Name, vdirs, webApp.Id, true });
 61
 62
 63        // jobDef.Schedule = new SPOneTimeSchedule(DateTime.Now);
 64        PropertyInfo scheduleProp = sPIisWebsiteUnprovisioningJobDefinitionType.GetProperty("Schedule",
 65                    BindingFlags.FlattenHierarchy |
 66                    BindingFlags.NonPublic |
 67                    BindingFlags.Instance |
 68                    BindingFlags.InvokeMethod |
 69                    BindingFlags.GetProperty |
 70                    BindingFlags.Public);
 71
 72        scheduleProp.SetValue(jobDef, new SPOneTimeSchedule(DateTime.Now), null);
 73
 74        // jobDef.Update();
 75        MethodInfo update = sPIisWebsiteUnprovisioningJobDefinitionType.GetMethod("Update",
 76                    BindingFlags.NonPublic |
 77                    BindingFlags.Public |
 78                    BindingFlags.Instance |
 79                    BindingFlags.InvokeMethod |
 80                    BindingFlags.FlattenHierarchy,
 81                    null,
 82                    new Type[] { }, null);
 83
 84
 85        update.Invoke(jobDef, new object[] { });
 86    }
 87
 88    if (deleteContent)
 89        DeleteContentDBs(webApp);
 90
 91    webApp.Delete();
 92
 93    return 1;
 94}
 95 
 96 
 97private static void DeleteContentDBs(SPWebApplication webApp)
 98{
 99    foreach (SPContentDatabase db in webApp.ContentDatabases)
100    {
101        db.Unprovision();
102    }
103}

The syntax of the command can be seen below:

C:\>stsadm -help gl-deletewebapp

stsadm -o gl-deletewebapp

Deletes a web application.

Parameters:
        -url 
        [-deleteiiswebsite]
        [-deletecontentdb]

Here’s an example of how to delete a web application without deleting the IIS web site or the content database:

stsadm –o gl-deletewebapp -url "http://webappname"

Here’s another example which will delete the IIS web site and the content database:

stsadm -o gl-deletewebapp -url "http://webappname" -deleteiiswebsite -deletecontentdb