How to get website's physical path on IIS 10? - c#

In my MVC app I want to get website's physical path on IIS server. This old post is about it, but I'm using a newer version of IIS (10.0.19041.1). Is there another way of accomplishing this task?
I was trying the accepted solution from the above mentioned post, but it throws me an exception System.NullReferenceException: 'Object reference not set to an instance of an object.' site was null..
using Microsoft.Web.Administration;
//...
int iisNumber = 2;
using (ServerManager serverManager = new ServerManager())
{
var site = serverManager.Sites.Where(s => s.Id == iisNumber).SingleOrDefault();
var applicationRoot = site.Applications.Where(a => a.Path == "/").SingleOrDefault(); //here I get the exception
var virtualRoot = applicationRoot.VirtualDirectories.Where(v => v.Path == "/").SingleOrDefault();
Console.WriteLine(virtualRoot.PhysicalPath);
System.Diagnostics.Debug.WriteLine("***************************************path to IIS website: " + virtualRoot.PhysicalPath + "***************************************");
}
My website properties:

Related

How to get app port from IIS Express in C#

I am trying to get The port which an app is running on, from IIS Express.
So far i tried those two approaches:
//first
var iisExpress = new DirectoryEntry("IIS://localhost/W3SVC/AppPools").Children;
//second
var mgr = new ServerManager().Sites;
But both give me only the DefaultAppPool with the Default Web App, while what i need is the apps i'm running localy from Visual Studio.
This will output the names and ports of all your websites running on IIS.
var serverManager = new ServerManager();
foreach (Site site in serverManager.Sites)
{
Console.WriteLine(site.Name);
Console.WriteLine(site.Bindings[0].EndPoint.Port);
}
If you want to save the port of a specific site (ex: MyWebsite) on a variable you can do the following:
var serverManager = new ServerManager();
var mySite = serverManager.Sites.FirstOrDefault(s => s.Name.Contains("MyWebsite"));
int myPort = mySite?.Bindings[0].EndPoint.Port ?? 0;

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];

ServerManager.CommitChanges( ) throws Error: "Cannot write configuration file"

Here is my code to create an application
public static bool CreateApplication(String websiteName, String applicationName, String AppDIR,String appPoolName)
{
try
{
var windowsDir = Environment.GetEnvironmentVariable("SystemRoot");
Process.Start(windowsDir+#"\system32\inetsrv\appcmd","unlock config -section:system.webServer/security/authentication/windowsAuthentication");
Process.Start(windowsDir+#"\system32\inetsrv\appcmd","unlock config -section:system.webServer/security/authentication/anonymousAuthentication");
ServerManager iisManager = new ServerManager();
if (!applicationName.Contains("/"))
applicationName = "/" + applicationName;
var app = iisManager.Sites[websiteName].Applications.Add(applicationName, AppDIR);
app.ApplicationPoolName = appPoolName;
var config = app.GetWebConfiguration();
var anonsection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", iisManager.Sites[websiteName].Name + applicationName);
anonsection["enabled"] = false;
var winsection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", iisManager.Sites[websiteName].Name + applicationName);
winsection["enabled"] = true;
iisManager.CommitChanges(); //Blows up here
return true;
}
catch
{
return false;
}
}
Everything is fine until it hit's the commit changes method call.
It throws this error
Error: Cannot write configuration file
All of the code is verified as working except for where I change the enabled values to false and true.
When I added these in it started to explode.
Is there any way to fix this from code, that can be distributed to other machines?
Update:
After re-locking the files
like this
Process.Start(windowsDir + #"\system32\inetsrv\appcmd", "lock config -section:system.webServer/security/authentication/windowsAuthentication");
Process.Start(windowsDir + #"\system32\inetsrv\appcmd", "lock config -section:system.webServer/security/authentication/anonymousAuthentication");
I now get this error
Error: Cannot commit configuration changes because the file has
changed on disk
I also ran into the second of your two issues (Error: Cannot commit configuration changes because the file has changed on disk) for our c# application which we use to configure IIS servers as part of our deploy operation.
For me in the end it was unintentionally nesting two using statements wrapped around the ServerManager object that caused the problem.
For your particular situation I would try to add a using statement for your reference to ServerManager and ensure that the command line appcmd calls are outside the using statement or moved within the using statement but translated into API calls via the ServerManager object.

Microsoft.Web.Administration null reference when trying to add application to site

i'm trying to add an application to site via code (C#) and i get a null reference.
someone have an idea why?
here's the code i use:
Application app;
using (var sm = new ServerManager())
{
if (site.Applications["/" + appName] == null)
{
site.Applications.Add("/" + appName, physicalPath);
sm.CommitChanges();
}
app = site.Applications["/" + appName];
}
return app;
the code fails on the "add" line.
the appName is a string like "MyNewApp".
the physicalPath is a string like "C:\inetpub\wwwroot\MyService".
the site is Microsoft.Web.Administration.Site object of the IIS6.1 (win 7 ent) default web site.
found the answer: NullReferenceException in Microsoft.Web.Administration when adding https binding
its all because of the "using" statement.
the site didn't have a parent ServerManager at the point of the application add.

Assign application pool to website in iis7 programmatically

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.

Categories