Detect IIS website is suspended - c#

I am currently able to detect whether a IIS Website is started/paused/stopped using the following code:
public int GetWebsiteStatus(string machineName, int websiteId)
{
DirectoryEntry root = new DirectoryEntry(
String.Format("IIS://{0}/W3SVC/{1}", machineName, websiteId));
PropertyValueCollection pvc = root.Properties["ServerState"];
return pvc.Value
// - 2: Website Started
// - 4: Website Stopped
// - 6: Website Paused
}
I also want to detect if a Website is suspended or not. If the Website is suspended the method above still returns 2 (which is correct) but not enough for me.
I cannot find any code which do the job for IIS6 and higher.

Ah, do you mean the App Pool as stopped because of the timeout configuration? This is a different state to the web site remember? Well, certainly, you could change the settings so it doesn't recycle, but you could also try using code like this;
First, add a reference to \Windows\System32\inetsrv\Microsoft.Web.Administration.dll, then;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Administration;
namespace MSWebAdmin_Application
{
class Program
{
static void Main(string[] args)
{
ServerManager serverManager = new ServerManager();
Site site = serverManager.Sites["Default Web Site"];
// get the app for this site
var appName = site.Applications[0].ApplicationPoolName;
ApplicationPool appPool = serverManager.ApplicationPools[appName];
Console.WriteLine("Site state is : {0}", site.State);
Console.WriteLine("App '{0}' state is : {1}", appName, appPool.State);
if (appPool.State == ObjectState.Stopped)
{
// do something because the web site is "suspended"
}
}
}
}
That code will independantly check the state of your appPool as opposed to your web site. It's possible for the web site to return "started" and the appPool to return "stopped".
See if it works in your case.

You might want to try using the following code, add your own logic and tidy up of course... but in essence you need to do the following and modify your code as you see fit.
Add the following enum
public enum ServerState
{
Unknown = 0,
Starting = 1,
Started = 2,
Stopping = 3,
Stopped = 4,
Pausing = 5,
Paused = 6,
Continuing = 7
}
Search for site and process it...
DirectoryEntry w3svc = new DirectoryEntry("IIS://" + "localhost" + "/W3SVC");
//check each site
foreach (DirectoryEntry site in w3svc.Children)
{
foreach (var s in site.Properties)
{
try
{
ServerState state =
(ServerState)
Enum.Parse(typeof (ServerState), site.Properties["ServerState"].Value.ToString());
if (state == ServerState.Paused)
{
//Do action
}
}
catch (Exception)
{
}
}
}
I hope this is useful for you as well...
http://csharp-tipsandtricks.blogspot.co.uk/2009_12_01_archive.html

Related

Ho to take server offline / bring server online IIS web farm server programmatically

I am developing a C# application to automate process of deploying website to the server.The website is hosted in a web farm in WINDOWS SERVER 2012 R2. So the problem here is I am trying to take server offline or bring it online by means of some programming interface. but I couldn't find anything related inside Microsoft docs. How do I get the job done?
UPDATE:
As suggested by Timur I did as following, but it didn't work.
ServiceController p = new ServiceController("W3SVC","SERVER_IP");
p.Start();
p.WaitForStatus(ServiceControllerStatus.Running);
This is the sample that generated by configuration manager. It take server offline/online by change the Enabled property of server item in web farm collection.
using System;
using System.Text;
using Microsoft.Web.Administration;
internal static class Sample
{
private static void Main()
{
using (ServerManager serverManager = new ServerManager())
{
Configuration config = serverManager.GetApplicationHostConfiguration();
ConfigurationSection webFarmsSection = config.GetSection("webFarms");
ConfigurationElementCollection webFarmsCollection = webFarmsSection.GetCollection();
ConfigurationElement webFarmElement = FindElement(webFarmsCollection, "webFarm", "name", #"123213");
if (webFarmElement == null) throw new InvalidOperationException("Element not found!");
ConfigurationElementCollection webFarmCollection = webFarmElement.GetCollection();
ConfigurationElement serverElement = FindElement(webFarmCollection, "server", "address", #"11.1.1.1");
if (serverElement == null) throw new InvalidOperationException("Element not found!");
serverElement["enabled"] = false;
serverManager.CommitChanges();
}
}
private static ConfigurationElement FindElement(ConfigurationElementCollection collection, string elementTagName, params string[] keyValues)
{
foreach (ConfigurationElement element in collection)
{
if (String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase))
{
bool matches = true;
for (int i = 0; i < keyValues.Length; i += 2)
{
object o = element.GetAttributeValue(keyValues[i]);
string value = null;
if (o != null)
{
value = o.ToString();
}
if (!String.Equals(value, keyValues[i + 1], StringComparison.OrdinalIgnoreCase))
{
matches = false;
break;
}
}
if (matches)
{
return element;
}
}
}
return null;
}
}
IIS is a Windows service. Therefore the easiest way to start/stop it will be to do something along the lines of this SO answer.
You'll be looking for service name, which likely depends on your version.
UPD see an artist's impression on how your management tool might look like
var hostNames = new List<string> { "appServer1", "webServer1", "webServer2" };
foreach (var host in hostNames)
{
var svc = new ServiceController("W3SVC", host);
svc.Stop();
svc.WaitForStatus(ServiceControllerStatus.Stopped);
Thread.Sleep(10000);// or your custom logic
svc.Start();
svc.WaitForStatus(ServiceControllerStatus.Running);
}
bear in mind, you'll need to run this as a user with sufficient privileges to successfully change service state: as in you need to run this as Admin.
You've got at least two options to do it:
Run your IDE as admin
Update your application manifest as described in this answer
UPD2 apparently you can interface with WFF controller like so

Get Number of Requests Queued in IIS using C#

I need to collect following two informations from WebRole running IIS-8 on Azure.
Number of requests queued in IIS
Number of requests current being processed by worker
Since we are on Azure cloud service, I believe it would be better to stick together with default IIS configuration provided by Azure.
Approach 1: Use WorkerProcess Request Collection
public void EnumerateWorkerProcess()
{
ServerManager manager = new ServerManager();
foreach (WorkerProcess proc in manager.WorkerProcesses)
{
RequestCollection req = proc.GetRequests(1000);
Debug.WriteLine(req.Count);
}
}
Cons:
Requires RequestMonitor to be enabled explicitly in IIS.
Approach 2: Use PerformanceCounter class
public void ReadPerformanceCounter()
{
var root = HostingEnvironment.MapPath("~/App_Data/PerfCount.txt");
PerformanceCounter counter = new PerformanceCounter(#"ASP.NET", "requests current", true);
float val = counter.NextValue();
using (StreamWriter perfWriter = new StreamWriter(root, true))
{
perfWriter.WriteLine(val);
}
}
Cons:
Requires higher privilege than currently running IIS process.
P.S. There has been a four years old SO post but not answered well.

Resolve 'configuration object is read only, because it has been committed by a call to ServerManager.CommitChanges()'?

I've written a custom action for an installer project that does the following:
Checks existing websites to see if any exist with the same name put
in by the user.
Creates the website in IIS if it doesn't exist.
Creates an application pool.
Assigns the application pool to the created website.
When it comes to assigning the application pool I get and error:
The configuration object is read only, because it has been committed
by a call to ServerManager.CommitChanges(). If write access is
required, use ServerManager to get a new reference.
This baffles me as it seems to suggest that I can't assign the newly created application pool with the ServerManager.CommitChanges() call. However, everything else works fine using this, which I wouldn't expect if this was an issue.
Here is my code:
I have a ServerManager instance created like so:
private ServerManager mgr = new ServerManager();
In my Install method I do the following:
Site site = CreateWebsite();
if (site != null)
{
CreateApplicationPool();
AssignAppPool(site);
}
Check existing websites - done in OnBeforeInstall method
private Site CheckWebsites()
{
SiteCollection sites = null;
Site site = null;
try
{
sites = mgr.Sites;
foreach (Site s in sites)
{
if (!string.IsNullOrEmpty(s.Name))
{
if (string.Compare(s.Name, targetSite, true) == 0) site = s;
}
}
}
catch{}
return site;
}
CreateWebSite method:
private Site CreateWebsite()
{
Site site = CheckWebsites();
if (site == null)
{
SiteCollection sites = mgr.Sites;
int port;
Int32.TryParse(targetPort, out port);
site = sites.Add(targetSite, targetDirectory, port);
mgr.CommitChanges();
}
else
{
//TO DO - if website already exists edit settings
}
return site;
}
Create App Pool
//non-relevant code...
ApplicationPool NewPool = mgr.ApplicationPools.Add(ApplicationPool);
NewPool.AutoStart = true;
NewPool.ManagedRuntimeVersion = "4.0";
NewPool.ManagedPipelineMode = ManagedPipelineMode.Classic;
mgr.CommitChanges();
Assign App Pool
private void AssignAppPool(Site site)
{
site.ApplicationDefaults.ApplicationPoolName = ApplicationPool; //ERRORS HERE
mgr.CommitChanges();
}
I can't see why a site could be created, an app pool created but then not assigned. Help.
I finally realised that the 'configuration object' referred to in the error was the 'site'. Seems obvious now, but basically I needed to re-get the site to then assign the app pool to it. I think this is allow the previous changes to take place and then pick them up. So I altered my code by removing the need to pass the Site into private void AssignAppPool() and just getting the site again like this:
Site site = mgr.Sites["TestWebApp"];

Get a list of sites running under IIS on a remote server?

I have a page running under IIS 6.0 on server Foo. I have some other sites also running under IIS 6.0 on a remote server Baz. I want to ping Baz with Foo with ASP.NET to retrieve a list of sites running on it. How can I do this?
Possibly like this, except in C# instead of VB.
This tells me that using Microsoft.Web.Administration.dll is not really an option because it's not distributable and only available on IIS 7.
Here's a code snippet to get a list of running Web Sites using Microsoft.Web.Administration, this DLL is located here : c:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll
class Program
{
static void Main(string[] args)
{
string serverName = "localhost";
using (Microsoft.Web.Administration.ServerManager sm = Microsoft.Web.Administration.ServerManager.OpenRemote(serverName))
{
int counter = 1;
foreach (var site in sm.Sites)
{
Console.Write(String.Format(CultureInfo.InvariantCulture, "Site number {0} : {1}{2}", counter.ToString(), site.Name, Environment.NewLine));
counter++;
}
}
Console.ReadLine();
}
}
Replace "locahost" with the remote server name.
Hope this works for IIS 6 (i tried it with IIS 7.5 only ;-))
I believe you can achieve this with System.DirectoryServices
string path = "IIS://{yourservername}/W3SVC";
using (DirectoryEntry w3svc = new DirectoryEntry(path))
{
foreach (DirectoryEntry entry in w3svc.Children)
{
if (entry.SchemaClassName == "IIsWebServer")
{
string websiteName = (string)entry.Properties["ServerComment"].Value;
}
}
}
Make sure you've enabled remote IIS administration on Baz

Programatically determine if IIS site is running or not

I have a script that gathers up all sites in IIS and emails a few details for auditing. I want to adjust it so that it only emails sites that are running. I do not need to know about sites that are stopped. I already have a reference to all of the DirectoryEntrys in IIS but I don't see any properties that would indicate if it is running or not.
How is this done? Ideally this should run on both IIS6 and IIS7.
The DirectoryEntry.Properties collection, contains a ServerState property. It's not documented very well but I found this blogger that created his own enumeration which appears to be correct. The enum is
public enum ServerState
{
Unknown = 0,
Starting = 1,
Started = 2,
Stopping = 3,
Stopped = 4,
Pausing = 5,
Paused = 6,
Continuing = 7
}
Using this, the logic to check if a DirectoryEntry is running, you would use:
DirectoryEntry entry;
ServerState state = (ServerState)Enum.Parse(typeof(ServerState), entry.Properties["ServerState"].Value.ToString())
if (state == ServerState.Stopped || state == ServerState.Paused)
{
//site is stopped
}
{

Categories

Resources