Assign application pool to website in iis7 programmatically - c#

I'm using the following method to create application pool and assign it to web site.
This is code for application pool creation:
WebSiteName = some website name which existed.
I see that all values are get where they should be in debug mode
private void ConfiugreAppPoolIIS7(targetMachine)
{
var mgr = ServerManager.OpenRemote(targetMachine);
if (AppPoolProp.ContainsKey("SomeTestAppPool"))
{
string reqAppPool = AppPoolProp["SomeTestAppPool"];
if (!mgr.ApplicationPools.Any(pool => pool.Name == reqAppPool))
{
ApplicationPool myApp = mgr.ApplicationPools.Add(reqAppPool);
myApp.AutoStart = true;
myApp.ManagedPipelineMode = ManagedPipelineMode.Classic;
myApp.ManagedRuntimeVersion = "V4.0";
myApp.ProcessModel.IdentityType = ProcessModelIdentityType.NetworkService;
myApp.Enable32BitAppOnWin64 = true;
mgr.CommitChanges();
foreach (Site site in mgr.Sites)
{
if(site.Name == WebSiteName)
{
site.Stop();
site.ApplicationDefaults.ApplicationPoolName = myApp.Name;
site.Start();
mgr.CommitChanges();
}
}
}
}
}
The result is, I see the Aplication pool created successfuly, and the Website created as well, but, I see in the advanced settings of my web site the default appplication pool is assigned.
Could you please help?

The problem has been fixed by fixing reference to right Microsoft.Web.Administration.dll
Cause of this server manage connected to express iis instead of my local iis.

Related

ServerManager fails with 0x80040154 in c# Winforms app creating simple web application in IIS

I am writing a small app that installs IIS and configures a website before deploying the files to it.
On a freshly reset Windows 10, the first attempt always fails with the 0x80040154 COM+ component failure as documented in This question
I looked at the version I am using and it is the latest and correct one for .net standard (4.8) and not the one meant for .net core
When I press the button to rerun the function it always finishes correctly. I tried using a retry routine, and it fails on each retry, yet runs fine again when the button is pressed. The reason for this I assume is that the server manager object isn't disposed when it hits the catch block since its in a using statement.
I can work around that, but I really want to understand the issue and make a permanent fix.
My routine simply creates a website in IIS and creates an app pool to assign to it.
And it is running with elevated privileges
For reference:
Machine is Windows 10 latest from the downloadable media creator.
Microsoft.Web.Administrator version is 7.0.0.0
App is .net 4.8 standard windows forms
using (var serverManager = new ServerManager())
{
string iisrootdir = drive;
//Check for inetpub/wwwroot
if (!Directory.Exists(iisrootdir)) //Check for Drive D
{
iisrootdir = #"C:\";
}
string iiscmsdir = Path.Combine(iisrootdir, "webdir", "appdir");
if (!Directory.Exists(iiscmsdir))
Directory.CreateDirectory(iiscmsdir);
var settings = new ApplicationSettings();
settings.ReadFromFile();
settings.CMSPATH = iiscmsdir;
settings.SaveToFile();
try
{
string poolName = "DefaultAppPool";
if (serverManager.Sites.Count > 0)
{
Site myDefualtWebsite = serverManager.Sites[0];
if (myDefualtWebsite != null)
{
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Remove Default Website"));
serverManager.Sites.Remove(myDefualtWebsite);
serverManager.CommitChanges();
}
}
if (!WebsiteExists("sitename"))
{
mySite.ServerAutoStart = true;
}
Site site = serverManager.Sites["sitename"];
if (!AppPoolExists(poolName))
{
serverManager.ApplicationPools.Add(poolName);
}
ApplicationPool apppool = serverManager.ApplicationPools[poolName];
apppool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
apppool.ManagedRuntimeVersion = "";
serverManager.Sites["sitename"].ApplicationDefaults.ApplicationPoolName = poolName;
foreach (var item in serverManager.Sites["sitename"].Applications)
{
item.ApplicationPoolName = poolName;
}
serverManager.CommitChanges();
apppool.Recycle();
serverManager.CommitChanges();
}
catch (Exception ex)
{
if (ex.Message.Contains("80040154") && errorCount < 4)
{
if (serverManager != null)
serverManager.Dispose();
errorCount++;
OnRaiseInstallEvent(new InstallEventArgs("CreateWebsite", ProcessState.Started,
"Error encountered with COM+ object, trying again: " + errorCount));
CreateWebsite(#"D:\");
}
else
{
if (serverManager != null)
serverManager.Dispose();
errorCount = 0;
OnRaiseErrorEvent(new InstallErrorEventArgs("CreateWebsite", ProcessState.Error, ex));
return false;
}
}
finally
{
serverManager?.Dispose();
}
Thanks for the help Guys. I found the problem.
DISM was running in its own thread. The Process object exited the moment it launched. My function was then attempting to configure IIS before it had finished installing.

How to get the ApplicationPool object based on a Site in IIS

I've created an application that gets all of my Sites in IIS and checking each URL in Bindings if there's an HTTP error and if a certain error is encountered, my application will reset the IIS Site Instance in IIS, however, it's not enough to fix the error as it should. I would be needing to reset both the Site and the Application pool it belongs to.
Is there any way to get the Application Pool object based on the Site object?
I have tried the code below but this only works if an application pool only has 1 Site/Application in it. The problem is, I don't get the matching App Pool of the Site if I have a number mismatch between the List of Sites vs List of ApplicationPools since 1 AppPool can have multiple Sites/Applications.
ServerManager serverMgr = new ServerManager();
SiteCollection sites;
ApplicationPoolCollection appPools;
public List<(Site, ApplicationPool, string)> getSiteInfo()
{
List<(Site, ApplicationPool, string)> siteInfo = new List<(Site, ApplicationPool, string)>();
List<string> siteUrls = new List<string>();
sites = serverMgr.Sites;
appPools = serverMgr.ApplicationPools;
foreach (Site site in sites)
{
foreach (ApplicationPool appPool in appPools)
{
foreach (Binding binding in site.Bindings)//getting site url
{
string bindingInfo = binding.BindingInformation; // "192.111.1.1:80:google.com" /// *:808:
string[] adrs = bindingInfo.Split(':'); //0 = ip, 1 = port, 2 = hostname
if (adrs[0] == "*")
{
adrs[0] = "localhost";
}
//adding to my list of sites and it's corresponding Application Pool in 1 tuple variable
siteInfo.Add((site, appPool, adrs[0] + ":" + adrs[1] + "/" + adrs[2])); //localhost:80/google.com
}
}
}
return siteInfo;
}
I need something similar to this code: (see comments)
public List<(Site, ApplicationPool, string)> getSiteInfo()
{
List<(Site, ApplicationPool, string)> siteInfo = new List<(Site, ApplicationPool, string)>();
List<string> siteUrls = new List<string>();
sites = serverMgr.Sites;
foreach (Site site in sites)
{
foreach (Binding binding in site.Bindings)
{
//I need something like this to make sure the AppPool I'm getting is of the Site I have.
ApplicationPool appPool = site.ApplicationPoolName;//<-- This Line
string bindingInfo = binding.BindingInformation;
string[] adrs = bindingInfo.Split(':');
if (adrs[0] == "*")
{
adrs[0] = "localhost";
}
//So that I can do this when passing the Site Info tuple Variable with the Site Object together with the corresponding AppPool for later use of the IISReset Class in my project.
siteInfo.Add((site, appPool, adrs[0] + ":" + adrs[1] + "/" + adrs[2]));
}
}
return siteInfo;
}
Sorry for the long and sloppy explanation but I would be happy to clear out if you have questions with this. Thank you.
I figured it out by using Applications attribute of a site. To get the application pool of a Site I did the code below. Where I got the list of applications of a site and used that to identify the app pool from the list of application pools in the server. However this only works if the site only has 1 application, that's why I'm indexing 0(zero) to get the first application (which is the only application of my site) to search for it's corresponding app pool.
ApplicationCollection apps = site.Applications;
ApplicationPool appPool = serverMgr.ApplicationPools[appname[0].ApplicationPoolName];

How can you get a website application name within a website?

I want to be able to check if an application exists within an existing website but I've not found anything. At the moment I have an installer project that gets the input from the user for the name of an existing website. I use this to check the site currently exists in IIS, and if so I then want to search the existing applications to see if a particular 1 exists. Here's what I have:
private void CheckWebsiteApps()
{
SiteCollection sites = null;
try
{
//Check website exists
sites = mgr.Sites;
foreach (Site s in sites)
{
if (!string.IsNullOrEmpty(s.Name))
{
if (string.Compare(s.Name, "mysite", true) == 0)
{
//Check if my app exists in the website
ApplicationCollection apps = s.Applications;
foreach (Microsoft.Web.Administration.Application app in apps)
{
//Want to do this
//if (app.Name == "MyApp")
//{
// Do Something
//}
}
}
}
}
}
catch
{
throw new InstallException("Can't determine if site already exists.");
}
}
Obviously app.Name doesn't exist, so what can I do to get this?
You can use the Microsoft.Web.Administration and Microsoft.Web.Management API for IIS to do this. They are not in the GAC though, you have to reference them from the inetsrv folder. On my machine they are located at...
C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll
C:\Windows\System32\inetsrv\Microsoft.Web.Management.dll
Here is some example code of enumerating them,
class Program
{
static void Main(string[] args)
{
ServerManager serverManager = new ServerManager();
foreach (var site in serverManager.Sites)
{
Console.WriteLine("Site -> " + site.Name);
foreach (var application in site.Applications)
{
Console.WriteLine(" Application-> " + application.Path);
}
}
Console.WriteLine("Press any key...");
Console.ReadKey(true);
}
}
Follow Up:
Applications do not have names, only the root Site has a name in IIS. Applications only have a path (as they are children of sites). If the Path is "/" then the Application is the root application for the site. If the path is anything other than / then it is a 2nd+ child application of the site. So you would need to use Application.Path to do what you want.

How to get Application Pool name through code (C#, ASP.net)

I want to recycle the application pool through my application.
Previously I was storing the application pool name in my database and using that to recycle.
But It happened in the past that we moved apps from one app pool to another and sometimes we forget to update the app pool name in the database.
So I am thinking to get the app pool name through the application and use that for recycling.
In many cases it might be enough to just read the name of the application pool from the environment variable:
var apppool = System.Environment.GetEnvironmentVariable(
"APP_POOL_ID", EnvironmentVariableTarget.Process);
Modified version of #Razon answer :)
public static string GetCurrentApplicationPoolName()
{
ServerManager manager = new ServerManager();
string DefaultSiteName = System.Web.Hosting.HostingEnvironment.ApplicationHost.GetSiteName();
Site defaultSite = manager.Sites[DefaultSiteName];
string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
string appPoolName = string.Empty;
foreach (Application app in defaultSite.Applications)
{
string appPath = app.Path;
if (appPath == appVirtualPath)
{
appPoolName = app.ApplicationPoolName;
}
}
return appPoolName;
}
May this can help: ApplicationPoolName Property
Namespace: Microsoft.Web.Administration
Assembly: Microsoft.Web.Administration (in Microsoft.Web.Administration.dll)
http://msdn.microsoft.com/en-us/library/microsoft.web.administration.application.applicationpoolname(v=vs.90).aspx

When programmatically creating a new IIS web site, how can I add it to an existing application pool?

I have successfully automated the process of creating a new IIS website, however the code I've written doesn't care about application pools, it just gets added to DefaultAppPool. However I'd like to add this newly created site to an existing application pool.
Here is the code I'm using to create the new website.
var w3Svc = new DirectoryEntry(string.Format("IIS://{0}/w3svc", webserver));
var newsite = new object[] { serverComment, new object[] { serverBindings }, homeDirectory };
var websiteId = w3Svc.Invoke("CreateNewSite", newsite);
site.Invoke("Start", null);
site.CommitChanges();
<update>
Although this is not directly related to the question, here are some sample values being used above. This might help someone understand exactly what the code above is doing more easily.
webServer: "localhost"
serverComment: "testing.dev"
serverBindings: ":80:testing.dev"
homeDirectory: "c:\inetpub\wwwroot\testing\"
</update>
If I know the name of the application pool that I'd like this web site to be in, how can I find it and add this site to it?
You have to assign the AppPool on the virtual dir (not the webserver) and set the AppIsolated property to 2 which mean pooled-process ;)
http://msdn.microsoft.com/en-us/library/ms525598%28v=VS.90%29.aspx
Relevant code sample from link:
static void AssignVDirToAppPool(string metabasePath, string appPoolName)
{
// metabasePath is of the form "IIS://<servername>/W3SVC/<siteID>/Root[/<vDir>]"
// for example "IIS://localhost/W3SVC/1/Root/MyVDir"
// appPoolName is of the form "<name>", for example, "MyAppPool"
Console.WriteLine("\nAssigning application {0} to the application pool named {1}:", metabasePath, appPoolName);
try
{
DirectoryEntry vDir = new DirectoryEntry(metabasePath);
string className = vDir.SchemaClassName.ToString();
if (className.EndsWith("VirtualDir"))
{
object[] param = { 0, appPoolName, true };
vDir.Invoke("AppCreate3", param);
vDir.Properties["AppIsolated"][0] = "2";
Console.WriteLine(" Done.");
}
else
Console.WriteLine(" Failed in AssignVDirToAppPool; only virtual directories can be assigned to application pools");
}
catch (Exception ex)
{
Console.WriteLine("Failed in AssignVDirToAppPool with the following exception: \n{0}", ex.Message);
}
}
Note that if you are not explicitly adding a new virtual directory to the application, the metabasePath will simply be "IIS://<servername>/W3SVC/<siteID>/Root"
You need to get the AppPoolfrom IIS://{0}/W3SVC/AppPools, and attach it to the site's AppPoolId. Something like:
var appPool = new DirectoryEntry(
string.Format("IIS://{0}/W3SVC/AppPools/{1}", webServer, appPoolName)
);
site.Properties["AppPoolId"].Value = appPool;
site.Properties["AppPoolId"][0]= "poolname";

Categories