Translating URL to physical path in admin script

I'm trying to write some admin scripts which will run on an intranet server (Windows 2000 with IIS 5.0). These scripts will not be run in ASP, but rather will be straight VBScript scripts (or javascript, or whatever - I'm pretty adaptable language-wise). I need to translate the URLs of some pages to the corresponding physical file paths. Does anybody have any idea how to go about this?
Who is Participating?
basically it would tie in with meverest's suggestion.. run a scheduled job to pull the results of disptree.vbs or similar into a table.
Reference the data contained in this table with the stored procedure you're writing to generate URLs.
If your Site descriptions match dns hostnames (or even IP's), this will have been pulled into the table as[firstObj.ServerComment[1-->x]] and should make it simple for you to differentiate sites by name.  Coding in something like dns lookup results would add to the useful data in this table.
I think I've said before I'm no sql expert so sorry for any fundamental errors in suggesting how to put your table together, but you should be able to use the general idea to put things into a useable format.  

As for IIS - think it's all coded in the dll's. is a very very basic description. I'd say the dll's contain code to match hostname:port combination to a particular node in the metabase which leads to a object ID, security config and default page description... not being open source i don't really know.
ahh... 4guysfromrolla save the day again (I hope) :)
see this link for code.
travisjhallAuthor Commented:
Not quite. Remember, this is for writing an admin script which will not be run in ASP. Thus, there is no Request object, nor a Server object, readily available - unless someone knows how to create such an object in regular VBScript. The code that you link to relies on the use of those ASP objects, so is no good.

I could, I suppose, write an ASP page to return the file paths of other pages, and have my script send a request to the local server and see what comes back. However, I'm hoping to get something that jumps through a few less hoops than that. Besides, if I want to go that way, there's better code for it than that example. The authors have set about building paths themselves when they could simply use Server.MapPath instead.

Tell you what, guys, for every wrong answer I get, I'll increase the value of the question by 50 points, until I hit 500 points. That seems fair, since a question that attracts wrong answers is obviously difficult.
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

ok, you can traverse the metabase structure with vbscript (see the admin script samples for methods) looking for the url paths, and get the physical locations that way.  but not only would that be time consuming and introduce a fair bit of processing overhead, it's a clumsy way to do it.

I'd reckon you'd be better off storing all the necessary information in some kind of database somewhere and then just refer to the database with (more efficial) sql queries to get the info you are after.

Is this for some kind of cms or something?

cheers,  Mike.
travisjhallAuthor Commented:
Actually, this script is probably going to be used to update a database, to keep it in sync with the files. (I'm not certain of the architecture yet. There's a couple of ways to jump, and which way I go will depend at least in part on what is possible.)

Overhead isn't really an issue, because being an admin script it will be run relatively rarely. It's not like the script will get run every time someone accesses the URL.

How would I go about traversing the metabase structure? I don't seem to be able to get any IIsWebDirectory or IIsWebFile objects, whether directly:
Set obj = GetObject("IIS://LocalHost/W3SVC/1/ROOT/warning.gif")

or indirectly:
Set obj1 = GetObject("IIS://LocalHost/W3SVC/1/ROOT")
Set obj2 = obj1.GetObject("IIsWebFile", "warning.gif")

In either case, I get error 80070003, "The system cannot find the path specified."

ok.... if you drop to the command prompt on the server and go into the inetpub\adminscripts directory,
to troubleshoot your 80070003 try running
>   adsutil enum /W3SVC/1
and see if it comes back as the correct site (or with anything at all).
You're right about the script i pointed you to being less than optimal- i'm not sure why they went about it the way they did - at first I'd thought it was to pull back true paths of virtual directories, but it doesn't do that either... they had another page there with the scripts to use server.mappath i just didn't spot it yesterday.

I know there's a vbscript in iis 6 called iiscnfg.vbs that exports metabase to an xml file - it has paths in that but I can't find an easy equivalent way to do it in IIS 5.  
This is a link to the adsi objects available for IIS unfortunately only a couple apply to iis 5.1 and earlier (just to make life simple for you).
Another way round may be to look at some of the site export tools that are available for migrating sites from server to server - you might be able to check the code on those to "borrow" ways of stripping info you require from the metabase.

You may have some luck if you post a minimal point question in the scripting forum linking back to this one, some of the scripting experts might have done some of this before.
travisjhallAuthor Commented:
I'll have to scout around and see if I can track down a copy of iiscnfg.vbs. Maybe it will have some useful info in it, even if it doesn't work under IIS5.

I've had some success with the following script fragment:

Function URLToPhysicalPath(sURL)
Dim url
Set url = New CURL
url.URL = sURL

Dim sFolders
sFolders = Split(url.Path, "/")

Dim obj
Dim sFolder
Dim sPath
sPath = "IIS://" & url.Host & "/W3SVC/1/ROOT/" & url.Path
Dim iFolder
On Error Resume Next
Set obj = GetObject(sPath)
If IsEmpty(obj) Then
      For iFolder = UBound(sFolders) To LBound(sFolders) Step -1
            sPath = Left(sPath, Len(sPath) - Len(sFolders(iFolder)) - 1)
            Set obj = GetObject(sPath)
            If Not IsEmpty(obj) Then
                  If obj.Class = "IIsWebVirtualDir" Then
                        If GetObject(obj.Parent).Get("Path") <> obj.Get("Path") Then Exit For
                  End If
            End If
      iFolder = UBound(sFolders) + 1
End If
On Error Goto 0

If iFolder < LBound(sFolders) Then Exit Function
Dim sPhysicalPath
sPhysicalPath = obj.Get("Path")
Dim iPhysicalFolder
For iPhysicalFolder = iFolder To UBound(sFolders)
      sPhysicalPath = sPhysicalPath & "\" & sFolders(iPhysicalFolder)
sPhysicalPath = sPhysicalPath & "\" & url.File

URLToPhysicalPath = sPhysicalPath
End Function

Class CURL
Dim mProtocol, mHost, mPath, mFile

Property Let URL(NewURL)
Dim re
Dim matches
Set re = New RegExp
re.Pattern = "^.*:"
Set matches = re.Execute(NewURL)
If matches.Count > 0 Then mProtocol = Left(matches(0), Len(matches(0)) - 1)
re.Pattern = "^" & mProtocol & ":\/\/[^\/]*"
Set matches = re.Execute(NewURL)
If matches.Count > 0 Then mHost = Mid(matches(0), Len(mProtocol) + 4)
re.Pattern = "[^\/:?#]*$"
Set matches = re.Execute(NewURL)
If matches.Count > 0 Then mFile = matches(0)
mPath = Mid(NewURL, Len(mProtocol) + Len(mHost) + 5, Len(NewURL) - Len(mProtocol) - Len(mHost) - Len(mFile) - 5)
End Property

Property Get Protocol
Protocol = mProtocol
End Property

Property Get Host
Host = mHost
End Property

Property Get Path
Path = mPath
End Property

Property Get File
File = mFile
End Property
End Class

Any comments on that? I've got a feeling it isn't quite complete. I think I should be checking my IIsWebServer object somehow to make sure I'm looking at the right server.

(Nothing actually wrong in what's been said, but I think I've complicated the matter further, so another 50 points.)
The only thing I can think of is where you have the /1/ in "/W3SVC/1/ROOT/" - that might cause you some problems.  If you can query the metabase to check the site number is correct first and include it as a variable instead it would be a little more adaptable (only really an issue if you've got multiple sites otherwise figure out what number your site is - default web is usually 1 - and hardcoding it shouldn't be a problem).

the iiscnfg tool used by iis 6 may not be of much use.  The metabase structure changed to xml between iis 5 and 6.  It's now easier to export and you can do a fair bit using wmi which is what I think they've used in that particular script.  It sits in the system32 directory on 2003 iis servers.
forgot to mention - you're also going to have a problem to sort out with the script if you're using virtual directories - your file paths won't track correctly.
If you look in c:\inetpub\adminscripts (or wherever you have iis installed), running
dispnode -a IIS://LocalHost/w3svc
should spit out your web numbers for you - you might be able to pull bits of that script into yours.
travisjhallAuthor Commented:
Actually, my script works fine with virtual directories. That's why I walk up the tree from the deepest level, rather than the other way around. The first IIsWebVirtualDir I find is the deepest one in the tree, so that's the one I need to build my physical path from. I've tested that.

As for the site number, yes, that's one that worries me. I'd rather do the job right and have it check that. That'll be a bit tricky, because there are multiple ways to address a host, but if I check the server bindings I should be able to match IP addresses to the correct site. If the host part of the URL isn't an IP address, I can check if it matches the name of the server, and if so figure out which site is the default. And after that, I guess I have to do some DNS resolution to get the IP address - but I think that warrants and entirely new question.

I think I'm about ready to close the question, then, but I seem to have done most of the work myself. Would you guys be happy with a B grade, or do you want an opportunity to upgrade that to an A?

(And then there's the really tricky bit - this might work best for me written as an extended stored procedure for SQL Server...)
default site's not necessarily a good idea, many people leave it there in a "stopped" state and create a second site that matches the name of the server... of course, not an issue if the servers are yours and you have control over config.

As for the A or B it's up to you but you did post in an IIS forum not a programming forum so you can't really blame us for not providing your code for you :)  (and this definitely isn't the right spot to ask for SQL stored procedures - sorry, but I'm no help with that one).
travisjhallAuthor Commented:
I wasn't expecting anyone to write code for me, or not much anyway (an example of correct use of GetObject if I was doing it wrong might have been required), but the closest either of you came to an actual solution was the idea of traversing the metabase, and that isn't really what I've had to do to get my script working. It's the other way around, walking up the url to find something that is in the metabase, and that was my idea.

However, I think I'm a reasonable man, so fill in another little piece of the puzzle and I'll make it an A... What property (or multiple properties) of the sites (IIsWebService objects) would you suggest I example to determine which site is the correct one for a given URL? Other than the server bindings in the case of a URL specifying a host IP, that is. And if there aren't any such properties which help, saying so is a valid answer.

And no, I certainly don't expect you guys to help me write extended stored procedures. That's tricky enough for the SQL Server experts. :)
revisited my earlier comments and I gave you the wrong script before though (it should be inetpub\adminscripts\disptree.vbs).  Command you want is:
disptree -a IIS://LocalHost/w3svc --NoRecurse
this gives you a listing of all your site numbers and their descriptive names.

alternately you can put the dns hostname of your site in where "LocalHost" is above to give you w3svc listings from remote sites/other sites on the server.  The problem is that if you throw any hostname or ip that is on a server at the script, it brings back all sites on that physical server.  While this is obviously not what you want, I think you could use this in combination with what meverest suggested, pull the result into a db periodically and make it useful by altering your site descriptions in IIS to match your host records for each site.  
The script basically spits out results in the format:
[yourObj.Name] (site number) - [yourObj.ServerComment] (Site Description visible in the mmc) [(yourObj.Class)] (type of site - IIsWebServer, etc).
travisjhallAuthor Commented:
So, you're saying that the best approach is probably to make sure that the Site Description is something useful to me, and then check that?

(Oh, and I worked out that disptree.vbs was a handy one to look at. Thanks for that.)

(Hmmm... So what does IIS itself do to determine which site a resource should be pulled from?)
travisjhallAuthor Commented:
I've been wanting to try some of this out before accepting the answer, but work pressures haven't permitted. I'll get back to this at some stage, but it might be a week or two, and I wanted to close the question and give the point before the end of the month at the absolute latest. Only fair to you, alimu. But I may have a few additional comments to add in a few weeks time, which will at least mean that we have a nice robust answer in the PAQ database.
if you want any more braindumping on this question just ask but I can't really promise any more than that.  Good luck and hope it all works!
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.