Checking whether a folder exists under an IIS application using C# - c#

I'm working on a web based deployment tool in C# which deploys applications remotely on IIS 7.
I've reached a point where where I'm able to deploy an application. Now I need to see if the application that is deployed has a a certain directory before attempting to set permissions on it (Since the tool would deploy different applications which may or may not have that folder).
There are two approaches that I took:
I've checked for classes that I can use under the ServerManager namespace. I can get a handle on an application deployed under a certain application pool using:
var iis = ServerManager.OpenRemote("serverName")
var iisApplication = iis.Sites[site].Applications["appName"];.
Now I can get the virtual directories under the application using :
var virtualDirectory = iisApplication.VirtualDirectories;
But then I'm not able to see a whole lot of folders which are under that virtual directory. For axample, my application is deployed as test and iisApplication.VirtualDirectories.First() gives me /test. I was want to be able to /test/_ApplicationLogs which is the directory I want to set permissions on.
My next approach was to use DirectoryEntry. Here, I'm not able to figure out the metabase path to use for my application. Is there a standard metabase path used for IIS 7?
For an application called test deployed locally, what would the metabase path be? And would I be able to get all the children so that I can use DirectoryEntry.Exists?

For now, I have a workaround. I can use the WhatIf (set it true) property under DeploymentSyncOptions, do a sync and then check if an object got added. If it did, the directory does not exist. Code :
var syncOptions = new DeploymentSyncOptions();
syncOptions.WhatIf = true;
using (deploymentObject)
{
var result = deploymentObject.SyncTo(
DeploymentWellKnownProvider.SetAcl,
"Default Web Site/path_to_folder",
destinationBaseOptions,
syncOptions);
if (result.ObjectsAdded != 0)
{
syncOptions.WhatIf = false;
deploymentObject.SyncTo(DeploymentWellKnownProvider.SetAcl,
"Default Web Site/path_to_folder",
destinationBaseOptions,
syncOptions);
}
}

Related

Can't see path to another server in ASP.NET MVC

I have an internal ASP.NET MVC site that needs to read an Excel file. The file is on a different server from the one that ASP.NET MVC is running on and in order to prevent access problems I'm trying to copy it to the ASP.NET MVC server.
It works OK on my dev machine but when it is deployed to the server it can't see the path.
This is the chopped down code from the model (C#):
string fPath = HttpContext.Current.Server.MapPath(#"/virtualdir");
string fName = fPath + "test.xlsm";
if (System.IO.File.Exists(fName))
{
// Copy the file and do what's necessary
}
else
{
if (!Directory.Exists(fPath))
throw new Exception($"Directory not found: {fPath} ");
else
throw new Exception($"File not found: {fName } ");
}
The error I'm getting is
Directory not found:
followed by the path.
The path in the error is correct - I've copied and pasted it into explorer and it resolves OK.
I've tried using the full UNC path, a mapped network drive and a virtual directory (as in the code above). Where required these were given network admin rights (to test only!) but still nothing has worked.
The internal website is using pass through authentication but I've used specific credentials with full admin rights for the virtual directory, and the virtual dir in IIS expands OK to the required folder.
I've also tried giving the application pool (which runs in Integrated mode) full network admin rights.
I'm kind of hoping I've just overlooked something simple and this isn't a 'security feature'.
I found this question copy files between servers asp.net mvc but the answer was to use FTP and I don't want to go down that route if I can avoid it.
Any assistance will be much appreciated.
First, To be on the safe side that your directory is building correctly, I would use the Path.Combine.
string fName = Path.Combine(fPath, "test.xlsm")
Second, I would check the following post and try some things there as it seems to be a similar issue.
Directory.Exists not working for a network path
If you are still not able to see the directory, there is a good chance the user does not have access to that network path. Likely what happened is the app pool running your application has access to the directory on the server. The production box likely doesn't have that same access. You would have to get with the network engineer to get that resolved.
Alternatively, you could write a Powershell script to run as a user who has access to both the production and the development server to copy the file over to the production server if that is your ultimate goal and your server administrators could schedule it for you if that is allowed in your environment.

In azure web app File.Exits return false actually file exists

We have a mail template in ~/Content/EmailTemplate/template.cshtml. But whenever we do the following:
var path="D:/site/wwwroot/Content/EmailTemplate/template.cshtml"
File.Exist(path)
File.Exist(path) returns false. While debugging in local source it works fine. It returns false in azure web app only. I have checked the file already exists there.
If you move the above code to another environment it will break.
I would suggest that you use map path as this will assure that you can move from hosting environment to hosting environment, including your local development environment.
string path = Server.MapPath("~/path/tofile");
You will also have higher confidence that you are targeting the file correctly.
For Azure WebApp,
D:\home is shared for us and we could read or write file in this path. More detail about Home directory access please refer to the WebApp Sandbox. File structure on Azure please refer to another document. We could browse it from Kudu (http://yourwebsite.scm.azurewebsites.net/) tool. In your case, we also could use the following code in the Azure WebApp.
string path = Environment.GetEnvironmentVariable("HOME") + #"\site\wwwroot\Content\EmailTemplate\template.cshtml"

Creating a folder on the desktop

I want to create a folder on my current user's desktop folder, however; I keep getting an access denied message. I have full write permissions under my profile in IIS.
string activeDir = #"C:\Users\dmm\Desktop\";
string newPath = System.IO.Path.Combine(activeDir, "mySubDir");
System.IO.Directory.CreateDirectory(newPath);
Any help would be appreciated.
Try using the built in objects to get the desktop path, and let .NET also handle the path building for the new folder. You will also want to check if the directory exists first.
string newFolder = "abcd1234";
string path = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
newFolder
);
if(!System.IO.Directory.Exists(path)) {
try {
System.IO.Directory.CreateDirectory(path);
} catch (IOException ie) {
Console.WriteLine("IO Error: " + ie.Message);
} catch (Exception e) {
Console.WriteLine("General Error: " + e.Message);
}
}
When you deploy an application on IIS by default it is executed with ApplicationPoolIdentity. Which is virtual user created and named as IIS AppPool\YourPoolName If this virtual user does not have write access to your desktop. You get that exception.
You have two options.
Give ApplicationPoolIdentity user write access to Desktop directory.
goto Desktop folder and add user IIS AppPool\YourPoolName with write access :
Change pool Identity to user which has write access to directory.
Go
IIS->Application Pools -> Your AppPool ->Advanced Settings -> Identity
->
Select Custom Account and click set button. and there you enter your windows user credentials.
I would recommend first option.
There are many to consider here, first of them being that your application is an ASP.NET application, and every current user will be different. If your application — just assume — runs correctly on your machine, it will never run on hosting environment because they do not grant write permissions to special folders and user accounts.
That said, you need to work in physical paths in order to create your directories.
var path = "C:\\Users\\afzaa\\Desktop\\";
var folder = Path.Combine(path, "folder");
Directory.CreateDirectory(folder);
The result of the above code is,
As you can see, the code properly works and has no issue at all in execution.
There are few things to note:
Your application has read/write permissions. Check IIS for that.
Your code can actually lookup the path you are trying to access. That applies to any folder inside Desktop too, a sub folder may have special permissions applied.
Do not do this, write the content online in your hosting domain. Users have different accounts and structures for folders and thus this will not work — Desktop path is different.
If you want to users to download the file, simply stream the file down and let them save it where they want to.
https://forums.asp.net/t/1807775.aspx?Create+e+New+Folder+Access+Denied+
https://answers.microsoft.com/en-us/windows/forum/windows_xp-files/unable-to-create-the-folder-new-folder-access-is/ac318218-a7b2-4ee2-b301-2ad91856050b
.NET MVC Access denied when trying to create Directory
If you ran your logic from an IIS application, you should use Server.MapPath:
System.IO.Directory.CreateDirectory(Server.MapPath(newPath));

Get IIS default installation directory

From what I can tell all ASP.NET web applications are placed into C:\inetpub folder by default. But is there any way to retrieve this folder in a C# code? Or do I just hard code it?
PS. I need this as a default folder for my C# program that installs my web application.
The following is limited to my experience so . . . YMMV
I do not believe that there is a true default location.
IIS will host a site in any folder that IIS has access permissions for. To determine what folder that might be, you can query the existing site list.
BUT
All is not lost. You can figure out where those sites are!
In a new IIS installation you can locate the directory of the "Default Site" created during the IIS installation.
Just keep in mind that there are no guarentees that the site will remain there. Personally, I delete the Default Site as soon as I start to configure IIS.
In IIS 7+
Site locations can be found in an XML file under IIS's configuration folder.
%systemroot%\System32\inetsrv\config\
In the XML file applicationHost.config see
The XML Node path is:
/configuration/system.applicationHost/sites/
From there you can iterate through each child node to see the various folder locations. For example, the default site will probably be listed as:
child node: site name="Default Web Site" id="1"
The .Net Way
Another trick would be to query IIS directly via .Net.
Here is the API documentation
Here are some methods to keep in your back pocket:
private static Site GetSite(string siteName)
{
Site site = (from s in (new ServerManager().Sites) where s.Name == siteName select s).FirstOrDefault();
if (site == null)
throw new Exception("The Web Site Name \"" + siteName + "\" could not be found in IIS!");
return site;
}
private static ApplicationCollection GetApplications(Site site)
{
//HttpContext.Current.Trace.Warn("Site ID3: " + site + "/n");
ApplicationCollection appColl = site.Applications;
return appColl;
}
private static String GetHostName(Site site)
{
BindingCollection bindings = site.Bindings;
String bind = null;
foreach (Binding binding in bindings)
if (binding.Host != null)
return binding.ToString();
return bind;
}
Update!
While I can't guarantee that this location will remain after install, you can also query the registry:
Check out HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\InetStp\pathWWWRoot\
I'm posting this as a seperate answer as its a completely different solution:
Do not worry where other apps are installed!
Use Web Deploy (and if you're getting creative with Web Publish, you can even set specific folder permissions)
If you need to do this pragmatically/have no access to those nifty tools...
Choose a location and set permissions on the folder to ensure that IIS is able to access it.
Then using the IIS API's register your new site.
Oh and you'll need to check those app pool permissions too

Attempt to create 'Virtual Directory' creates 'Virtual Application' instead

I have a web application that deploys with two virtual directories under it's IIS virtual application. On IIS 6 boxes, the following code creates these virtual directories as expected, however on IIS 7 boxes, I end up with my virtual application having two other virtual applications under it, rather than one virtual application with two virtual directories under it. I've tried the following two methods, but both still create a virtual application, not a virtual directory. How can this code be changed to deploy the needed virtual directories, not the undesired virtual applications?
one:
private void AddVirtualDir(DirectoryEntry entry)
{
DirectoryEntry virtualDirectory = (DirectoryEntry)entry.Invoke("Create", "IIsWebVirtualDir", "VirtualDirectory");
virtualDirectory.InvokeSet("Path", #"VirtualPath");
virtualDirectory.InvokeSet("AppFriendlyName", "VirtualDirectory");
virtualDirectory.Properties["AccessRead"][0] = true;
virtualDirectory.Properties["AccessScript"][0] = 512;
virtualDirectory.Properties["AppIsolated"].Clear();
virtualDirectory.Properties["AppIsolated"].Add(2);
virtualDirectory.Invoke("AppCreate", false);
virtualDirectory.CommitChanges();
entry.CommitChanges();
}
two:
private void AddVirtualDir(DirectoryEntry entry)
{
var virtualDirectory = entry.Children.Add("VirtualDirectory", "IIsWebVirtualDir");
virtualDirectory.Properties["AccessRead"][0] = true;
virtualDirectory.Properties["AccessScript"][0] = 512;
virtualDirectory.Properties["AppFriendlyName"][0] = "EditorControls";
virtualDirectory.Properties["AppIsolated"][0] = 2;
virtualDirectory.Properties["Path"][0] = Path.Combine(_INSTALLDIR, #"Kryptiq_Root\FormManagement\EditorControls");
virtualDirectory.CommitChanges();
entry.CommitChanges();
}
How this works on IIS6
The problem here is that you're setting the AppIsolated value. In IIS6 this is used to configure how an application should run, and generally you should never need to touch this or add it anywhere.
AppIsolated always defaults to 2 which means pooled process, i.e. the application will run in either the parent application's application pool or in the pool specified by AppPoolId.
The reason that there are other values is so that you can configure an application to run in a couple of legacy IIS5 modes - In Process and Out of Process mode.
So unless you configured your site's /root application to run as anything other than AppIsolated="2" then you don't need to set this value.
Your code can be as simple as:
using (var entry = new DirectoryEntry("IIS://localhost/W3SVC/1/ROOT"))
{
using (DirectoryEntry virtualDirectory = entry.Children.Add("MyVdir",
"IIsWebVirtualDir"))
{
virtualDirectory.Properties["Path"][0] = PATH_TO_MY_STUFF;
virtualDirectory.Properties["AccessRead"][0] = true;
virtualDirectory.Properties["AccessScript"][0] = 512;
virtualDirectory.CommitChanges();
}
}
If you do set AppIsolated in IIS6 it gets ignored because for the directory to become an application you also need to set AppRoot.
IIS7 - IIS6 compatibility shim
In IIS7 when using System.DirectoryServices you're working with an underlying II6 compatibility API which is translating these ADSI calls to calls to the new IIS7 API. It's not perfect and I suspect that when it see's AppIsolated being set it's assuming you want an application, despite you not specifying any other application related metabase values.
IIS7 Managed API is better
You probably know this, but it's better to work with IIS7 configuration via the managed Microsoft.Web.Administration bits. Not all of the ADSI/metabase compatibility settings have equivalents in IIS7 which can force the translation layer to make compromises to work around this. I mention these types of problems in my answers here and here.

Categories