Microsoft Bob

Just a short, simple blog for Bob to share some tips and tricks.

Be sure to check out my non-technical blog at www.bobsbasement.net.

Month List

How to add <clear/> or <remove/> Elements through Scripting

I had a question recently where someone was trying to add <clear /> or <remove /> elements to a collection in their IIS 7 configuration settings. With that in mind, for today's blog I thought that I would discuss a couple of ways to add <clear /> and <remove /> elements by using two specific scripting methods: AppCmd and VBScript.

It should be noted that you can also use JavaScript or PowerShell, but I'm not covering those because the syntax for those is available elsewhere. (JavaScript syntax is available in the Configuration Editor in IIS Manager, and the PowerShell syntax is available through the Web Server (IIS) Administration Cmdlet Reference.) You can also use Managed-Code, and the syntax for that is also available in the Configuration Editor in IIS Manager; but compiled code isn't scripting, is it? :-)

Here's the scenario, IIS makes it possible to modify the contents of an inherited collection in two ways:

  • You can clear the contents of an inherited configuration section, as illustrated by the following configuration excerpt:
    <configuration>
       <system.webServer>
          <defaultDocument enabled="true">
             <files>
                <clear />
             </files>
          </defaultDocument>
       </system.webServer>
    </configuration>
  • You can remove an item from an inherited collection, as illustrated by the following configuration excerpt:
    <configuration>
       <system.webServer>
          <defaultDocument enabled="true">
             <files>
                <remove value="index.html" />
             </files>
          </defaultDocument>
       </system.webServer>
    </configuration>

With that in mind, let's look at scripting those settings.

Using AppCmd

AppCmd.exe is a great utility that ships with IIS 7, which allows editing the configuration settings for IIS from a command line. This also allows you to create batch scripts that automate large numbers of configuration changes. For example, the following batch file enables ASP session state, sets the maximum number of ASP sessions to 1000, and then sets the session time-out to 10 minutes for the Default Web Site:

appcmd.exe set config "Default Web Site" -section:system.webServer/asp /session.allowSessionState:"True" /commit:apphost

appcmd.exe set config "Default Web Site" -section:system.webServer/asp /session.max:"1000" /commit:apphost

appcmd.exe set config "Default Web Site" -section:system.webServer/asp

I'm a big fan of IIS 7's AppCmd.exe, but unfortunately it has two rather large limitations:

  • AppCmd.exe does not directly support clearing the contents of a configuration section. (But there's a workaround that I list below.)
  • AppCmd.exe does not support removing an item from a collection.

These limitations have caused me some grief from time to time, because I often want to script the modification of collections, and I would love to remove items or clear a collection.

How to add a <clear /> element using AppCmd:

Although it's kind of a hack, there is a way to force AppCmd.exe to add a <clear /> element.

Here's what you need to do in order to clear the list of default documents for the Default Web Site:

  1. Create an XML file like the following and save it as "CLEAR.XML":
    <?xml version="1.0" encoding="UTF-8"?>
    <appcmd>
        <CONFIG CONFIG.SECTION="system.webServer/defaultDocument" path="MACHINE/WEBROOT/APPHOST" overrideMode="Allow" locked="false">
            <system.webServer-defaultDocument  enabled="true">
                <files>
                    <clear />
                </files>
            </system.webServer-defaultDocument>
        </CONFIG>
    </appcmd>
  2. Run the following command:
    appcmd.exe set config /in "Default Web Site" < CLEAR.xml

Unfortunately this technique does not work for <remove /> elements. :-( But that being said, you can add a <remove /> element through VBScript; for more information, see the Using VBScript section.

Using VBScript

Fortunately, VBScript doesn't have AppCmd.exe's limitations, so you can add both <clear /> and <remove /> elements.

How to add a <clear /> element in VBScript:

The following steps will clear the list of default documents for the Default Web Site:

  1. Save the following VBScript code as "clear.vbs":
    Set adminManager = WScript.CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
    adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST/Default Web Site"
    Set defaultDocumentSection = adminManager.GetAdminSection("system.webServer/defaultDocument", _
      "MACHINE/WEBROOT/APPHOST/Default Web Site")
    Set filesCollection = defaultDocumentSection.ChildElements.Item("files").Collection
    filesCollection.Clear
    adminManager.CommitChanges
  2. Run the VBscript code by double-clicking the "clear.vbs" file.

How to add a <remove /> element in VBScript:

The following steps will remove a single item from the list of default documents for the Default Web Site:

  1. Save the following VBScript code as "remove.vbs":
    Set adminManager = WScript.CreateObject("Microsoft.ApplicationHost.WritableAdminManager")
    adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST/Default Web Site"
    Set defaultDocumentSection = adminManager.GetAdminSection("system.webServer/defaultDocument", _
      "MACHINE/WEBROOT/APPHOST/Default Web Site")
    Set filesCollection = defaultDocumentSection.ChildElements.Item("files").Collection
    addElementPos = FindElement(filesCollection, "add", Array("value", "index.html"))
    If (addElementPos = -1) Then
       WScript.Echo "Element not found!"
       WScript.Quit
    End If
    filesCollection.DeleteElement(addElementPos)
    adminManager.CommitChanges
    
    Function FindElement(collection, elementTagName, valuesToMatch)
       For i = 0 To CInt(collection.Count) - 1
          Set element = collection.Item(i)
          If element.Name = elementTagName Then
             matches = True
             For iVal = 0 To UBound(valuesToMatch) Step 2
                Set property = element.GetPropertyByName(valuesToMatch(iVal))
                value = property.Value
                If Not IsNull(value) Then
                   value = CStr(value)
                End If
                If Not value = CStr(valuesToMatch(iVal + 1)) Then
                   matches = False
                   Exit For
                End If
             Next
             If matches Then
                Exit For
             End If
          End If
       Next
       If matches Then
          FindElement = i
       Else
          FindElement = -1
       End If
    End Function
  2. Run the VBscript code by double-clicking the "remove.vbs" file.

More Information

For more information about scripting and IIS configuration settings, see the following:

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Posted: Jun 01 2011, 11:19 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: IIS | Scripting
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Batch File: Delete Duplicate Files

Using this Batch File

Some time ago a friend of mine gave me a bunch of JPG files, but for some reason she had two copies of every image in the collection. The names of the images had all been randomized, and since there were hundreds of files in the collection it would have taken hours to find and delete the duplicates. With that in mind, I wrote the following batch file that loops through the collection of files and does a binary comparison to find and delete duplicate files.

To use the example code, copy the batch file code from below into Notepad and save it as "_del_dupes.cmd" in the folder where you have duplicate files

Note: As with many utilities that I write - this is a destructive operation, meaning that it will delete files without prompting, so you should always make a backup just in case something goes terribly wrong... ;-]

Batch File Example Code

@echo off

dir *.jpg /b > _del_dupes.1.txt

for /f "delims=|" %%a in (_del_dupes.1.txt) do (
   if exist "%%a" (
      dir *.jpg /b > _del_dupes.2.txt
      for /f "delims=|" %%b in (_del_dupes.2.txt) do (
         if not "%%a"=="%%b" (
            echo Comparing "%%a" to "%%b"...
            fc /b "%%a" "%%b">NUL
            if errorlevel 1 (
               echo DIFFERENT
            ) else (
               echo SAME
               del "%%b"
            )
         ) 
      ) 
   )
)

del _del_dupes.?.txt
Posted: Dec 24 2008, 14:27 by Bob | Comments (3)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Windows
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Creating XML Reports for FSRM Quota Usage

I had a great question in follow up to the "Secure, Simplified Web Publishing using Microsoft Internet Information Services 7.0" webcast that I delivered the other day, "How you can you programmatically access the quota usage information from the File Server Resource Manager (FSRM)?"

First of all, there is a native API for writing code to access FSRM data detailed at the following URL:

http://msdn2.microsoft.com/en-us/library/bb625489.aspx

That's a bit of overkill if you're just looking to script something.

There is a WMI interface as well, but it’s only for FSRM events.

So that leaves you with a pair of command-line tools that you can script in order to list your quota usage information:

  • storrept.exe - Used to manage storage reports
  • dirquota.exe - Used to manage quota usage

Right out of the box the first command-line tool, storrept.exe, can generate a detailed XML report using a user-definable scope. To see this in action, take the following example syntax and modify the scope parameter to your desired paths:

storrept.exe reports generate /Report:QuotaUsage /Format:XML /Scope:"C:\"

 You can also specify multiple paths in your scope using a pipe-delimited format like:

/Scope:"C:\Inetpub|D:\Inetpub"

When the command has finished, it will tell you the path to your report like the following example:

Storage reports generated successfully in "C:\StorageReports\Interactive".

The XML-based information in the report can then be consumed with whatever method you usually use to parse XML. It should be noted that storrept.exe also supports the following formats: CSV, DHTML, HTML, and TXT.

This XML might be okay for most applications, but for some reason I wanted to customize the information that I received, so I experimented with the second command-line tool, dirquota.exe, to get the result that I was looking for.

First of all, using dirquota.exe quota list returns information in the following format:

Quotas on machine SERVER: Quota Path: C:\inetpub\ftproot Source Template: 100 MB Limit (Matches template) Quota Status: Enabled Limit: 100.00 MB (Hard) Used: 1.00 KB (0%) Available: 100.00 MB Peak Usage: 1.00 KB (10/25/2007 2:15 PM) Thresholds: Warning ( 85%): E-mail Warning ( 95%): E-mail, Event Log Limit (100%): E-mail, Event Log

This information is formatted nicely and is therefore easily parsed, so I wrote the following batch file called "dirquota.cmd" to start things off:

@echo off echo Processing the report... dirquota.exe quota list > dirquota.txt cscript.exe //nologo dirquota.vbs

Next, I wrote the following vbscript application called "dirquota.vbs" to parse the output into some easily-usable XML code:

Option Explicit

Dim objFSO, objFile1, objFile2
Dim strLine, strArray(2)
Dim blnQuota,blnThreshold

' create objects
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFile1 = objFSO.OpenTextFile("dirquota.txt")
Set objFile2 = objFSO.CreateTextFile("dirquota.xml")

' start the XML output file
objFile2.WriteLine "<?xml version=""1.0""?>"
objFile2.WriteLine "<Quotas>"

' set the runtime statuses to off
blnQuota = False
blnThreshold = False

' loop through the text file
Do While Not objFile1.AtEndOfStream

  ' get a line from the file
  strLine = objFile1.ReadLine

  ' only process lines with a colon character
  If InStr(strLine,":") Then
    ' split the string manually at the colon character
    strArray(1) = Trim(Left(strLine,InStr(strLine,":")-1))
    strArray(2) = Trim(Mid(strLine,InStr(strLine,":")+1))

    ' filter on strings with parentheses
    strLine = strArray(1)
    If InStr(strLine,"(") Then
      strLine = Trim(Left(strLine,InStr(strLine,"(")-1)) & "*"
    End If

    ' process the inidivdual entries
    Select Case UCase(strLine)

      ' a quota path signifies a new record
      Case UCase("Quota Path")

        ' close any open threshold collections
        If blnThreshold = True Then
          objFile2.WriteLine "</Thresholds>"
        End If

        ' close an open quota element
        If blnQuota= True Then
          objFile2.WriteLine "</Quota>"
        End If

        ' signify a new quota element
        objFile2.WriteLine "<Quota>"

        ' output the relelvant information
        objFile2.WriteLine FormatElement(strArray(1),strArray(2))

        ' set the runtime statuses
        blnQuota= True
        blnThreshold = False

      ' these bits of informaiton are parts of a quota
      Case UCase("Source Template"), UCase("Quota Status"), _
          UCase("Limit"), UCase("Used"), _
          UCase("Available"), UCase("Peak Usage")

        ' close any open threshold collections
        If blnThreshold = True Then
          objFile2.WriteLine "</Thresholds>"
        End If

        ' set the runtime status
        blnThreshold = False

        ' output the relelvant information
        objFile2.WriteLine FormatElement(strArray(1),strArray(2))

      ' these bits of informaiton are thresholds
      Case UCase("Warning*"), UCase("Limit*")

        ' open a threshold collection if not already open
        If blnThreshold = False Then
          objFile2.WriteLine "<Thresholds>"
        End If

        ' output the relelvant information
        objFile2.WriteLine FormatElement( _
          Left(strLine,Len(strLine)-1), _
          Replace(Mid(strArray(1), _
          Len(strLine))," ","") & " " & strArray(2))

        ' set the runtime status
        blnThreshold = True

    End Select
  End If
Loop

' close any open threshold collections
If blnThreshold = True Then
  objFile2.WriteLine "</Thresholds>"
End If

' close an open quota element
If blnQuota= True Then
  objFile2.WriteLine "</Quota>"
End If

' end the XML output file
objFile2.WriteLine "</Quotas>"

objFile1.Close
objFile2.Close
Set objFSO = Nothing

' format data into an XML element
Function FormatElement(tmpName,tmpValue)
  FormatElement = "<" & Replace(tmpName," ","") & _
  ">" & tmpValue & "</" & Replace(tmpName,Chr(32),"") & ">"
End Function

When the batch file and vbscript are run, they will create a file named "dirquota.xml" which will resemble the following example XML:

<?xml version="1.0"?>
<Quotas>
  <Quota>
    <QuotaPath>C:\inetpub\ftproot</QuotaPath>
    <SourceTemplate>100 MB Limit (Matches template)</SourceTemplate>
    <QuotaStatus>Enabled</QuotaStatus>
    <Limit>100.00 MB (Hard)</Limit>
    <Used>1.00 KB (0%)</Used>
    <Available>100.00 MB</Available>
    <PeakUsage>1.00 KB (10/25/2007 2:15 PM)</PeakUsage>
    <Thresholds>
      <Warning>(85%) E-mail</Warning>
      <Warning>(95%) E-mail, Event Log</Warning>
      <Limit>(100%) E-mail, Event Log</Limit>
    </Thresholds>
  </Quota>
</Quotas>

I found the above XML much easier to use than the XML that came from the storrept.exe report, but I'm probably comparing apples to oranges. In any event, I hope this helps someone with questions about FSRM reporting.

Have fun!

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
Posted: Oct 25 2007, 10:27 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Scripting
Tags: , ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Viewing current FTP7 sessions using C#

A few weeks ago my friend Jaroslav posted a blog entry about viewing the current FTP7 sessions using Javascript, and I followed that up with a blog post about viewing the current FTP7 sessions using VBScript.

This blog entry follows up on those postings by showing you how to view the current FTP7 sessions using C#. To do so, start a new Windows Console Application project using C# in Visual Studio 2005 on a computer running Windows Server 2008 with the new FTP7 server installed. You will need to add a reference to the AppHostAdminLibrary by manually browsing to the nativerd.dll file that's located in the %WinDir%\System32\InetSrv folder. After you've added the reference, replace all of the C# code from the project template with the following C# code:

using System;
using System.Collections.Generic;
using System.Text;
using AppHostAdminLibrary;

namespace FtpDumpSessions
{
  class FtpDumpSessions
  {
    static void Main(string[] args)
    {
      AppHostWritableAdminManager objAdminManager =
        new AppHostWritableAdminManager();

      // get the collection of sites
      IAppHostElement objSitesElement =
        objAdminManager.GetAdminSection(
        "system.applicationHost/sites",
        "MACHINE/WEBROOT/APPHOST");
      uint intSiteCount =
        objSitesElement.Collection.Count;
      Console.WriteLine(
        "Site count: {0}",
        intSiteCount);

      try
      {
        // loop through the sites collection
        for (int intSite = 0;
          intSite < intSiteCount;
          ++intSite)
        {
          // get a site
          IAppHostElement objFtpSite =
            objSitesElement.Collection[intSite];

          // get the FTP section
          IAppHostElement objFtpSiteElement =
            objFtpSite.ChildElements["ftpServer"];

          // get the sessions collection
          IAppHostElement objFtpSessions =
            objFtpSiteElement.ChildElements["sessions"];
          uint intSessionCount =
            objFtpSessions.Collection.Count;
          Console.WriteLine(
            "\tFTP sessions for {0}: {1}",
            objFtpSite.Properties["name"].Value, intSessionCount);

          // loop through the sessions
          for (int intSession = 0;
            intSession < intSessionCount;
            ++intSession)
          {
            IAppHostElement objFtpSession =
              objFtpSessions.Collection[intSession];
            // loop through each session's properties
            for (int intProperty = 0;
              intProperty < objFtpSession.Properties.Count;
              ++intProperty)
            {
              Console.WriteLine(
                "\t\t{0}: {1}",
                objFtpSession.Properties[intProperty].Name,
                objFtpSession.Properties[intProperty].Value);
            }
          }
        }
      }
      catch (System.Exception ex)
      {
        Console.WriteLine(
          "\r\nError: {0}",
          ex.Message);
      }
    }
  }
}

When you compile and run the project, you should see a listing of all users connected to your FTP7 sites.

That's about it for this post - have fun!

Posted: Jul 06 2007, 01:59 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: FTP | Scripting
Tags: , ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Viewing current FTP7 sessions using VBScript

A few weeks ago my friend Jaroslav posted a blog entry about viewing the current FTP7 sessions using Javascript, and I followed that up with a blog post about viewing the current FTP7 sessions using C#.

This blog entry follows up on those postings by showing you how to view the current FTP7 sessions using VBScript. To do so, copy the following VBScript code to Windows Notepad and save the file as "ftp_sessions.vbs" on a computer running Windows Server 2008 with the new FTP7 server installed:

Option Explicit

Dim objAdminManager, objSiteCollection, objFtpSiteElement
Dim objSite, objFtpSession, objFtpSessions, objFtpProperty
Dim intSite, intFtpSession, intFtpProperty
Dim intSiteCount, intFtpSessionCount, intFtpPropertyCount

Set objAdminManager = WScript.CreateObject("Microsoft.ApplicationHost.AdminManager")

' get the collection of sites
Set objSiteCollection = objAdminManager.GetAdminSection( _
  "system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST" )

intSiteCount = CInt(objSiteCollection.Collection.Count)

WScript.Echo String(40,"*")
WScript.Echo "Site count: " & intSiteCount
WScript.Echo String(40,"*")

' loop through the sites collection
For intSite = 0 To intSiteCount-1

  ' get a site
  Set objSite = objSiteCollection.Collection.Item(intSite)
  
  ' get the FTP section
  Set objFtpSiteElement = objSite.ChildElements.Item("ftpServer")
  
  ' get the sessions collection
  Set objFtpSessions = objFtpSiteElement.ChildElements.Item("sessions")
  intFtpSessionCount = CInt(objFtpSessions.Collection.Count)

  WScript.Echo String(40,"=")
  WScript.Echo "FTP sessions for " & _
    objSite.Properties.Item("name").Value & _
    ": " & intFtpSessionCount
  WScript.Echo String(40,"=")

  ' loop through the sessions
  For intFtpSession = 0 To intFtpSessionCount - 1
    Set objFtpSession = objFtpSessions.Collection.Item(intFtpSession)
    intFtpPropertyCount = CInt(objFtpSession.Properties.Count)
    ' loop through each session's properties
    For intFtpProperty = 0 To intFtpPropertyCount - 1
      Set objFtpProperty = objFtpSession.Properties.Item(intFtpProperty)
      WScript.Echo CStr(objFtpProperty.Name) & ": " & CStr(objFtpProperty.Value)
    Next
    WScript.Echo String(40,"-")
  Next
Next

To make sure that you don't see any message box pop-ups, run the script from the command-line using the following syntax:

cscript.exe ftp_sessions.vbs

That's about it for this post - have fun!

Posted: Jul 06 2007, 01:59 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: FTP | Scripting
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Scripting MIDI Events in Sibelius

OK - I have to admit, when you realize that you are making software choices based on scripting language support you start to get the feeling that there are times when you just have to accept the fact that you are a geek.

Here's a case in point: I write music as a hobby, and when shopping for a program to write sheet music with, I chose Sibelius because I discovered that they have a really cool scripting language called "ManuScript". OK - so the name is kind of silly, but it's pretty cool to write code with.

The way it works is that you create what Sibelius calls a "plug-in", and you assign it to a category that will be used as the menu under which your plug-in will be displayed. Once you've done all that, you can start writing code.

For example, I needed to add sustain pedal MIDI events to an entire piano score, and doing so manually would have been a tedious exercise. So I made my life easier and created a quick plug-in that adds the MIDI events to apply the sustain pedal at full level to the beginning of every measure, and then adds the MIDI events to lift the sustain pedal at the end of every measure:

// Verify that a score is open.
if (Sibelius.ScoreCount=0)
{
   Sibelius.MessageBox("Please open a score.");
   return false;
}

// Retrieve a score object for the active score.
score = Sibelius.ActiveScore;
// Retrieve an object for the current selection.
selection = score.Selection;

if (selection.IsPassage)
{
   // Loop through the highlighted measures.
   for each Bar b in selection
   {
      // Add MIDI sustain pedal events.
      b.AddText(1,"~C64,127",TechniqueTextStyle);
      b.AddText(b.Length,"~C64,0",TechniqueTextStyle);
   }
   // Return a status message.    Sibelius.MessageBox("Finished."); }

I should point out, however, that this is meant to be a brief example of what you can do. Running this same plug-in on the same selection will re-add the sustain pedal events to your score; I didn't add any advanced logic to check for the existence of any prior sustain pedal events. If anyone wants to take on that challenge, have fun and don't forget to share your results!

Posted: May 11 2007, 04:45 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Scripting | MIDI
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Converting W3C log files to NCSA format

Around a year ago I wrote a blog entry titled "Converting NCSA log files to W3C format", which showed how to use the MSWC.IISLog object to convert log files in the NCSA format back to W3C format. I wrote that blog entry to make up for the fact that the CONVLOG.EXE utility only converts log files to NCSA format, which some older log analysis software packages require. So what happens if you have a bunch of log files in W3C format and you don't have a copy of CONVLOG.EXE on your computer?

This blog entry is something of a reverse direction on my previous post, and shows you how to use the MSUtil.LogQuery object to convert W3C log files to NCSA format. The MSUtil.LogQuery object is shipped with LogParser, which you can download from one of the following URLs:

Once you've downloaded and installed the LogParser package, you will need to manually register the LogParser.dll file in order to use the MSUtil.LogQuery object. Having done so, you can use the Windows Script Host (WSH) code in this blog article to convert a folder filled with W3C log files to NCSA format. 

To use this code, just copy the code into notepad, and save it with a ".vbs" file extension on your system. To run it, copy the script to a folder that contains your W3C log files, (named "ex*.log"), then double-click it.

Option Explicit
Dim objFSO
Dim objFolder
Dim objInputFile
Dim objOutputFile
Dim objLogQuery
Dim objLogRecordSet
Dim objLogRecord
Dim strInputPath
Dim strOutputPath
Dim strLogRecord
Dim strLogTemp

Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(".")

For Each objInputFile In objFolder.Files
 strInputPath = LCase(objInputFile.Name)
 If Left(strInputPath,2) = "ex" And Right(strInputPath,4) = ".log" Then
  strOutputPath = objFolder.Path & "\" & "nc" & Mid(strInputPath,3)
  strInputPath = objFolder.Path & "\" & strInputPath
  Set objLogQuery = CreateObject("MSUtil.LogQuery")
  Set objLogRecordSet = objLogQuery.Execute("SELECT * FROM " & strInputPath)
  Set objOutputFile = objFSO.CreateTextFile(strOutputPath)
  Do While Not objLogRecordSet.atEnd
  
   Set objLogRecord = objLogRecordSet.getRecord
   strLogRecord = FormatField(objLogRecord.getValue("c-ip"))
   strLogRecord = strLogRecord & " " & FormatField("")
   strLogRecord = strLogRecord & " " & FormatField(objLogRecord.getValue("cs-username"))
   strLogTemp = BuildDateTime(objLogRecord.getValue("date"),objLogRecord.getValue("time"))
   strLogRecord = strLogRecord & " " & FormatField(strLogTemp)
   strLogRecord = strLogRecord & " """ & FormatField(objLogRecord.getValue("cs-method"))
   strLogRecord = strLogRecord & " " & FormatField(objLogRecord.getValue("cs-uri-stem"))
   strLogTemp = FormatField(objLogRecord.getValue("cs-version"))
   If strLogTemp = "-" Then
    strLogRecord = strLogRecord & " HTTP/1.0"""
   Else
    strLogRecord = strLogRecord & " " & strLogTemp & """"
   End If   
   strLogRecord = strLogRecord & " " & FormatField(objLogRecord.getValue("sc-status"))
   strLogRecord = strLogRecord & " " & FormatField(objLogRecord.getValue("sc-bytes"))
    objOutputFile.WriteLine strLogRecord
   objLogRecordSet.moveNext
  Loop
  
  Set objLogQuery = Nothing
  objOutputFile.Close
 
 End If
Next

Function FormatField(tmpField)
 On Error Resume Next
 FormatField = "-"
 If Len(tmpField) > 0 Then FormatField = Trim(tmpField)
End Function

Function BuildDateTime(tmpDate,tmpTime)
 On Error Resume Next
 BuildDateTime = "[" & _
  Right("0" & Day(tmpDate),2) & "/" & _
  Left(MonthName(Month(tmpDate)),3) & "/" & _
  Year(tmpDate) & ":" & _
  Right("0" & Hour(tmpTime),2) & ":" & _
  Right("0" & Minute(tmpTime),2) & ":" & _
  Right("0" & Second(tmpTime),2) & _
  " +0000]"
End Function

I hope this helps!

Posted: Nov 06 2006, 21:08 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: IIS | LogParser
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Programmatically Enumerating Installations of the FrontPage Server Extensions

I had a great question from a customer the other day: "How do you programmatically enumerate how many web sites on a server have the FrontPage Server Extensions installed?" Of course, that's one of those questions that sounds so simple at first, and then you start to think about how to actually go about it and it gets a little more complicated.

The first thought that came to mind was to just look for all the "W3SVCnnnn" subfolders that are located in the "%ALLUSERSPROFILE%\Application Data\Microsoft\Web Server Extensions\50" folder. (These folders contain the "ROLES.INI" files for each installation.) The trouble with this solution is that some folders and files do not get cleaned up when the server extensions are uninstalled, so you'd get erroneous results.

The next thought that came to mind was to check the registry, because each installation of the server extensions will create a string value and subkey named "Port /LM/W3SVC/nnnn:" under the "[HKLM\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\Ports]" key. Enumerating these keys will give you the list of web sites that have the server extensions or SharePoint installed. The string values that are located under the subkey contain some additional useful information, so I thought that as long as I was enumerating the keys, I might as well enumerate those values.

The resulting script is listed below, and when run it will create a log file that lists all of the web sites that have the server extensions or SharePoint installed on the server that is specified by the "strComputer" constant.

Option Explicit

Const strComputer = "localhost"

Dim objFSO, objFile
Dim objRegistry
Dim strRootKeyPath, strSubKeyPath, strValue
Dim arrRootValueTypes, arrRootValueNames
Dim arrSubValueTypes, arrSubValueNames
Dim intLoopA, intLoopB

Const HKEY_LOCAL_MACHINE = &H80000002
Const REG_SZ = 1

strRootKeyPath = "Software\Microsoft\" & _
  "Shared Tools\Web Server Extensions\Ports"

Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile("ServerExtensions.Log")

objFile.WriteLine String(40,"-")
objFile.WriteLine "Report for server: " & UCase(strComputer)
objFile.WriteLine String(40,"-")

Set objRegistry = GetObject(_
  "winmgmts:{impersonationLevel=impersonate}!\\" & _
  strComputer & "\root\default:StdRegProv")
objRegistry.EnumValues HKEY_LOCAL_MACHINE, strRootKeyPath, _
  arrRootValueNames, arrRootValueTypes

For intLoopA = 0 To UBound(arrRootValueTypes)
  If arrRootValueTypes(intLoopA) = REG_SZ Then
    objFile.WriteLine arrRootValueNames(intLoopA)
    strSubKeyPath = strRootKeyPath & _
      "\" & arrRootValueNames(intLoopA)
    objRegistry.EnumValues HKEY_LOCAL_MACHINE, _
      strSubKeyPath, arrSubValueNames, arrSubValueTypes
    For intLoopB = 0 To UBound(arrSubValueTypes)
      If arrSubValueTypes(intLoopB) = REG_SZ Then
        objRegistry.GetStringValue HKEY_LOCAL_MACHINE, _
          strSubKeyPath, arrSubValueNames(intLoopB), strValue
        objFile.WriteLine vbTab & _
          arrSubValueNames(intLoopB) & "=" & strValue
      End If
    Next
    objFile.WriteLine String(40,"-")
  End If
Next

objFile.Close

The script should be fairly easy to understand, and you can customize it to suit your needs. For example, you could change the "strComputer" constant to a string array and loop through an array of servers.

Note: More information about the WMI objects used in the script can be found on the following pages:

Hope this helps!

Posted: Jul 11 2006, 21:02 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: FrontPage | Scripting
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

IIS 6: Reverting log files back to the default W3C fields

Recently I had to work with a customer that was trying to use a 3rd-party utility that read W3C log files and it was failing to complete processing. I had the customer send me his log files, and upon examination I discovered that the trouble was occuring because the customer had been experimenting with adding and removing the different fields from their log files and this was causing the log parsing utility to crash.

As luck would have it, IIS provides a useful logging utility object that you can read more about at the following URL:

I had used this logging utility object for an earlier project, so I was familiar with how it worked. With that knowledge in mind, I wrote the following script that loops through all of the log files in a folder and creates new log files in a subfolder that contain only the default W3C fields. (BTW - I sent this script to the customer and he was able to parse all of his log files successfully. ;-] )

Option Explicit
Randomize Timer

' Declare variables.

Dim objIISLog
Dim objFSO, objFolder, objFile
Dim objOutputFile, strInputFile
Dim strOutputFile, strOutputPath
Dim strLogRecord
Dim blnExists

' Create file system object.
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
' Retrieve an object For the current folder.
Set objFolder = objFSO.GetFolder(".")

' Create a subfolder with a random name.
blnExists = True
Do While blnExists = True
strOutputPath = objFolder.Path & "\" & CreateRandomName(20)
blnExists = objFSO.FolderExists(strOutputPath)
Loop
objFSO.CreateFolder strOutputPath

' Loop through the log files in the current folder.
For Each objFile In objFolder.Files

' Test for a log file.
If Right(LCase(objFile.Name),4) = ".log" Then

' Format the file names/paths.
strInputFile = objFolder.Path & "\" & objFile.Name
strOutputFile = strOutputPath & "\" & objFile.Name

' Create and open an IIS logging object.
Set objIISLog = CreateObject("MSWC.IISLog")
' Open the input log file.
objIISLog.OpenLogFile strInputFile, 1, "", 0, ""
' Open the output log file.
Set objOutputFile = objFSO.CreateTextFile(strOutputFile)

' Read the initial record from the log file.
objIISLog.ReadLogRecord

' Write the headers to the output log file.
objOutputFile.WriteLine "#Software: Microsoft Internet Information Services 5.0"
objOutputFile.WriteLine "#Version: 1.0"
objOutputFile.WriteLine "#Date: " & BuildDateTime(objIISLog.DateTime)
objOutputFile.WriteLine "#Fields: date time c-ip cs-username s-ip s-port " & _
"cs-method cs-uri-stem cs-uri-query sc-status cs(User-Agent)"

' Loop through the records in the log file.
Do While Not objIISLog.AtEndOfLog

' Format the log file fields.
strLogRecord = BuildDateTime(objIISLog.DateTime)
strLogRecord = strLogRecord & _
" " & FormatField(objIISLog.ClientIP) & _
" " & FormatField(objIISLog.UserName) & _
" " & FormatField(objIISLog.ServerIP) & _
" " & FormatField(objIISLog.ServerPort) & _
" " & FormatField(objIISLog.Method) & _
" " & FormatField(objIISLog.URIStem) & _
" " & FormatField(objIISLog.URIQuery) & _
" " & FormatField(objIISLog.ProtocolStatus) & _
" " & FormatField(objIISLog.UserAgent)

' Write the output log file record.
objOutputFile.WriteLine strLogRecord

' Read the next record from the log file.
objIISLog.ReadLogRecord

Loop

' Close the input log file.
objIISLog.CloseLogFiles 1
objIISLog = Null

End If

Next

' Inform the user that the operation has completed.
MsgBox "Finished!"

' Format a log file field.
Function FormatField(tmpField)
On Error Resume Next
FormatField = "-"
If Len(tmpField) > 0 Then FormatField = Trim(tmpField)
End Function

' Format a log file date.
Function BuildDateTime(tmpDateTime)
On Error Resume Next
tmpDateTime = CDate(tmpDateTime)
BuildDateTime = Year(tmpDateTime) & "-" & _
Right("0" & Month(tmpDateTime),2) & "-" & _
Right("0" & Day(tmpDateTime),2) & " " & _
Right("0" & Hour(tmpDateTime),2) & ":" & _
Right("0" & Minute(tmpDateTime),2) & ":" & _
Right("0" & Second(tmpDateTime),2)
End Function

' Create a random name.
Function CreateRandomName(intNameLength)
Const strValidChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Dim tmpX, tmpY, tmpZ
For tmpX = 1 To intNameLength
tmpY = Mid(strValidChars,Int(Rnd(1)*Len(strValidChars))+1,1)
tmpZ = tmpZ & tmpY
Next
CreateRandomName = tmpZ
End Function

Happy coding!

Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Posted: Mar 27 2006, 12:01 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: IIS | Scripting
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

IIS 6: Creating an Itemized List of Server Bindings

One of my servers has a large number of individual web sites on it, and each of these web sites has several server bindings for different IP addresses, Port Assignments, and Host Headers. As I continue to add more web sites on the server, it becomes increasingly difficult to keep track of all the details using the IIS user interface.

With that in mind, I wrote the following ADSI script which creates a text file that contains an itemized list of all server bindings on a server.

Option Explicit
On Error Resume Next

Dim objBaseNode, objChildNode
Dim objBindings, intBindings
Dim objFSO, objFile, strOutput

' get a base object
Set objBaseNode = GetObject("IIS://LOCALHOST/W3SVC")
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile("ServerBindings.txt")

' check if if have an error ...
If (Err.Number <> 0) Then

    ' ... and output the error.
    strOutput = "Error " & Hex(Err.Number) & "("
    strOutput = strOutput & Err.Description & ") occurred."

' ... otherwise, continue processing.
Else
    ' loop through the child nodes
    For Each objChildNode In objBaseNode
        ' is this node for a web site?
        If objChildNode.class = "IIsWebServer" Then
            ' get the name of the node
            strOutput = strOutput & "LM/W3SVC/" & _
                objChildNode.Name
            ' get the server comment
            strOutput = strOutput & " (" & _
                objChildNode.ServerComment & ")" & vbCrLf
            ' get the bindings
            objBindings = objChildNode.ServerBindings
            ' loop through the bindings
            For intBindings = 0 To UBound(objBindings)
                strOutput = strOutput & vbTab & _
                    Chr(34) & objBindings(intBindings) & _
                    Chr(34) & vbCrLf
            Next
        End If
    ' try not to be a CPU hog
    Wscript.Sleep 10
    Next
End If

objFile.Write strOutput
objFile.Close

Set objBaseNode = Nothing
Set objFSO = Nothing

Hope this helps!

Posted: Feb 21 2006, 14:47 by Bob | Comments (0)
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: IIS | Scripting
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us