Creating Virtuals and reading Installed Sites on IIS with .Net
I was futzing around today with installation of one of my Web applications. Automatic installation of apps as to where they can be fully self-installed has always been a thing that I like to do, but it’s difficult to get this right with the ‘lower end’ installer tools and it’s a major PITA (not to mention an expensive undertaking) when dealing with the high end installer tools that do support that sort of thing well.
My typical install scenarios are these:
- Allow selection of a Web Site to install to
- Create a Virtual Directory
- Possibly set up alternate script maps
- If running IIS set up an Application Pool
I haven’t gotten around to the latter two items, but I did start building a small wrapper class that retrieves a list of Web Sites installed locally (or on a remote machine) and allows creation of a virtual directory with the most common settings.
The following VirtualDirectory class is a pretty basic implementation that does the core functions that I typically need when creating a virtual directory. If you look at the public field list you can get a pretty good idea what options I implemented. Obviously not everything is covered here, but it sure is nicer to call the CreateVirtual() method than jerking around with the DirectoryEntry class. The following C# class handles these base tasks for me:
public class VirtualDirectory
{
public WebServerType ServerType = WebServerType.IIS6;
public string Virtual = "";
public string FriendlyName = "";
public string Path = "";
public string IISPath = "IIS://LOCALHOST/W3SVC/1/ROOT";
public string ApplicationPool = ""; // n/a
public bool AuthNTLM = true;
public bool AuthAnonymous = true;
public bool AuthBasic = true;
public string DefaultDocuments = "default.htm,default.aspx,default.asp";
///
/// Used for GetWebSites to retrieve sites for a given domain or IP
///
public string DomainName = "localhost";
///
/// Contains Virtual directory entry (as a DirectoryEntry object) after
/// the virtual was created.
///
public DirectoryEntry VDir = null;
///
/// Contains error message on a failure result.
///
public string ErrorMessage = "";
///
/// Returns a list of Web Sites on the local machine
///
///
public WebSiteEntry[] GetWebSites()
{
string Path = "IIS://" + this.DomainName + "/W3SVC";
DirectoryEntry root = null;
try
{
root = new DirectoryEntry(Path);
}
catch
{
this.SetError("Couldn't access root node");
return null;
}
if (root == null)
{
this.SetError("Couldn't access root node");
return null;
}
ArrayList al = new ArrayList(20);
foreach (DirectoryEntry Entry in root.Children)
{
PropertyCollection Properties = Entry.Properties;
try
{
WebSiteEntry Site = new WebSiteEntry();
Site.SiteName = (string) Properties["ServerComment"].Value;
Site.IISPath = Entry.Path;
al.Add(Site);
}
catch { ; }
}
root.Close();
return (WebSiteEntry[]) al.ToArray(typeof(WebSiteEntry));
}
///
/// Creates a Virtual Directory on the Web Server and sets a few
/// common properties based on teh property settings of this object.
///
///
public bool CreateVirtual()
{
this.SetError(null);
DirectoryEntry root= new DirectoryEntry(this.IISPath);
if (root == null)
{
this.SetError("Couldn't access root node");
return false;
}
try
{
this.VDir = root.Children.Add(Virtual,"IISWebVirtualDir");
}
catch
{
try { this.VDir= new DirectoryEntry(this.IISPath + "/" + Virtual); }
catch {;}
}
if (this.VDir == null)
{
this.SetError("Couldn't create virtual.");
return false;
}
root.CommitChanges();
VDir.CommitChanges();
return this.SaveVirtualDirectory();
}
public bool SaveVirtualDirectory()
{
PropertyCollection Properties = VDir.Properties;
try
{
Properties["Path"].Value = Path;
}
catch (Exception ex)
{
this.SetError("Invalid Path provided " + ex.Message);
return false;
}
this.VDir.Invoke("AppCreate",true);
if (this.FriendlyName == "")
VDir.Properties["AppFriendlyName"].Value = Virtual;
else
VDir.Properties["AppFriendlyName"].Value = this.FriendlyName;
if (this.DefaultDocuments != "")
VDir.Properties["DefaultDoc"].Value = this.DefaultDocuments;
int Flags = 0;
if (this.AuthAnonymous)
Flags = 1;
if (this.AuthBasic)
Flags = Flags + 2;
if (this.AuthNTLM)
Flags = Flags + 4;
Properties["AuthFlags"].Value = Flags; // NTLM AuthBasic Anonymous
VDir.CommitChanges();
return true;
}
protected void SetError(string ErrorMessage)
{
if (ErrorMessage == null)
this.ErrorMessage = "";
this.ErrorMessage = ErrorMessage;
}
}
public enum WebServerType
{
IIS4, IIS5, IIS6
}
There is also a WebSiteEntry class that’s used to capture the list of Web Sites when calling the GetWebSites() method which returns an array of these objects.
public class WebSiteEntry
{
public string SiteName = "";
public string Comment = "";
public string IISPath = "";
}
Working with DirectoryEntry and especially VDir.Properties collection is not a lot of fun. I had a hell of a time trying to get this collection to work correctly. Originally I was referencing the collection directly off the VDir class. For some unknown reason this did not work until I moved the collection off onto a separate variable. This did not work:
string Path = (string) VDir.Properties["Path"];
But this did:
PropertyCollection Properties = VDir.Properties;
string Path = (string) Properties["Path"];
Go figure. By the way, if you need to find documentation in MSDN on what properties are available do a search on IIsWebVirtualDir, which will give you a list of all the settings available. I’m glad to see Microsoft moved the ADSI settings back into MSDN more visibly in recent versions of MSDN – about a year ago, the new WMI settings were the only ones that I could find.
To utilize the class, here is an example of how I use it in my West Wind Web Store configuration application. This app runs as desktop app as a Post install executable and handles a bunch of configuration tasks for the application, such as creating the SQL database, configuring the Web Site. This is then followed by further configuration once the Web Site is configured via a ASP. Net based application configuration.
The following two snippets deal with displaying a list of Web Sites to the user and then allowing the user to select a Web Site, virtual path name with a physical path pointing at the current installation directory.
The following loads a list of sites into a dropdown box:
private void LoadSites()
{
VirtualDirectory vd = new VirtualDirectory();
Sites = vd.GetWebSites();
if (Sites == null)
{
MessageBox.Show("Couldn't read IIS Configuration\r\n" +
"Make sure IIS is installed on this machine.",
"Web Monitor",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
}
for (int x=0; x < Sites.Length; x++)
{
this.lstSites.Items.Add(Sites[x].SiteName);
}
if (Sites.Length > 0)
this.lstSites.SelectedIndex = 0;
}
To actually create a virtual directory I then pick the user’s selection and go with their choice:
private void btnCreateVDir_Click(object sender, System.EventArgs e)
{
int lnIndex = this.lstSites.SelectedIndex;
if (lnIndex < 0)
return;
VirtualDirectory Virtual = new VirtualDirectory();
Virtual.Virtual = this.txtVirtual.Text;
Virtual.Path = this.txtPath.Text;
Virtual.IISPath = this.Sites[lnIndex].IISPath + "/ROOT";
if ( Virtual.CreateVirtual() )
{
MessageBox.Show("Virtual Directory " + Virtual.Virtual + " created.")
this.VirtualCreated = true;
}
else
MessageBox.Show("Virtual Directory " + Virtual.Virtual +
" failed to create.\r\n\r\n" +
Virtual.ErrorMessage + "\r\n\r\n")
}
I’ve used this sort of thing in several of my Web apps thus far and it’s worked out great – I have had no support calls on this stuff where with default installs going to the default Web Site I’ve often had major issues either because the server wasn’t properly dealing with localhost (especially in multi-homed servers).
I hope somebody finds the above useful, because it sure saves me a lot of time now when building new Web installations.
Other Posts you might also like
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Resolving Paths To Server Relative Paths in .NET Code
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Getting the ASP.NET Core Server Hosting Urls at Startup and in Requests
- Back to Basics: Rewriting a URL in ASP.NET Core
The Voices of Reason
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I think what you need to do is run this Admin task out of a separate directory and set your web.config file to use Impersonation for your Authentication. Then log in as an Admin user which shoudl then pass through the Admin rights to your ASP.Net application.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Thank you a lot for posting this solution!
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I use this from a wep app (if someone need a snippet)..
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
is this work with IIs5 and Server 2000 (not 2003). I tried it on my machine and I get always accept denied response.
Thanks
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
For using system.DirectoryServices Microsoft writes:
This example requires Windows Server 2003 Service Pack 1 (SP1). Previous to Windows Server 2003 SP1, the IIS ADSI provider could not be used from System.DirectoryServices to delete metabase nodes.
see : msdn.microsoft.com/library/default.asp?url=/library/en-us/iissdk/iis/deleting_metabase_nodes_using_system_directoryservices.asp
is it right?
Thanks
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I'm pretty sure this works with all versions of IIS - I certainly haven't heard anything from anybody else and I have this in a couple of product installations (of course sometimes people are difficult in reporting bugs :-})
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
On another note:Windows Server 2003 Service Pack 1 (SP1), does not exist. There have been no service packs for 2003.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I am using WIN 2000 ,asp.net
but I keep getting Access Denied , How do I change permission and what setting need changing in WEB.CONFIG file foe impersonation
Thanks
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I wrote about this in another context a while back:
http://west-wind.com/weblog/posts/295.aspx
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
string Path = (string) VDir.Properties["Path"][0];
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
You can use ACL setting routines to do this programmatically, although I think you should be using some other scheme to configure this - most likely with a public and protected directory structure.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Haven't tried it yet, cause i got a question.
Do I have to have Active Directory installed on the server to use it?
Thanks!
Gurr
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
this is ver simialy to something i did recently to help deploy an app across multiple servers. I get an "Access is denied" error trying to create the virtual directoy on a server other than the one the code is running on. It is a webservice trying to create the virtual directory and in the web config file i have added the impersonate tag with administrative rights. Any ideas?
Cheers,
Steve
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
One question. What do you need to set in order for it to be a virtual directory and not an application on IIS?
Thanks,
John
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Just don't call AppCreate(). You actually create a virtual only and explicitly have to set it to make it an application.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Anyone knows how can i add a property to a directoryEntry??
# Creating virtual directory -Please Read this(This is not code) But please Read it and Help Us
I read all Feed backs and comments. What I felt from the above Feed back is that most people are not equiped with full knowledge of .NET - including me.
So What I suggest to Dear friends who are posting Good Code sapmle is that Please write it in friendly way. Why we are searching for codes in Internet is for speeding up our Developments and not for complaining.
Let us take the example - The Code given above
Most people are getting - Access denied + all other problems.
and What was our aim :
To make a virtual Directory.
Where:
Locally and remotely
Important : All most all codes work locally and will not work remotely.
Then Make sure that the code will work locally and remotely.
Or if you want any other changes include all changes and give us line by line comments.
If possible Error and error description and how to remove errors.
If you people, who are writing and publishing codes can look into above matters All these feed backs comments , re-searching etc can be avoided not only that it saves time.
Always remember one thing, If a beginner can understand your code , then any body can understand it with out any problem.
Thanking you
senprogrammes@yahoo.com
Sen K. Mathew
# re: Creating Virtuals(Here is another help)
Wooooo I found One more thing
I am writing this again to you all to help you.
My biggest headache was that I could not create a virtual directory in other machines(eg. staging server or production server etc).
I was searching all these time and wondering why it is not creating a directory on a staging or production server.
whooooo, I found out.
do you notice the code(I am giving you the code from the begining) :
public class VirtualDirectory
{
public WebServerType ServerType =
WebServerType.IIS6;
public string Virtual = "";
public string FriendlyName = "";
public string Path = "";
'***********************************
public string IISPath
= "IIS://LOCALHOST/W3SVC/1/ROOT";
'*************************************
"IIS://LOCALHOST/W3SVC/1/ROOT" here is our culprit.
Does anyone knew or know
What is this "IIS://LOCALHOST/W3SVC/1/ROOT" means
Please look at the number 1
that is /1/Root .
This number (here it is 1(one) is the Site Id number.
That is suppose if your IIS has a site other than Local Web-site, Then this number would be a different one. Please remember if your machine is a dedicated server you will not see this id, because it has only one site Default web site. But in a shared host many sites would be running and you would be able to see the Site Id for each Site. If you mention an ID which is already there . Your programme will never create a Virtual directory, instead it will give you confusing error messages like Access denied etc etc etc. So If you have 5 Sites in IIS Make sure that your line of code has 6 in it, means new site's id's number
that is
"IIS://LOCALHOST/W3SVC/6/ROOT".
'************************************
I hope this would help you
Thanking you
Sen K. Mathew
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
You wrote :"This did not work:
string Path = (string) VDir.Properties["Path"];"
AD/IIS Property can be multi-valued, so it is represented as PropertyValueCollection.
So the following should work:
string Path = (string) VDir.Properties["Path"][0];"
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I am fairly new in the world of .Net and I am currently working on an app that retrieves the virtualdirectories on a remote system, running windows 2003 server.
I have tried the above examples but it keeps giving me "access denied". If I run the app locally on the server then it nicely returns a list.
It doesn't matter if I perform a call like
New DirectoryEntry("IIS:\\" & name & "\W3SVC") or
New DirectoryEntry("IIS:\\" & name & "\W3SVC", login, password)
it keeps returning "access denied".
What can be wrong????. Do I need to perform some settings on the server or on the client.
When looking in IIS on the server, Web Sites properties, Directory Security then anonymous access authentication is enabled.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
i don't if this is the right place to share with you this..but i was a delphi programmer..(well i'm still) but right now i don't know if Delphi is a good option for MS framework...and it seems that C# is the best for that..what do you think?
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
hiyaolee@hotmail.com
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I've noticed that your CreateVirtual function hides some exceptions catch {;}.
Do you have any particular reason for this?
I am asking, because I am using your class in my install application and for one client it returns back without error, but doesn't create the virtual directory.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I'm not sure why those are there - presumably sites that were created and discarded and didn't clean up right or something.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I will add System.Diagnostics.Trace.WriteLine(exc.ToString()) to investigate the problem.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Regrds,
Diwakaran.N.S
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
i want to create new site on IIS during installation of Webservice with .NET . and to
deploy webservice on that Site.
help me......
# How.....?
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
IIS://LOCALHOST/W3SVC/1/ROOT/mydir
You can walk this syntax all the way down the hierarchy.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
How do i solve this?
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Someone asked above - "How can I programatically select the ASP.NET Version (ASP.NET Tab in the properties of the virtual directory)? "
Well, my question is the same, does enyone know how to solve this?
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
Also keep in mind that IIS 7 doesn't tie the .NET version to a virtual but rather to an Application Pool.
Here's the code from the web configuration tool that does this for IIS 5/6:
public bool SetDotNetVersion() { // *** Only supported on IIS 5/6 - on IIS 7 the Application Pool // *** determines the .NET version used if (ServerType != WebServerTypes.IIS6 && ServerType != WebServerTypes.IIS5) return false; try { string ADSIPath = this.IISPath.Substring(this.IISPath.IndexOf("W3SVC")); string ExeFile = System.IO.Path.GetFullPath( Environment.SystemDirectory + "\\..\\Microsoft.NET\\Framework\\v" + this.DotNetVersion) + "\\aspnet_regiis.exe"; string CommandLine = "-s " + ADSIPath + "/" + this.Virtual; Process p = new Process(); p.StartInfo.FileName = ExeFile; p.StartInfo.Arguments = CommandLine; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.UseShellExecute = false; p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; p.StartInfo.CreateNoWindow = true; p.Start(); string LastCompilerResult = p.StandardOutput.ReadToEnd(); p.Dispose(); } catch(Exception ex) { this.ErrorMessage = ex.Message; return false; } return true; }
where this.DotNetVersion needs to be the fully qualified version number (ie. the same as the path).
I doubt that this matters much these days though because you'll want to set to 2.0 most likely as 3.0 and 3.5 all use 2.0 as the base runtime anyway. That only leaves 1.0 and 1.1 as alternate options which should be starting to get rare these days.
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
but get errors with ServerType, WebServerTypes, Me.IISPath
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I've created an IIS Web Site from C# and now I want to make one of its folders (not Virtual Directories, just physical folders i've created within the web site's path) unreadable, doing it from C#.
Do you have any clue of how to do this?
Thanks,
Brian
# re: Creating Virtuals and reading Installed Sites on IIS with .Net
I have created a similar class to setup websites & virtual directories on remote IIS servers.
I'm running it from a web application and keep running into problems when trying to make any Invoke calls.. I can set properties no problems.. but whenever I try to use Invoke to call "AppCreate" for example, i get a System.UnauthorizedAccessException: Access is denied error saying something about the ASP.NET anonymous use required permissions etc...
This is strange since I'm passing the DirectoryEntry a username & password with admin priviledges, and I can set properties etc.. but can't use Invoke?
Don't know if you have run into this problem before or have any insights on overcoming it..
Thanks..