<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Robert McMurray's Blog [MSFT]</title><link>http://blogs.msdn.com/robert_mcmurray/default.aspx</link><description>IIS, FTP, WebDAV, FPSE, WMI, ADSI, ISAPI, ASP, FastCGI, etc. ;-)</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Using URL Rewrite with Web Site Testing</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/10/13/using-url-rewrite-with-web-site-testing.aspx</link><pubDate>Tue, 13 Oct 2009 22:49:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9906874</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9906874.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9906874</wfw:commentRss><description>&lt;P&gt;Recently I ran into an interesting situation when I was rolling out a new web-based application. I had tested it on my development server, and because it was a rather complex application I wanted to test it on my production server before making it live. I had already set up a placeholder web site with a home page that announced the new site as "Coming Soon," and I had some of the supporting infrastructure configured for the placeholder site: database connections, permissions, FTP bindings, etc.&lt;/P&gt;
&lt;P&gt;In order to test the new site, I could have set up a temporary web site by duplicating the placeholder web site, running my tests, and then deleting the temporary site after I was comfortable that everything was working in production. Or I could simply remove the placeholder web site and replace it with the temporary web site once the testing phase was over. I didn't like either of those ideas, so I came up with much easier solution using URL Rewrite. Here are the details:&lt;/P&gt;
&lt;P&gt;First, I added an additional host header binding to the placeholder web site with a temporary name that only I knew about. Since I use a wildcard A record for my DNS, I can add any additional prefixes to my domain name without registering those as CNAME records in DNS. (This makes the temporary name more difficult to discover.) For example, if I created an A record for "*.example.com," then I could use "temp.example.com" without any changes to DNS.&lt;/P&gt;
&lt;P&gt;Second, I added a URL Rewrite rule that checked all inbound requests by using the following logic:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;If the request was for "temp.example.com", then URL Rewrite would allow regular access the web site.&lt;/LI&gt;
&lt;LI&gt;If the request was for any other domain name and any web page other than "ComingSoon.htm", then URL Rewrite would redirect those requests to "/ComingSoon.htm".&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Here's what the URL Rewrite rule looked like in my web.config file:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black" id=codeSnippet&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;system.webServer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;  &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;rewrite&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;rules&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;      &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;clear&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;      &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;rule&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Temporary Redirect"
         &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;patternSyntax&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Wildcard"
         &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;stopProcessing&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="true"&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;match&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;url&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="ComingSoon.htm"
         &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;negate&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="true"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;conditions&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;          &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;input&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="{HTTP_HOST}"
           &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;negate&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="true"
           &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;pattern&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="temp.example.com"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;conditions&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;action&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;type&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Redirect"
         &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;url&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="/ComingSoon.htm"
          &lt;/SPAN&gt;&lt;SPAN style="COLOR: #ff0000"&gt;appendQueryString&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="false"
         &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;redirectType&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Found"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;      &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;rule&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;rules&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;  &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;rewrite&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;system.webServer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;This allowed me to test the web site on my production server by using a temporary domain name that no one else knew about, and sending every other HTTP request to the "Coming Soon" announcement page. After a few days of putting the web site through some test passes, all that I needed to do was to remove the URL Rewrite rule and the site was officially live.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9906874" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category></item><item><title>Migrating FPSE Roles Between Servers</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/10/12/migrating-fpse-roles-between-servers.aspx</link><pubDate>Mon, 12 Oct 2009 17:35:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9906215</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9906215.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9906215</wfw:commentRss><description>&lt;P&gt;I ran into one of those strange situations the other day where you feel like you've been doing the same thing on your computer at some time in the distant past - kind of like déjà vu for geeks. In this specific case, I was moving some web sites that I am hosting for other people that still use FPSE from an older physical server to a new virtual server that is hosted through Hyper-V. (I'm also trying to convert them all to WebDAV, but that's another story.)&lt;/P&gt;
&lt;P&gt;Anyway, I had dozens of custom FPSE roles set up for each of those sites that I didn't want to manually replicate on the new server. Unfortunately, FPSE doesn't have a way to migrate the roles from one server to another. All of those FPSE-related roles are kept in local groups with cryptic names like &lt;B&gt;OWS_nnnnn_xxxxx&lt;/B&gt;, so I started thinking, "&lt;I&gt;If only I could write a script that could migrate the OWS_nnnnn_xxxxx groups between the two servers...&lt;/I&gt;"&lt;/P&gt;
&lt;P&gt;Then it dawned on me - I had written such a script several years ago! (Now if only I could find it...) Like many people that write code, I'm something of a code packrat - I tend to keep all of my old code around somewhere, just in case. Sparing you the details of my long search, I eventually found the script that I was looking for, and I thought that it would make a nice blog because I'm sure that someone else may need to migrate their FPSE roles.&lt;/P&gt;
&lt;P&gt;Here's the script and a brief description of what script it will do:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Create ADSI objects for the source and destination servers. &lt;/LI&gt;
&lt;LI&gt;Loop through the ADSI objects and only looks for groups. 
&lt;UL&gt;
&lt;LI&gt;Note: You could also use an &lt;CODE&gt;object.Filter&lt;/CODE&gt; statement for this. &lt;/LI&gt;
&lt;LI&gt;You will obviously need to update the server names for your source and destination servers. &lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;Compare each group name with the group stub and only process those groups that match the stub. 
&lt;UL&gt;
&lt;LI&gt;By way of explanation, FPSE role groups have names like &lt;B&gt;OWS_nnnnn_xxxxx&lt;/B&gt;, where &lt;B&gt;nnnnn&lt;/B&gt; is a simple numeric hash that identifies the site, and the &lt;B&gt;xxxxx&lt;/B&gt; denotes the individual FPSE role like &lt;I&gt;admin&lt;/I&gt;, &lt;I&gt;browser&lt;/I&gt;, &lt;I&gt;author&lt;/I&gt;, etc. &lt;/LI&gt;
&lt;LI&gt;The script uses the &lt;I&gt;OWS_nnnnn&lt;/I&gt; stub and will copy all of the role groups that it finds. For example: &lt;I&gt;OWS_12345_admin&lt;/I&gt;, &lt;I&gt;OWS_12345_author&lt;/I&gt;, &lt;I&gt;OWS_12345_browser&lt;/I&gt;, etc. &lt;/LI&gt;
&lt;LI&gt;In the code I used &lt;I&gt;OWS_nnnnn&lt;/I&gt; for the stub, so you would have to replace &lt;I&gt;nnnnn&lt;/I&gt; with the numbers that you see in the list of groups using Computer Management on the source computer. &lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;Determine if the group exists on the destination server, and creates it if it doesn't already exist. &lt;/LI&gt;
&lt;LI&gt;Loop through the list of users in the group on the source server and adds those same users to the group on the destination server. 
&lt;UL&gt;
&lt;LI&gt;Note: This will fail for any local users that were used on the source server that do not exist on the destination server. I only used domain accounts, so I didn't run into that problem. &lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Some additional notes:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The person that runs this code must be an administrator on each server. &lt;/LI&gt;
&lt;LI&gt;The two computers must be on the same network or the ADSI calls will fail. &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;With that in mind, here's the script code:&lt;/P&gt;
&lt;HR&gt;
&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;Option&lt;/SPAN&gt; Explicit
&lt;SPAN style="COLOR: #0000ff"&gt;On&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Error&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Resume&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Next&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objComputer1, objGroup1
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objComputer2, objGroup2

&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objOBJECT
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objUSER
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; strGroupName
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; strGroupDesc

&lt;SPAN style="COLOR: #0000ff"&gt;Const&lt;/SPAN&gt; ERROR_SUCCESS = 0

&lt;SPAN style="COLOR: #008000"&gt;' Note: update the following server names.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Const&lt;/SPAN&gt; strComputer1 = &lt;SPAN style="COLOR: #800000"&gt;"SERVER1"&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Const&lt;/SPAN&gt; strComputer2 = &lt;SPAN style="COLOR: #800000"&gt;"SERVER2"&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #008000"&gt;' Note: update the following group stub.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Const&lt;/SPAN&gt; strGroupStub = &lt;SPAN style="COLOR: #800000"&gt;"OWS_nnnnn"&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #008000"&gt;' ----------------------------------------------------------------------&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objComputer1 = GetObject(&lt;SPAN style="COLOR: #800000"&gt;"WinNT://"&lt;/SPAN&gt; &amp;amp; strComputer1 &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;",computer"&lt;/SPAN&gt;)
&lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError
&lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objComputer2 = GetObject(&lt;SPAN style="COLOR: #800000"&gt;"WinNT://"&lt;/SPAN&gt; &amp;amp; strComputer2 &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;",computer"&lt;/SPAN&gt;)
&lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError

&lt;SPAN style="COLOR: #0000ff"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Each&lt;/SPAN&gt; objOBJECT &lt;SPAN style="COLOR: #0000ff"&gt;in&lt;/SPAN&gt; objComputer1
  &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; UCase(objOBJECT.&lt;SPAN style="COLOR: #0000ff"&gt;Class&lt;/SPAN&gt;) = &lt;SPAN style="COLOR: #800000"&gt;"GROUP"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    strGroupName = objOBJECT.Name

    &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; UCase(Left(strGroupName,Len(strGroupStub))) = UCase(strGroupStub) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;

      Err.Clear : &lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objGroup1 = GetObject(&lt;SPAN style="COLOR: #800000"&gt;"WinNT://"&lt;/SPAN&gt; &amp;amp; strComputer1 &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"/"&lt;/SPAN&gt; &amp;amp; strGroupName &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;",group"&lt;/SPAN&gt;)
      &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError

      strGroupDesc = objGroup1.Description
      WScript.Echo &lt;SPAN style="COLOR: #800000"&gt;"Copying "&lt;/SPAN&gt; &amp;amp; strGroupName &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"..."&lt;/SPAN&gt;

      Err.Clear : &lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objGroup2 = GetObject(&lt;SPAN style="COLOR: #800000"&gt;"WinNT://"&lt;/SPAN&gt; &amp;amp; strComputer2 &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"/"&lt;/SPAN&gt; &amp;amp; strGroupName &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;",group"&lt;/SPAN&gt;)
      
      &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; -2147022676 &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
          HandleError
        &lt;SPAN style="COLOR: #0000ff"&gt;Else&lt;/SPAN&gt;
          Err.Clear : &lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objGroup2 = objComputer2.Create(&lt;SPAN style="COLOR: #800000"&gt;"group"&lt;/SPAN&gt;,strGroupName)
          &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError

          Err.Clear : objGroup2.SetInfo
          &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError

          Err.Clear : objGroup2.Description = strGroupDesc
          &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError

          Err.Clear : objGroup2.SetInfo
          &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError
        &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;

      &lt;SPAN style="COLOR: #0000ff"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Each&lt;/SPAN&gt; objUSER &lt;SPAN style="COLOR: #0000ff"&gt;in&lt;/SPAN&gt; objGroup1.Members
        WScript.Echo vbTab &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"Adding "&lt;/SPAN&gt; &amp;amp; objUSER.Name
        Err.Clear : objGroup2.Add objUSER.ADsPath 
        &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; -2147023518 &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError
        Err.Clear : objGroup2.SetInfo
        &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;lt;&amp;gt; ERROR_SUCCESS &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt; HandleError
      &lt;SPAN style="COLOR: #0000ff"&gt;Next&lt;/SPAN&gt;

      &lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objGroup1 = &lt;SPAN style="COLOR: #0000ff"&gt;Nothing&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objGroup2 = &lt;SPAN style="COLOR: #0000ff"&gt;Nothing&lt;/SPAN&gt;
      
    &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Next&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #008000"&gt;' ----------------------------------------------------------------------&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #0000ff"&gt;Sub&lt;/SPAN&gt; HandleError()
  WScript.Echo vbCrLf &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"FATAL ERROR:"&lt;/SPAN&gt;
  WScript.Echo vbTab &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"Number: "&lt;/SPAN&gt; &amp;amp; &lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number) &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;" (0x"&lt;/SPAN&gt; &amp;amp; Hex(&lt;SPAN style="COLOR: #0000ff"&gt;CLng&lt;/SPAN&gt;(Err.Number)) &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;")"&lt;/SPAN&gt;
  WScript.Echo vbTab &amp;amp; &lt;SPAN style="COLOR: #800000"&gt;"Description: "&lt;/SPAN&gt; &amp;amp; Err.Description
  WScript.Quit
&lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; Sub&lt;/PRE&gt;
&lt;HR&gt;

&lt;P&gt;As usual, all of the normal caveats like "&lt;I&gt;this code is totally unsupported&lt;/I&gt;" will apply, but I've used this code with great success on several severs over the years. The great thing about this code is that it's non-destructive because it doesn't delete anything on the source server - it only creates groups on the destination server.&lt;/P&gt;
&lt;P&gt;You can also use &lt;TT&gt;&lt;SPAN style="COLOR: #0000ff"&gt;Const&lt;/SPAN&gt; strGroupStub = &lt;SPAN style="COLOR: #800000"&gt;"OWS_"&lt;/SPAN&gt;&lt;/TT&gt; to migrate all FPSE role groups from one server to another, but that's a major operation. With that in mind, I'd try it out on a single FPSE site using "&lt;B&gt;OWS_nnnnn&lt;/B&gt;" as a stub before trying out a full server by using "&lt;B&gt;OWS_&lt;/B&gt;" as a stub; the latter is very time-consuming and CPU-intensive.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9906215" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FrontPage+Topics/default.aspx">FrontPage Topics</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/Scripting/default.aspx">Scripting</category></item><item><title>Creating a Microsoft Access Provider for IIS Database Manager</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/09/25/creating-a-microsoft-access-provider-for-iis-database-manager.aspx</link><pubDate>Sat, 26 Sep 2009 01:22:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9899735</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9899735.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9899735</wfw:commentRss><description>&lt;P&gt;Following up on my &lt;A href="http://blogs.msdn.com/robert_mcmurray/archive/2009/09/24/database-manager-api-topics-on-msdn.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2009/09/24/database-manager-api-topics-on-msdn.aspx"&gt;last blog post&lt;/A&gt; about the API set for the &lt;A href="http://www.iis.net/extensions/DatabaseManager" target=_blank mce_href="http://www.iis.net/extensions/DatabaseManager"&gt;IIS Database Manager&lt;/A&gt;, I have something of a secret to let you in on - you can use the code samples in several of those API documents to create a fully functional provider for Microsoft Access databases. I would never use an Access database in a production environment, but having an Access provider has had some great benefits for me from a test perspective. I often use Access databases for test projects, and using the IIS Database Manager to manage the Access databases on my test systems means that I don't need to install Microsoft Access on any of my test servers.&lt;/P&gt;
&lt;P&gt;That being said, as I was writing the API documentation I needed to create something of value to test my code samples. Since the Database Manager feature team was already creating database providers for SQL Server and MySQL, it seemed to me like Microsoft Access was the only other readily-accessible database that I could use for my samples. I mentioned in my last post that Saad Ladki was the Program Manager for Database Manager; Saad had started work on an Access provider at one point, but he abandoned the provider as his schedule grew tighter, so I took over that project so I could use it for the API samples.&lt;/P&gt;
&lt;P&gt;With that in mind, here's what you need to do to create a Microsoft Access provider for the IIS Database Manager:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Create a new class project named "AccessDatabase" in Visual Studio&lt;/LI&gt;
&lt;LI&gt;Add project references for: 
&lt;UL&gt;
&lt;LI&gt;System.Configuration&lt;/LI&gt;
&lt;LI&gt;Microsoft.Web.Management.DatabaseManager&lt;BR&gt;&lt;B&gt;Note&lt;/B&gt;: This may require adding a reference path for your project, such as "C:\Windows\assembly\GAC_MSIL\Microsoft.Web.Management.DatabaseManager\1.0.1.0__31bf3856ad364e35", where "C:" is your operating system drive.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;Open the class in the editor, remove the existing code, and insert the following empty class:&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Collections;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Collections.Generic;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Configuration.Provider;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Data;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Data.Common;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Diagnostics;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.IO;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Linq;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Text;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Reflection;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Data.OleDb;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; Microsoft.Web.Management.DatabaseManager;

&lt;SPAN style="COLOR: #0000ff"&gt;namespace&lt;/SPAN&gt; AccessDatabase
{
    &lt;SPAN style="COLOR: #0000ff"&gt;public&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;class&lt;/SPAN&gt; &lt;SPAN style="COLOR: #2992ad"&gt;AccessProvider&lt;/SPAN&gt; :
        &lt;SPAN style="COLOR: #2992ad"&gt;DatabaseProvider&lt;/SPAN&gt;,
        &lt;SPAN style="COLOR: #2992ad"&gt;IDbTableManager&lt;/SPAN&gt;,
        &lt;SPAN style="COLOR: #2992ad"&gt;IDbTableDataManager&lt;/SPAN&gt;,
        &lt;SPAN style="COLOR: #2992ad"&gt;IDbViewManager&lt;/SPAN&gt;,
        &lt;SPAN style="COLOR: #2992ad"&gt;IDbBackupManager&lt;/SPAN&gt;,
        &lt;SPAN style="COLOR: #2992ad"&gt;IDbRestoreManager&lt;/SPAN&gt;
    {

    }
}&lt;/PRE&gt;&lt;/LI&gt;
&lt;LI&gt;Copy the code samples from the following MSDN topics into the class; note that some of the utility methods in the code samples will be duplicated so you'll have to remove the duplicate methods: 
&lt;UL&gt;
&lt;LI&gt;&lt;U&gt;DatabaseProvider&lt;/U&gt; 
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.connectionstringarguments.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.connectionstringarguments.aspx"&gt;ConnectionStringArguments Property&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.calculateconnectionstring.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.calculateconnectionstring.aspx"&gt;CalculateConnectionString Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/ee255810.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/ee255810.aspx"&gt;ExecuteQuery Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.testconnection.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.testconnection.aspx"&gt;TestConnection Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.getdatabasehostname.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.getdatabasehostname.aspx"&gt;GetDatabaseHostName Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.getservertypes.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.getservertypes.aspx"&gt;GetServerTypes Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.getdatabaseinfo.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.getdatabaseinfo.aspx"&gt;GetDatabaseInfo Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.verifydependencies.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.databaseprovider.verifydependencies.aspx"&gt;VerifyDependencies Method&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;U&gt;IDbTableManager&lt;/U&gt; 
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.createtable.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.createtable.aspx"&gt;CreateTable Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.droptable.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.droptable.aspx"&gt;DropTable Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.edittable.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.edittable.aspx"&gt;EditTable Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.getprimarykey.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.getprimarykey.aspx"&gt;GetPrimaryKey Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.gettableinfo.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.gettableinfo.aspx"&gt;GetTableInfo Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.gettables.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtablemanager.gettables.aspx"&gt;GetTables Method&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;U&gt;IDbTableDataManager&lt;/U&gt; 
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.deleterow.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.deleterow.aspx"&gt;DeleteRow Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.editrow.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.editrow.aspx"&gt;EditRow Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.getdata.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.getdata.aspx"&gt;GetData Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.insertrow.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbtabledatamanager.insertrow.aspx"&gt;InsertRow Method&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;U&gt;IDbViewManager&lt;/U&gt; 
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.createview.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.createview.aspx"&gt;CreateView Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.dropview.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.dropview.aspx"&gt;DropView Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.editview.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.editview.aspx"&gt;EditView Method&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.getviews.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbviewmanager.getviews.aspx"&gt;GetViews Method&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;U&gt;IDbBackupManager&lt;/U&gt; 
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbbackupmanager.createbackup.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbbackupmanager.createbackup.aspx"&gt;CreateBackup Method&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;U&gt;IDbRestoreManager&lt;/U&gt; 
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbrestoremanager.restorebackup.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.idbrestoremanager.restorebackup.aspx"&gt;RestoreBackup Method&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;Save, compile, sign, and GAC the provider. For more information about using the Gacutil.exe tool, see the following topic on Microsoft the MSDN Web site: 
&lt;BLOCKQUOTE&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/ex0ss12c(VS.80).aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/ex0ss12c(VS.80).aspx"&gt;Global Assembly Cache Tool (Gacutil.exe)&lt;/A&gt;&lt;/BLOCKQUOTE&gt;&lt;/LI&gt;
&lt;LI&gt;Follow the instructions in the &lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.aspx"&gt;Microsoft.Web.Management.DatabaseManager Namespace&lt;/A&gt; topic to add the following entry to your administration.config file, which will register the provider for IIS Manager:&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;provider&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Access Provider"
  &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;providerName&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="System.Data.OleDb"
  &lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;type&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="AccessDatabase.AccessProvider,AccessDatabase,Version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
   &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;databaseBackup&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #ff0000"&gt;backupEnabled&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="true"&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #ff0000"&gt;restoreEnabled&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="true"&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #ff0000"&gt;backupPath&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\backups"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;provider&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: You will need to update the PublicKeyToken with the public key token from your assembly.&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;Add an OLEDB connection string for your Access database; the following web.config sample shows what that might look like:&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;configuration&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;connectionStrings&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Northwind"&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #ff0000"&gt;providerName&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="System.Data.OleDb"&lt;/SPAN&gt;
      &lt;SPAN style="COLOR: #ff0000"&gt;connectionString&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\inetpub\wwwroot\App_Data\Northwind.mdb;"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;connectionStrings&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;configuration&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: Since the code samples were all written using OLEDB, not ODBC, you will need to make sure that you use an OLEDB connection string like my example.&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;Open the IIS Manager and open the Database Manager for the site where you added the connection string. You should now be able to manage the tables and views for your Access database, as well as backing up and restoring your database.&lt;/LI&gt;&lt;/UL&gt;
&lt;H3&gt;Additional Notes&lt;/H3&gt;
&lt;H4&gt;Creating Database Backups&lt;/H4&gt;
&lt;P&gt;Even though I wrote my provider to implement backing up and restoring databases, this functionality is purely optional - you could easily remove the &lt;SPAN style="COLOR: #2992ad"&gt;IDbBackupManager&lt;/SPAN&gt; and &lt;SPAN style="COLOR: #2992ad"&gt;IDbRestoreManager&lt;/SPAN&gt; implementations from the class and remove the &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;databaseBackup&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt; element from administration.config.&lt;/P&gt;
&lt;H4&gt;OLEDB Errors When Saving JOIN/ORDER BY Views (Queries)&lt;/H4&gt;
&lt;P&gt;Unfortunately, there is one unexpected OLEDB problem with Microsoft Access databases that I ran into that I could not work around. When you attempt to save a view (query) that contains both &lt;CODE style="COLOR: #0000ff"&gt;JOIN&lt;/CODE&gt; statements and &lt;CODE style="COLOR: #0000ff"&gt;ORDER BY&lt;/CODE&gt; statements, you will receive an error that states "Only simple SELECT queries are allowed in VIEWS."&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;IMG src="http://y7b40a.blu.livefilestore.com/y1pjFQ4CUsjEl2QqYKBlpvHBm0umh-Sr24a2Z-GifS8O97pPE7nqXznC_8Bdz1q_4L6ZE_YPl4uaEIs3kR33m8LuBDiFfbvMoln/SimpleSelectQueries.jpg" width=338 height=145 mce_src="http://y7b40a.blu.livefilestore.com/y1pjFQ4CUsjEl2QqYKBlpvHBm0umh-Sr24a2Z-GifS8O97pPE7nqXznC_8Bdz1q_4L6ZE_YPl4uaEIs3kR33m8LuBDiFfbvMoln/SimpleSelectQueries.jpg"&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;As long as the SQL code is syntactically correct you can execute the query, but you cannot save it. If a query that contains both &lt;CODE style="COLOR: #0000ff"&gt;JOIN&lt;/CODE&gt; statements and &lt;CODE style="COLOR: #0000ff"&gt;ORDER BY&lt;/CODE&gt; statements has already been created in Microsoft Access, you can open it and make changes, but you will not be able to save those changes back to the database.&lt;/P&gt;
&lt;P&gt;For example, the following SQL code will cause the error to occur:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;SELECT&lt;/SPAN&gt; Categories.CategoryName, Products.ProductName
&lt;SPAN style="COLOR: #0000ff"&gt;FROM&lt;/SPAN&gt; Products &lt;SPAN style="COLOR: #0000ff"&gt;INNER&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;JOIN&lt;/SPAN&gt; Categories &lt;SPAN style="COLOR: #0000ff"&gt;ON&lt;/SPAN&gt; Products.CategoryID = Categories.CategoryID
&lt;SPAN style="COLOR: #0000ff"&gt;ORDER&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;BY&lt;/SPAN&gt; Categories.CategoryName, Products.ProductName;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;While the following code will not cause the error:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;SELECT&lt;/SPAN&gt; Categories.CategoryName, Products.ProductName
&lt;SPAN style="COLOR: #0000ff"&gt;FROM&lt;/SPAN&gt; Products &lt;SPAN style="COLOR: #0000ff"&gt;INNER&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;JOIN&lt;/SPAN&gt; Categories &lt;SPAN style="COLOR: #0000ff"&gt;ON&lt;/SPAN&gt; Products.CategoryID = Categories.CategoryID;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Even though the SQL code is syntactically correct and will execute without any problems, the error occurs at the OLEDB layer when the provider executes the &lt;CODE style="COLOR: #0000ff"&gt;CREATE VIEW&lt;/CODE&gt; statement to save the view to the database. This appears to be an unfortunate and irresolvable OLEDB limitation when you are using an Access database. When you are faced with such a situation, you will need to open the database in Microsoft Access to save the query.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9899735" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category></item><item><title>Database Manager API Topics on MSDN</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/09/24/database-manager-api-topics-on-msdn.aspx</link><pubDate>Thu, 24 Sep 2009 21:16:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9899083</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9899083.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9899083</wfw:commentRss><description>&lt;P&gt;Today Microsoft released version 1.0 of the &lt;A href="http://www.iis.net/extensions/DatabaseManager" target=_blank mce_href="http://www.iis.net/extensions/DatabaseManager"&gt;IIS Database Manager&lt;/A&gt;, which enables you to manage local and remote SQL Server or MySQL databases through the IIS Manager. I cannot stress enough how this module has rapidly become one of my favorite extensions for IIS Manager. There are many times when I need to access the data in one of my databases where opening the database management tool would be inconvenient or impossible. (For example, when I am working remotely, or when I don't have the database management tools installed.) In these situations, the Database Manager has been worth its weight in gold.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://kcqorw.blu.livefilestore.com/y1p3SKuJU-n7sGoMIdJtUXyKBtJ9WL-5bXR3digSGCObRXLo5Hi3rmeiGNscNL3Jnl4dwQDaDKAxEFwd3ThxFAOQFQDjUkfgjGp/Database_Manager_UI.jpg" target=_blank mce_href="http://kcqorw.blu.livefilestore.com/y1p3SKuJU-n7sGoMIdJtUXyKBtJ9WL-5bXR3digSGCObRXLo5Hi3rmeiGNscNL3Jnl4dwQDaDKAxEFwd3ThxFAOQFQDjUkfgjGp/Database_Manager_UI.jpg"&gt;&lt;IMG src="http://kcqorw.blu.livefilestore.com/y1pEMF4wLCYxxLX4u0uUdhLLSn5KIsJkFFgbhxtMHceCBRSOZku4LGMO-lI3hnB3aPxwO7qHgyVpNtu1rcNZEi-vp8-ihWb4dhg/DatabaseManager_small.jpg" width=640 height=467 mce_src="http://kcqorw.blu.livefilestore.com/y1pEMF4wLCYxxLX4u0uUdhLLSn5KIsJkFFgbhxtMHceCBRSOZku4LGMO-lI3hnB3aPxwO7qHgyVpNtu1rcNZEi-vp8-ihWb4dhg/DatabaseManager_small.jpg"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Another great feature of the IIS Database Manager is that it is extensible, like every other part of IIS these days, meaning that you can create your own database providers. Following up on that thought, in addition to all of the walkthroughs and blog posts that I've been writing about FTP and WebDAV over the past several months, I've also been working on documenting the API set for the &lt;A href="http://www.iis.net/extensions/DatabaseManager" target=_blank mce_href="http://www.iis.net/extensions/DatabaseManager"&gt;IIS Database Manager&lt;/A&gt;, and all of those topics have been published on the MSDN Web site under the following URL:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.aspx" mce_href="http://msdn.microsoft.com/en-us/library/microsoft.web.management.databasemanager.aspx"&gt;Microsoft.Web.Management.DatabaseManager Namespace&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I would be remiss if I didn't give credit where it's due, so my special thanks go to:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;B&gt;The Database Manager Feature Team&lt;/B&gt; - the entire team did some great work: 
&lt;UL&gt;
&lt;LI&gt;Saad, &lt;A href="http://blogs.iis.net/bdela/" target=_blank mce_href="http://blogs.iis.net/bdela/"&gt;Brian&lt;/A&gt;, &lt;A href="http://blogs.iis.net/rlucero/" target=_blank mce_href="http://blogs.iis.net/rlucero/"&gt;Bob&lt;/A&gt;, Tim, Andrew, Nitasha, Richard, &lt;A href="http://blogs.msdn.com/carlosag/" target=_blank mce_href="http://blogs.msdn.com/carlosag/"&gt;Carlos&lt;/A&gt;, Madhur, &lt;A href="http://blogs.iis.net/nazim/" target=_blank mce_href="http://blogs.iis.net/nazim/"&gt;Nazim&lt;/A&gt;, Ivan, Crystal, Faith, Diana, Pete, Rich, Raul&lt;/LI&gt;
&lt;LI&gt;That being said, there are two people that I'd like to call our for their additional assistance: 
&lt;UL&gt;
&lt;LI&gt;&lt;B&gt;Saad Ladki&lt;/B&gt; - Saad was the Program Manager for the Database Manager. I have to give credit where it's due and say that my job would have been a lot harder if Saad hadn't done the initial research for some of my code samples.&lt;/LI&gt;
&lt;LI&gt;&lt;B&gt;&lt;A href="http://blogs.iis.net/bdela/" mce_href="http://blogs.iis.net/bdela/"&gt;Brian Delahunty&lt;/A&gt;&lt;/B&gt; - Brian was my principal contact on the feature team whenever I needed help or backed myself into a corner. Brian was very accommodating when I suggested changes, and he always had great explanations when something needed clarification.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;B&gt;&lt;A href="http://www.microsoft.com/mspress/" mce_href="http://www.microsoft.com/mspress/"&gt;Microsoft Press&lt;/A&gt;&lt;/B&gt; - I know that it seems like a gratuitous plug, but I spent dozens of hours going through my "&lt;A href="http://www.microsoft.com/mspress/books/2520.aspx" mce_href="http://www.microsoft.com/mspress/books/2520.aspx"&gt;Microsoft® OLE DB 2.0 Programmer's Reference and Data Access SDK&lt;/A&gt;" book looking up the various parts of the OLEDB schema. That book was simply indispensable while I was writing my API samples.&lt;/LI&gt;&lt;/UL&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9899083" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category></item><item><title>eWeek Reviews for IIS 7.5 and FTP 7.5</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/09/23/eweek-reviews-for-iis-7-5-and-ftp-7-5.aspx</link><pubDate>Thu, 24 Sep 2009 01:45:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9898705</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9898705.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9898705</wfw:commentRss><description>&lt;P&gt;One of my coworkers, Vijay Sen, just forwarded the following eWeek review of IIS 7.5 to me:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://www.eweek.com/c/a/Windows/REVIEW-Microsoft-IIS-75-Improves-Management-Deployment-Options-822018/" target=_blank mce_href="http://www.eweek.com/c/a/Windows/REVIEW-Microsoft-IIS-75-Improves-Management-Deployment-Options-822018/"&gt;http://www.eweek.com/c/a/Windows/REVIEW-Microsoft-IIS-75-Improves-Management-Deployment-Options-822018/&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The review was written by &lt;A href="http://www.eweek.com/cp/bio/Jim-Rapoza/" target=_blank mce_href="http://www.eweek.com/cp/bio/Jim-Rapoza/"&gt;Jim Rapoza&lt;/A&gt;, and he said some great things about IIS 7.5, which ships with both Windows Server 2008 R2 and Windows 7 client. But what really made my day was the following things that he said about FTP 7.5:&lt;/P&gt;
&lt;BLOCKQUOTE style="BACKGROUND-COLOR: #eeeeee; COLOR: #000000"&gt;
&lt;P&gt;&lt;EM&gt;Another welcome change in IIS 7.5 is the elevation of FTP as a full-fledged part of the server. In previous versions, setup and management of an FTP server in IIS were done pretty much separately from Web server management. In IIS 7.5, FTP administration is fully integrated into the IIS Management Console.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;I found this to be a very good implementation of FTP, making it possible to quickly set up secure FTP servers and tie them to my Websites. Especially nice was the ability to easily use virtual host names for the FTP sites. All in all, the FTP implementation in IIS 7.5 is one of the best I’ve seen, even when compared with dedicated FTP server products.&lt;/EM&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;It’s great to see all of our hard work being recognized!&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif"&gt;&lt;/P&gt;
&lt;P&gt;My thanks once again to everyone on the FTP and IIS feature teams that helped make this version of the FTP service: &lt;A href="http://blogs.iis.net/jaroslad/" mce_href="http://blogs.iis.net/jaroslad/"&gt;Jaroslav&lt;/A&gt;, Emily, &lt;A href="http://blogs.msdn.com/danielvl/" mce_href="http://blogs.msdn.com/danielvl/"&gt;Daniel&lt;/A&gt;, Umer, &lt;A href="http://blogs.iis.net/suDt/" mce_href="http://blogs.iis.net/suDt/"&gt;Suditi&lt;/A&gt;, Ciprian, Jeong, &lt;A href="http://blogs.iis.net/davcox/" mce_href="http://blogs.iis.net/davcox/"&gt;Dave&lt;/A&gt;, Andrew, &lt;A href="http://blogs.msdn.com/carlosag/" mce_href="http://blogs.msdn.com/carlosag/"&gt;Carlos&lt;/A&gt;, &lt;A href="http://blogs.iis.net/bdela/" mce_href="http://blogs.iis.net/bdela/"&gt;Brian&lt;/A&gt;, &lt;A href="http://blogs.iis.net/wadeh/" mce_href="http://blogs.iis.net/wadeh/"&gt;Wade&lt;/A&gt;, Ulad, &lt;A href="http://blogs.iis.net/nazim/" mce_href="http://blogs.iis.net/nazim/"&gt;Nazim&lt;/A&gt;, Reagan, Claudia, Rick, Tim, &lt;A href="http://blogs.iis.net/TobinTitus/" mce_href="http://blogs.iis.net/TobinTitus/"&gt;Tobin&lt;/A&gt;, Kern, Jenny, Nitasha, Venkat, Vijay. (I hope that I didn't leave anyone out!)&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9898705" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>FTP 7.5 Extensibility and Visual Studio Express Editions</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/09/17/ftp-7-5-extensibility-and-visual-studio-express-editions.aspx</link><pubDate>Thu, 17 Sep 2009 21:19:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9896442</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9896442.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9896442</wfw:commentRss><description>&lt;P&gt;In earlier blog posts I have mentioned that I written the several walkthroughs to help developers get started writing providers for the FTP 7.5 service, all of which available on Microsoft's &lt;A href="http://learn.iis.net/" target=_blank mce_href="http://learn.iis.net/"&gt;learn.iis.net&lt;/A&gt; Web site under the "&lt;A href="http://learn.iis.net/page.aspx/590/developing-for-ftp-75/" target=_blank mce_href="http://learn.iis.net/page.aspx/590/developing-for-ftp-75/"&gt;Developing for FTP 7.5&lt;/A&gt;" section. In each of these walkthroughs I wrote the steps as if you were using Visual Studio 2008.&lt;/P&gt;
&lt;P&gt;Following up on that, I received a great question yesterday from a customer, Paul Dowdle, who wondered if it was possible to write an extensibility provider for the FTP 7.5 service using one of the Visual Studio Express Editions. By way of coincidence, I used to install Visual C# Express Edition on my laptop when I was traveling around the world to speak at events like &lt;A href="http://www.msteched.com/" target=_blank mce_href="http://www.msteched.com/"&gt;TechEd&lt;/A&gt;. I usually did this because the Express Edition took up less hard drive space than a full installation of Visual Studio, and I was only writing code in C# on my laptop.&lt;/P&gt;
&lt;P&gt;To answer Paul's question, the short answer is - yes, you can use Visual Studio Express Editions to develop custom providers for the FTP 7.5 service, with perhaps a few small changes from my walkthroughs.&lt;/P&gt;
&lt;P&gt;For example, if you look at my "&lt;A href="http://learn.iis.net/page.aspx/598/how-to-use-managed-code-c-to-create-a-simple-ftp-authentication-provider/" target=_blank&gt;How to Use Managed Code (C#) to Create a Simple FTP Authentication Provider&lt;/A&gt;" walkthrough, in the section that is titled "Step 1: Set up the Project Environment", there is an optional step 6 for adding a custom build event to register the DLL automatically in the Global Assembly Cache (GAC) on your development computer.&lt;/P&gt;
&lt;P&gt;When I installed Microsoft Visual C# 2008 Express Edition on a new computer, I didn't have the "%VS90COMNTOOLS%" environment variable or the "vsvars32.bat" file, so I had to update the custom build event to the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE&gt;net stop ftpsvc
"%ProgramFiles%\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe" /if "$(TargetPath)"
net start ftpsvc&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Once I made that change, the rest of the walkthrough worked as written.&lt;/P&gt;
&lt;P&gt;So, to reiterate my earlier statement - you can use Visual Studio Express Editions to develop custom providers for the FTP 7.5 service. My thanks to Paul for the great question!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9896442" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>Hiding your FTP Server Type and Preventing Unauthorized Access</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/09/10/hiding-your-ftp-server-type-and-preventing-unauthorized-access.aspx</link><pubDate>Thu, 10 Sep 2009 23:05:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9893855</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9893855.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9893855</wfw:commentRss><description>&lt;P&gt;As evidenced by my &lt;A href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions" target=_blank mce_href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions"&gt;How to Use Managed Code (C#) to Create an FTP Authentication Provider with Dynamic IP Restrictions&lt;/A&gt; walkthrough and my other FTP authentication extensibility walkthroughs, I spend a lot of time trying to find ways to prevent unauthorized access to my FTP server while still allowing valid users to have easy access to their site content. Today's blog discusses several of the ideas that I like to use on my FTP servers.&lt;/P&gt;
&lt;H4&gt;Preventing Unauthorized Access&lt;/H4&gt;
&lt;P&gt;To start things off, I globally disable FTP Basic Authentication on my server and I only use custom authentication providers. Since my FTP users do not have actual accounts on my server or my domain, that helps prevent access to my physical server. It was for this reason that I wrote the authentication provider that I discuss in my &lt;A href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-using-an-xml-database/" target=_blank mce_href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-using-an-xml-database/"&gt;How to Use Managed Code (C#) to Create an FTP Authentication Provider using an XML Database&lt;/A&gt; walkthrough; I can hand out accounts with FTP access for sites that are hosted on my servers, but those accounts are stored in an XML file and not in the security accounts database on my server or my domain.&lt;/P&gt;
&lt;P&gt;But there are other ways to help secure your FTP server. For example, I wrote an earlier blog post about &lt;A href="http://blogs.msdn.com/robert_mcmurray/archive/2008/12/17/ftp-clients-part-3-creating-a-global-listener-ftp-site.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2008/12/17/ftp-clients-part-3-creating-a-global-listener-ftp-site.aspx"&gt;Creating a Global Listener FTP Site&lt;/A&gt;. On my servers I like to create a global listener site and disable all of the built-in authentication methods.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://y7b40a.blu.livefilestore.com/y1po9uDXUnvLNqdZK2dEzQWsLqaXG7inoFz_H881H8J--X-hwo41dWJ3_eHb50XzagqsV_RP9VLJiSFaDgHhAod6i0EICF7SzL2/global%20listener%20site.jpg" target=_blank mce_href="http://y7b40a.blu.livefilestore.com/y1po9uDXUnvLNqdZK2dEzQWsLqaXG7inoFz_H881H8J--X-hwo41dWJ3_eHb50XzagqsV_RP9VLJiSFaDgHhAod6i0EICF7SzL2/global%20listener%20site.jpg"&gt;&lt;IMG border=3 src="http://y7b40a.blu.livefilestore.com/y1po9uDXUnvLNqdZK2dEzQWsLqaXG7inoFz_H881H8J--X-hwo41dWJ3_eHb50XzagqsV_RP9VLJiSFaDgHhAod6i0EICF7SzL2/global%20listener%20site.jpg" width="50%" height="50%" mce_src="http://y7b40a.blu.livefilestore.com/y1po9uDXUnvLNqdZK2dEzQWsLqaXG7inoFz_H881H8J--X-hwo41dWJ3_eHb50XzagqsV_RP9VLJiSFaDgHhAod6i0EICF7SzL2/global%20listener%20site.jpg"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Once that has been completed I add the custom authentication provider from my &lt;A href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions" target=_blank mce_href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions"&gt;How to Use Managed Code (C#) to Create an FTP Authentication Provider with Dynamic IP Restrictions&lt;/A&gt; walkthrough, but I don't create any accounts in the database - I just want to log and block the IP addresses of would-be attackers that are attempting to brute force their way into my server. Configuring the authentication options is accomplished through the &lt;B&gt;FTP Authentication&lt;/B&gt; feature in IIS Manager.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://y7b40a.blu.livefilestore.com/y1pnRv7mOVfWdg6rqBSthl45xDuJRnaqxFGxoknchIvknjoE68WE45N0Zte2euTZx2x-MmrZFCz5qtcASgMgOVM2Q/custom%20authentication.jpg" target=_blank mce_href="http://y7b40a.blu.livefilestore.com/y1pnRv7mOVfWdg6rqBSthl45xDuJRnaqxFGxoknchIvknjoE68WE45N0Zte2euTZx2x-MmrZFCz5qtcASgMgOVM2Q/custom%20authentication.jpg"&gt;&lt;IMG border=3 src="http://y7b40a.blu.livefilestore.com/y1pnRv7mOVfWdg6rqBSthl45xDuJRnaqxFGxoknchIvknjoE68WE45N0Zte2euTZx2x-MmrZFCz5qtcASgMgOVM2Q/custom%20authentication.jpg" width="50%" height="50%" mce_src="http://y7b40a.blu.livefilestore.com/y1pnRv7mOVfWdg6rqBSthl45xDuJRnaqxFGxoknchIvknjoE68WE45N0Zte2euTZx2x-MmrZFCz5qtcASgMgOVM2Q/custom%20authentication.jpg"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Ever since I wrote and deployed my dynamic IP address restriction authentication provider, it has faithfully blocked dozens of would-be attackers.&lt;/P&gt;
&lt;H4&gt;Hiding your FTP Server Type&lt;/H4&gt;
&lt;P&gt;The next approach that I take is "Security through Obscurity", where I hide the easily recognizable signatures that identify my FTP server. The first step is to suppress the default banner and add a custom FTP banner. This is accomplished through the &lt;B&gt;FTP Messages&lt;/B&gt; feature in IIS Manager.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://y7b40a.blu.livefilestore.com/y1pkwDMrU610dFt0Ug2OZmoqQJtBHCvs7dpjxkg-5UGYdkKDuoaQ0RUwwriheMaJfsjw37mKU0-86yebF1hhmCNbA/messages.jpg" target=_blank mce_href="http://y7b40a.blu.livefilestore.com/y1pkwDMrU610dFt0Ug2OZmoqQJtBHCvs7dpjxkg-5UGYdkKDuoaQ0RUwwriheMaJfsjw37mKU0-86yebF1hhmCNbA/messages.jpg"&gt;&lt;IMG border=3 src="http://y7b40a.blu.livefilestore.com/y1pkwDMrU610dFt0Ug2OZmoqQJtBHCvs7dpjxkg-5UGYdkKDuoaQ0RUwwriheMaJfsjw37mKU0-86yebF1hhmCNbA/messages.jpg" width="50%" height="50%" mce_src="http://y7b40a.blu.livefilestore.com/y1pkwDMrU610dFt0Ug2OZmoqQJtBHCvs7dpjxkg-5UGYdkKDuoaQ0RUwwriheMaJfsjw37mKU0-86yebF1hhmCNbA/messages.jpg"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Next, switch your directory listing style to Unix format. All FTP clients that I have tested understand this format, but it hides the fact that your server can do MS-DOS listings. Configuring your directory listing style is accomplished through the &lt;B&gt;FTP Directory Browsing &lt;/B&gt;feature in IIS Manager.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://y7b40a.blu.livefilestore.com/y1pF7d1kpsjM9VBLgUc2GuWw_Ukko9FUQU_JtdKrKgDFaiD-tTp3UoVfgsyNfltgAADqmM-m7JjlkL64edKdU09iQ/directory%20browsing.jpg" target=_blank mce_href="http://y7b40a.blu.livefilestore.com/y1pF7d1kpsjM9VBLgUc2GuWw_Ukko9FUQU_JtdKrKgDFaiD-tTp3UoVfgsyNfltgAADqmM-m7JjlkL64edKdU09iQ/directory%20browsing.jpg"&gt;&lt;IMG border=3 src="http://y7b40a.blu.livefilestore.com/y1pF7d1kpsjM9VBLgUc2GuWw_Ukko9FUQU_JtdKrKgDFaiD-tTp3UoVfgsyNfltgAADqmM-m7JjlkL64edKdU09iQ/directory%20browsing.jpg" width="50%" height="50%" mce_src="http://y7b40a.blu.livefilestore.com/y1pF7d1kpsjM9VBLgUc2GuWw_Ukko9FUQU_JtdKrKgDFaiD-tTp3UoVfgsyNfltgAADqmM-m7JjlkL64edKdU09iQ/directory%20browsing.jpg"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;After implementing these changes, it might now resemble the following example FTP session when you log in with FTP:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;TABLE style="BACKGROUND-COLOR: #000000; COLOR: #ffffff"&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;PRE&gt;CMD&amp;gt;ftp localhost

Connected to DESKTOP.domain.local.
220 Welcome to my server!
User (DESKTOP.domain.local:(none)): foobar
331 Password required for foobar.
Password:
230 User logged in.
ftp&amp;gt; dir
200 EPRT command successful.
125 Data connection already open; Transfer starting.
drwxrwxrwx   1 owner    group               0 Jan 22 11:45 App_Code
drwxrwxrwx   1 owner    group               0 Sep 10 10:32 App_Data
drwxrwxrwx   1 owner    group               0 Aug 17 13:35 bin
-rwxrwxrwx   1 owner    group             689 May  8  2008 iisstart.htm
-rwxrwxrwx   1 owner    group            2714 Jan 22 11:54 web.config
-rwxrwxrwx   1 owner    group          184946 May  8  2008 welcome.png
226 Transfer complete.
ftp: 418 bytes received in 0.02Seconds 83.00Kbytes/sec.
ftp&amp;gt; bye
221 Goodbye.

CMD&amp;gt;&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The last step that I take is to block the FTP infrastructure commands &lt;B&gt;SYST&lt;/B&gt; and &lt;B&gt;FEAT&lt;/B&gt; on the global listener FTP site. These commands respectively display your operating system and list of FTP features. Blocking these commands helps to further obfuscate your server type from attackers.&lt;/P&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: It is important that you block these two commands only on your global listener FTP site and not on your other FTP sites, because some FTP clients may depend on these commands in order to interact properly with your other FTP sites. That said, if you are working with a small set of FTP clients and none of them seem to have a problem with suppressing the &lt;B&gt;SYST&lt;/B&gt; command, you could probably suppress that globally. Most clients that I have tested seem to have no problem with that, especially if you are using Unix directory listings.&lt;/P&gt;
&lt;P&gt;In any event, you block the &lt;B&gt;SYST&lt;/B&gt; and &lt;B&gt;FEAT&lt;/B&gt; commands through the &lt;B&gt;FTP Request Filtering &lt;/B&gt;feature in IIS Manager.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://y7b40a.blu.livefilestore.com/y1pyRKiOzDCI1bIKmYbXQPEMbdt80pRsnwJ-mw83yyp9UUtFP3Li67R3egteQT6MDhD1JCF8ofHxKt8IWLZ0ZruBA/request%20filtering.jpg" target=_blank mce_href="http://y7b40a.blu.livefilestore.com/y1pyRKiOzDCI1bIKmYbXQPEMbdt80pRsnwJ-mw83yyp9UUtFP3Li67R3egteQT6MDhD1JCF8ofHxKt8IWLZ0ZruBA/request%20filtering.jpg"&gt;&lt;IMG border=3 src="http://y7b40a.blu.livefilestore.com/y1pyRKiOzDCI1bIKmYbXQPEMbdt80pRsnwJ-mw83yyp9UUtFP3Li67R3egteQT6MDhD1JCF8ofHxKt8IWLZ0ZruBA/request%20filtering.jpg" width="50%" height="50%" mce_src="http://y7b40a.blu.livefilestore.com/y1pyRKiOzDCI1bIKmYbXQPEMbdt80pRsnwJ-mw83yyp9UUtFP3Li67R3egteQT6MDhD1JCF8ofHxKt8IWLZ0ZruBA/request%20filtering.jpg"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now when you log in with FTP and attempt to use the &lt;B&gt;SYST&lt;/B&gt; and &lt;B&gt;FEAT&lt;/B&gt; commands, it should look like the following example FTP session:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;TABLE style="BACKGROUND-COLOR: #000000; COLOR: #ffffff" id=table1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;PRE&gt;CMD&amp;gt;ftp localhost

Connected to DESKTOP.domain.local.
220 Welcome to my server!
User (DESKTOP.domain.local:(none)): foobar
331 Password required for foobar.
Password:
230 User logged in.
ftp&amp;gt; quote SYST
500 'SYST': command not allowed.
ftp&amp;gt; quote FEAT
500 'FEAT': command not allowed.
ftp&amp;gt; bye
221 Goodbye.

CMD&amp;gt;&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/BLOCKQUOTE&gt;
&lt;H4&gt;Summary&lt;/H4&gt;
&lt;P&gt;The steps in this blog are certainly not all-inclusive, and I keep coming up with other ideas as time goes on, but following these steps should help to prevent unauthorized access to your FTP service. All of the normal warnings about using strong passwords, changing your passwords often, using SSL whenever you can, and keeping your hotfixes up-to-date will always apply.&lt;/P&gt;
&lt;P&gt;But that being said, one of the ways that I keep finding new things to block is by simply watching my FTP log files to see what attackers are trying to do. By using some of the techniques that I discuss in my &lt;A href="http://blogs.msdn.com/robert_mcmurray/archive/2009/06/11/using-logparser-with-ftp-7-x-sessions.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2009/06/11/using-logparser-with-ftp-7-x-sessions.aspx"&gt;Using LogParser with FTP 7.x Sessions&lt;/A&gt; blog, you can monitor your FTP log files in order to analyze what would-be attackers are trying to do.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9893855" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>Creating Recursive Directory Listing Files for FTP Clients</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/09/05/creating-recursive-directory-listing-files-for-ftp-clients.aspx</link><pubDate>Sat, 05 Sep 2009 22:39:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9891823</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9891823.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9891823</wfw:commentRss><description>&lt;P&gt;One of the changes that we made in FTP 7.0 and FTP 7.5 was to remove recursive directory listings, which are commonly retrieved by typing "&lt;CODE&gt;ls -lR&lt;/CODE&gt;" from a command-line FTP client, which should send a command like "&lt;CODE&gt;NLST -lR&lt;/CODE&gt;" over FTP to the server. There were several reasons why we decided to remove recursive directory listings, but the main reason was simply to reduce CPU usage on the server; recursive directory listing requests take a lot of resources to fulfill. With that in mind, both FTP 7.0 and FTP 7.5 will ignore the recursive switch on directory requests.&lt;/P&gt;
&lt;P&gt;That being said - quite often it's pretty handy to have a full directory listing from an FTP server. From a client perspective you could probably write script to automate an FTP client to create a recursive listing, but that's a lot of work. Back in my younger days when I ran FTP sites on Unix servers, I would always create two types of list files on my FTP servers for FTP clients to retrieve:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;"&lt;STRONG&gt;ls-lr.txt&lt;/STRONG&gt;" - I would create only one file of this type for my entire FTP server, which would go in the root of my FTP site and it would contain a full recursive listing of all files in my FTP site. &lt;/LI&gt;
&lt;LI&gt;"&lt;STRONG&gt;00index.txt&lt;/STRONG&gt;" - I would create one file of this type in each folder of my FTP site, and each index file would contain a listing of files and their descriptions for that folder. &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Of course, anyone that's been around the Internet since the days before we had HTTP and the world-wide-web should know that I didn't come up with this idea on my own - I learned it from other FTP site administrators. (And anyone who remembers those days should also recognize those two files with a strange sense of nostalgia. 00index.txt files of course led to index.htm files when WWW sites came along later, but that's another story.)&lt;/P&gt;
&lt;P&gt;In any event, as I continued to host FTP sites over the years I have written various scripts to create recursive directory listings, and I thought that one of my scripts might make a good blog post. With that in mind, here is a Windows Script Host file that I created, which I named "ls-lr.vbs", and this script will create a recursive directory listing for an FTP site. I choose the Unix directory listing style for this script since that's the format that I have used for years and the broader number of FTP clients and users should recognize it.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: #000000"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;Option&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Explicit&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;On&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Error&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Resume&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Next
&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;' Declare all variables.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objArguments
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; strBaseFolder
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objFSO
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objFile
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objFolder
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objSubFolder
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; objSubFile
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; lngFolderCount
&lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; lngBaseCount
&lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objArguments = WScript.Arguments

&lt;SPAN style="COLOR: #008000"&gt;' Determine the number of command-line arguments.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Select&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Case&lt;/SPAN&gt; objArguments.Count
  &lt;SPAN style="COLOR: #0000ff"&gt;Case&lt;/SPAN&gt; 0:
    strBaseFolder = WScript.ScriptFullName
    strBaseFolder = Left(strBaseFolder,InStrRev(strBaseFolder,"&lt;SPAN style="COLOR: #8b0000"&gt;\"))
&lt;/SPAN&gt;  &lt;SPAN style="COLOR: #0000ff"&gt;Case&lt;/SPAN&gt; 1:
    strBaseFolder = objArguments(0)
  &lt;SPAN style="COLOR: #0000ff"&gt;Case&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Else&lt;/SPAN&gt;:
    MsgBox "&lt;SPAN style="COLOR: #8b0000"&gt;This script takes a single argument for the&lt;/SPAN&gt;" &amp;amp; vbCrLf &amp;amp; _
      "&lt;SPAN style="COLOR: #8b0000"&gt;starting directory, or specify no arguments&lt;/SPAN&gt;" &amp;amp; vbCrLf &amp;amp; _
      "&lt;SPAN style="COLOR: #8b0000"&gt;to use the current directory.&lt;/SPAN&gt;", vbInformation
&lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Select
&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;' Create a file system object.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objFSO = WScript.CreateObject("&lt;SPAN style="COLOR: #8b0000"&gt;Scripting.FileSystemObject&lt;/SPAN&gt;")

&lt;SPAN style="COLOR: #008000"&gt;' Test if the base folder exists.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; Right(strBaseFolder,1) &amp;lt;&amp;gt; "&lt;SPAN style="COLOR: #8b0000"&gt;\" Then strBaseFolder = strBaseFolder &amp;amp; &lt;/SPAN&gt;"\"&lt;SPAN style="COLOR: #8b0000"&gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; objFSO.FolderExists(strBaseFolder) = &lt;SPAN style="COLOR: #0000ff"&gt;False&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    MsgBox "&lt;SPAN style="COLOR: #8b0000"&gt;The specified folder does not exist.&lt;/SPAN&gt;", vbCritical
    WScript.Quit
&lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If
&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;' Open the output file for the directory listing.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objFile = objFSO.CreateTextFile(strBaseFolder &amp;amp; "&lt;SPAN style="COLOR: #8b0000"&gt;ls-lr.txt&lt;/SPAN&gt;")
     
&lt;SPAN style="COLOR: #008000"&gt;' Define the initial values for the folder counters.&lt;/SPAN&gt;
lngFolderCount = 1
lngBaseCount = 0
  
&lt;SPAN style="COLOR: #008000"&gt;' Dimension an array to hold the folder names.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;ReDim&lt;/SPAN&gt; strFolders(1)
  
&lt;SPAN style="COLOR: #008000"&gt;' Store the root folder in the array.&lt;/SPAN&gt;
strFolders(lngFolderCount) = strBaseFolder
    
&lt;SPAN style="COLOR: #008000"&gt;' Loop while we still have folders to process.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;While&lt;/SPAN&gt; lngFolderCount &amp;lt;&amp;gt; lngBaseCount
  &lt;SPAN style="COLOR: #008000"&gt;' Set up a folder object to a base folder.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;Set&lt;/SPAN&gt; objFolder = objFSO.GetFolder(strFolders(lngBaseCount+1))
  
  &lt;SPAN style="COLOR: #008000"&gt;' Output the folder name to the listing file.&lt;/SPAN&gt;
  objFile.WriteLine vbCrLf &amp;amp; _
    Replace(Mid(strFolders(lngBaseCount+1),Len(strBaseFolder)),"&lt;SPAN style="COLOR: #8b0000"&gt;\",&lt;/SPAN&gt;"/"&lt;SPAN style="COLOR: #8b0000"&gt;) &amp;amp; _
&lt;/SPAN&gt;    vbCrLf
  
  &lt;SPAN style="COLOR: #008000"&gt;' Loop through the collection of subfolders for the base folder.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Each&lt;/SPAN&gt; objSubFolder &lt;SPAN style="COLOR: #0000ff"&gt;In&lt;/SPAN&gt; objFolder.SubFolders
    &lt;SPAN style="COLOR: #008000"&gt;' Increment the folder count.&lt;/SPAN&gt;
    lngFolderCount = lngFolderCount + 1
    &lt;SPAN style="COLOR: #008000"&gt;' Increase the array size&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;ReDim&lt;/SPAN&gt; Preserve strFolders(lngFolderCount)
    &lt;SPAN style="COLOR: #008000"&gt;' Store the folder name in the array.&lt;/SPAN&gt;
    strFolders(lngFolderCount) = objSubFolder.Path
    &lt;SPAN style="COLOR: #008000"&gt;' Output the folder to the listing file.&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;Call&lt;/SPAN&gt; WriteEntry(objSubFolder)
  &lt;SPAN style="COLOR: #0000ff"&gt;Next&lt;/SPAN&gt;
  
  &lt;SPAN style="COLOR: #008000"&gt;' Loop through the collection of subfolders for the base folder.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Each&lt;/SPAN&gt; objSubFile &lt;SPAN style="COLOR: #0000ff"&gt;In&lt;/SPAN&gt; objFolder.Files
    &lt;SPAN style="COLOR: #008000"&gt;' Output the file to the listing file.&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;Call&lt;/SPAN&gt; WriteEntry(objSubFile)
  &lt;SPAN style="COLOR: #0000ff"&gt;Next&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #008000"&gt;' Increment the base folder counter.&lt;/SPAN&gt;
  lngBaseCount = lngBaseCount + 1
&lt;SPAN style="COLOR: #0000ff"&gt;Wend
&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Sub&lt;/SPAN&gt; WriteEntry(tmpObject)
  &lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; tmpAttributes
  &lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; tmpSize
  
  &lt;SPAN style="COLOR: #008000"&gt;' Test for a symbolic link.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; (tmpObject.Attributes &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; 1024) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    tmpAttributes = "&lt;SPAN style="COLOR: #8b0000"&gt;lrwxrwxrwx&lt;/SPAN&gt;"
    tmpSize = 0
  &lt;SPAN style="COLOR: #008000"&gt;' Test for a directory.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;ElseIf&lt;/SPAN&gt; (tmpObject.Attributes &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; 16) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    tmpAttributes = "&lt;SPAN style="COLOR: #8b0000"&gt;drwxrwxrwx&lt;/SPAN&gt;"
    tmpSize = 0
  &lt;SPAN style="COLOR: #008000"&gt;' Otherwise - it's a file.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;Else&lt;/SPAN&gt;
    tmpAttributes = "&lt;SPAN style="COLOR: #8b0000"&gt;-rwxrwxrwx&lt;/SPAN&gt;"
    tmpSize = tmpObject.Size
  &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;
  
  &lt;SPAN style="COLOR: #008000"&gt;' Test for a read-only object.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; (tmpObject.Attributes &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; 1) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    tmpAttributes = Replace(tmpAttributes,"&lt;SPAN style="COLOR: #8b0000"&gt;w&lt;/SPAN&gt;","&lt;SPAN style="COLOR: #8b0000"&gt;-&lt;/SPAN&gt;")
  &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;
  
  &lt;SPAN style="COLOR: #008000"&gt;' Write the list entry to the output file.&lt;/SPAN&gt;
  objFile.WriteLine tmpAttributes &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt;   1 owner    group &lt;/SPAN&gt;" &amp;amp; _
    Right(&lt;SPAN style="COLOR: #0000ff"&gt;String&lt;/SPAN&gt;(15,Chr(32)) &amp;amp; &lt;SPAN style="COLOR: #0000ff"&gt;CStr&lt;/SPAN&gt;(tmpSize),15) &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt; &lt;/SPAN&gt;" &amp;amp; FormatDate(tmpObject.DateLastModified) &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt; &lt;/SPAN&gt;" &amp;amp; tmpObject.Name
&lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Sub
&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;Function&lt;/SPAN&gt; FormatDate(tmpDate)
  FormatDate = &lt;SPAN style="COLOR: #0000ff"&gt;CStr&lt;/SPAN&gt;(Year(tmpDate)) &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt;-&lt;/SPAN&gt;" &amp;amp; Right("&lt;SPAN style="COLOR: #8b0000"&gt;00&lt;/SPAN&gt;" &amp;amp; &lt;SPAN style="COLOR: #0000ff"&gt;CStr&lt;/SPAN&gt;(Month(tmpDate)),2) &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt;-&lt;/SPAN&gt;" &amp;amp; Right("&lt;SPAN style="COLOR: #8b0000"&gt;00&lt;/SPAN&gt;" &amp;amp; &lt;SPAN style="COLOR: #0000ff"&gt;CStr&lt;/SPAN&gt;(Day(tmpDate)),2)
&lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;Function&lt;/SPAN&gt;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;To use the script, copy the code into Windows Notepad and save it to your computer as "ls-lr.vbs." If you double-click the script it will use the current folder to create a recursive folder listing, and if you run this script from a command-line it can take a single argument of a folder path, or you can pass no arguments to the script in order to use the current folder. In either case it will create a file named "ls-lr.txt" in the root of the destination folder that contains the recursive directory listing in Unix format.&lt;/P&gt;
&lt;P&gt;For example, the following listing was created from a folder in my music collection on my desktop computer:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE&gt;/

drwxrwxrwx   1 owner    group               0 2009-07-30 Against the Silence
dr-xr-xr-x   1 owner    group               0 2009-07-30 Collective
drwxrwxrwx   1 owner    group               0 2009-07-30 Speakeasy
-rwxrwxrwx   1 owner    group            2741 2009-09-05 ls-lr.txt

/Against the Silence

-rwxrwxrwx   1 owner    group         9386309 2009-07-30 01-Against the Silence.wma
-rwxrwxrwx   1 owner    group         3974684 2009-07-30 02-Side-Stage Syndrome.wma
-rwxrwxrwx   1 owner    group         7539014 2009-07-30 03-The Dash on my Headstone.wma
-rwxrwxrwx   1 owner    group         7244819 2009-07-30 04-Teeth Like Knives.wma
-rwxrwxrwx   1 owner    group         9910687 2009-07-30 05-The Band Played on.wma

/Collective

-r-xr-xr-x   1 owner    group         2767821 2009-03-05 At the Moment.wma
-r-xr-xr-x   1 owner    group         5259473 2009-03-05 Colt . 45.wma
-r-xr-xr-x   1 owner    group         2572687 2009-03-05 El Mariachi.wma
-r-xr-xr-x   1 owner    group         2395577 2009-03-05 Gold and Silver.wma
-r-xr-xr-x   1 owner    group         2269487 2009-03-05 Keep Waiting.wma
-r-xr-xr-x   1 owner    group         2050335 2009-03-05 Nighttown.wma
-r-xr-xr-x   1 owner    group         1458931 2009-03-05 Rise.wma
-r-xr-xr-x   1 owner    group         3140077 2009-03-05 Rivers Underneath.wma
-r-xr-xr-x   1 owner    group         2278489 2009-03-05 Sad Parade.wma
-r-xr-xr-x   1 owner    group         1909249 2009-03-05 The Hungry Wolf.wma
-r-xr-xr-x   1 owner    group         2467613 2009-03-05 Threshold.wma
-r-xr-xr-x   1 owner    group          795501 2009-03-05 Tranewreck.wma
-r-xr-xr-x   1 owner    group          417239 2009-03-05 Zzyzx.wma

/Speakeasy

-rwxrwxrwx   1 owner    group         4004604 2009-03-05 01-Minuteman.wma
-rwxrwxrwx   1 owner    group         6309752 2009-03-05 02-Sundown Motel.wma
-rwxrwxrwx   1 owner    group         5504122 2009-03-05 03-Keep Waiting.wma
-rwxrwxrwx   1 owner    group         2766262 2009-03-05 04-You Know How It Is.wma
-rwxrwxrwx   1 owner    group         7495952 2009-03-05 05-Rivers Underneath.wma
-rwxrwxrwx   1 owner    group         6294888 2009-03-05 06-Gold and Silver.wma
-rwxrwxrwx   1 owner    group         8062882 2009-03-05 07-Freefall.wma
-rwxrwxrwx   1 owner    group         4437286 2009-03-05 08-[Untitled].wma
-rwxrwxrwx   1 owner    group         3355592 2009-03-05 09-St. Eriksplan.wma
-rwxrwxrwx   1 owner    group         4966942 2009-03-05 10-Disquiet.wma
-rwxrwxrwx   1 owner    group         4788302 2009-03-05 11-Fascination Street.wma
-rwxrwxrwx   1 owner    group         7950944 2009-03-05 12-This Love.wma&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;SMALL&gt;(&lt;B&gt;Note/Disclaimer/etc.&lt;/B&gt;: It may or may not be obvious that this listing is for music from the band &lt;A href="http://en.wikipedia.org/wiki/Stavesacre" target=_blank mce_href="http://en.wikipedia.org/wiki/Stavesacre"&gt;Stavesacre&lt;/A&gt;, but just to be clear and avoid any RIAA entanglements - these files aren't actually hosted on any of my FTP sites; I used the script on my desktop computer to create a listing as an example for this blog.)&lt;/SMALL&gt;&lt;/P&gt;
&lt;P&gt;&lt;SMALL&gt;&lt;IMG src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/wink_smile.gif" mce_src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/wink_smile.gif"&gt;&lt;/SMALL&gt;&lt;/P&gt;
&lt;H3&gt;Customizing the Script Output&lt;/H3&gt;
&lt;P&gt;There are several customizations that you can do with this script, each of which has it's own benefits and drawbacks.&lt;/P&gt;
&lt;H4&gt;Adding Directory Sizes&lt;/H4&gt;
&lt;P&gt;It is trivial from a coding perspective to have the code calculate directory sizes since the &lt;I&gt;Folder&lt;/I&gt; object that I use has as &lt;I&gt;Size &lt;/I&gt;property, but it slows down the script exponentially to calculate that. That being said, if you're willing to take the performance hit, you can modify the highlighted section of the &lt;CODE&gt;WriteEntry()&lt;/CODE&gt; function as follows:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: #000000"&gt;  &lt;SPAN style="COLOR: #008000"&gt;' Test for a symbolic link.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; (tmpObject.Attributes &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; 1024) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    tmpAttributes = "&lt;SPAN style="COLOR: #8b0000"&gt;lrwxrwxrwx&lt;/SPAN&gt;"
    tmpSize = 0
  &lt;SPAN style="COLOR: #008000"&gt;' Test for a directory.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;ElseIf&lt;/SPAN&gt; (tmpObject.Attributes &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; 16) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    tmpAttributes = "&lt;SPAN style="COLOR: #8b0000"&gt;drwxrwxrwx&lt;/SPAN&gt;"
    &lt;SPAN style="BACKGROUND-COLOR: #ffff99"&gt;tmpSize = tmpObject.Size&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #008000"&gt;' Otherwise - it's a file.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;Else&lt;/SPAN&gt;
    tmpAttributes = "&lt;SPAN style="COLOR: #8b0000"&gt;-rwxrwxrwx&lt;/SPAN&gt;"
    tmpSize = tmpObject.Size
  &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;This will insert the folder size into the output, but once again it will make the script much slower and take up considerably more CPU time to compute.&lt;/P&gt;
&lt;H4&gt;Uppercase Folder Names&amp;nbsp; and Lowercase File Names&lt;/H4&gt;
&lt;P&gt;Since Windows is a case-insensitive operating system, you can easily choose to display all of your folder names in all uppercase characters and your file names in all lowercase characters without causing any client confusion. This can be accomplished by adding the first highlighted section and modifying the second highlighted section of the &lt;CODE&gt;WriteEntry()&lt;/CODE&gt; function as follows:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffff99; COLOR: #000000"&gt;  &lt;SPAN style="COLOR: #0000ff"&gt;Dim&lt;/SPAN&gt; tmpName
  
  &lt;SPAN style="COLOR: #008000"&gt;' Test for a directory.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt; (tmpObject.Attributes &lt;SPAN style="COLOR: #0000ff"&gt;And&lt;/SPAN&gt; 16) &lt;SPAN style="COLOR: #0000ff"&gt;Then&lt;/SPAN&gt;
    tmpName = UCase(tmpObject.Name)
  &lt;SPAN style="COLOR: #0000ff"&gt;Else&lt;/SPAN&gt;
    tmpName = LCase(tmpObject.Name)
  &lt;SPAN style="COLOR: #0000ff"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;If&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: #000000"&gt;  &lt;SPAN style="COLOR: #008000"&gt;' Write the list entry to the output file.&lt;/SPAN&gt;
  objFile.WriteLine tmpAttributes &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt;   1 owner    group &lt;/SPAN&gt;" &amp;amp; _
    Right(&lt;SPAN style="COLOR: #0000ff"&gt;String&lt;/SPAN&gt;(15,Chr(32)) &amp;amp; &lt;SPAN style="COLOR: #0000ff"&gt;CStr&lt;/SPAN&gt;(tmpSize),15) &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt; &lt;/SPAN&gt;" &amp;amp; FormatDate(tmpObject.DateLastModified) &amp;amp; _
    "&lt;SPAN style="COLOR: #8b0000"&gt; &lt;/SPAN&gt;" &amp;amp; &lt;SPAN style="BACKGROUND-COLOR: #ffff99"&gt;tmpName&lt;/SPAN&gt;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;H3&gt;Parting Thoughts&lt;/H3&gt;
&lt;P&gt;There are other customizations that you can easily make, such as creating a string array to sort the files and folders for each folder listing as a single list rather than listing folders first and files second as the currently script does. But that slows down the script way too much, and I prefer to see folders listed before files anyway. (Which is why I always use &lt;KBD&gt;SET DIRCMD=/OGN&lt;/KBD&gt; for my command prompt sessions as well.)&lt;/P&gt;
&lt;P&gt;Another easy customization would be to change the &lt;CODE&gt;FormatDate()&lt;/CODE&gt; function to change the date format for the output file, which is why I used a function to do my date formatting. For example, you could easily use the &lt;CODE&gt;FormatDate()&lt;/CODE&gt; function as a wrapper for VBScript's built-in &lt;CODE&gt;FormatDateTime()&lt;/CODE&gt; function, and then use any of the &lt;CODE&gt;vbGeneralDate&lt;/CODE&gt;, &lt;CODE&gt;vbLongDate&lt;/CODE&gt;, &lt;CODE&gt;vbShortDate&lt;/CODE&gt;, etc. options to specify the format. You can also use your own customized logic to return the date string, so you don't need to feel limited by my examples.&lt;/P&gt;
&lt;P&gt;Another useful customization would be to compute the actual size for the resulting "ls-lr.txt" file and modify the output file to contain the correct file size. Currently the script in this blog adds an entry to the listing for the "ls-lr.txt" file, but that contains the temporary size of the output file as the script is running so it will seldom be accurate. (I usually run my script and update the "ls-lr.txt" file manually, but in some versions of this script I have had it ignore the "ls-lr.txt" file and remove it from the output listings.)&lt;/P&gt;
&lt;P&gt;In closing, this script may be doing more than it might actually need to do by way of checking for symbolic links and read-only attributes, which our FTP service doesn’t actually do, but it was very easy to add that code and it runs just as fast either way.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9891823" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/Scripting/default.aspx">Scripting</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>The End of Metabase Corruption (R.I.P.)</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/08/22/the-end-of-metabase-corruption-r-i-p.aspx</link><pubDate>Sat, 22 Aug 2009 10:28:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9880070</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9880070.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9880070</wfw:commentRss><description>&lt;P&gt;Even though IIS 7 with its new XML-based configuration settings has been around for a while, I was going through some old directories on one of my computers the other day and I stumbled across an image that I had labeled "Metabase Corruption." I have kept that image around for the past decade or so because it's one of the few actual examples of metabase corruption that I have actually verified, although there have definitely been more cases than just the ones that I have seen. But I thought that it might make for a good blog entry to explain the origins of the term "Metabase Corruption" and what that means to you if you're using a version of IIS that is earlier than IIS 7.&lt;/P&gt;
&lt;H4&gt;A brief history of the IIS metabase and metabase editors&lt;/H4&gt;
&lt;P&gt;Many years ago I was working in the Microsoft Product Support Services (PSS) group, where I provided support for enterprise-level customers that were using IIS versions 1.0 through 6.0. The early versions of IIS, (versions 1.0 through 3.0), stored all of their settings in the registry. This was fine for that period of time, but it didn't scale well and for several other reasons it was cumbersome to deal with. Just to bring back some nightmares, here's an image of the IIS 3.0 manager:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/photos/robert_mcmurray/images/9530997/original.aspx" target=_blank mce_href="http://blogs.msdn.com/photos/robert_mcmurray/images/9530997/original.aspx"&gt;&lt;IMG alt="IIS 3.0 Manager" src="http://blogs.msdn.com/photos/robert_mcmurray/images/9530997/500x265.aspx" mce_src="http://blogs.msdn.com/photos/robert_mcmurray/images/9530997/500x265.aspx"&gt;&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;When IIS 4.0 was released, it introduced the "metabase," which was a proprietary database that stored all of the metadata for IIS, and all of that information was physically stored in a file that was named metabase.bin. The metabase scaled considerably better than the registry, but it introduced several other problems. The biggest problem was that the metabase was something of a "black box" for end users - all of your settings were stored in the metabase, and the only way to get to those settings was through the IIS Manager or by using unsupported command-line tools. The next big problem was that the metabase wasn't portable - you couldn't copy the metabase from one computer to another. (If you tried you would quickly realize that you just killed IIS.) When the IIS 4.0 Resource Kit was released it introduced MetaEdit 1.0, which was an editor for the metabase that looked like RegEdit, but it crashed so often that it really wasn't useful.&lt;/P&gt;
&lt;P&gt;During the Windows 2000 timeframe I took over the MetaEdit code base and I created MetaEdit 2.0, which was released with the Windows 2000 Resource Kit. I fixed all of the stability problems from MetaEdit 1.0 and I added export/import functionality so that users could copy sections of the metabase between servers. Actually, I wrote MetaEdit before Microsoft stopped adding Easter Eggs to software, so there's a small Easter Egg in the Help::About for MetaEdit that you can find by holding down Shift-Ctrl-Alt and double-clicking the icon - you first see a photo of Bill Gates and then you see a photo of me from the days when I had longer hair...&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;IMG src="http://y7b40a.blu.livefilestore.com/y1pf2cuhrhPrw3keEIlIgRIt4p_cjoEEgioFb5VwJeO8yOEgsWtnxcH857pJSvkPkAdHwDwELFI4VeymXmrXof8BQ/MetaEdit22.jpg" mce_src="http://y7b40a.blu.livefilestore.com/y1pf2cuhrhPrw3keEIlIgRIt4p_cjoEEgioFb5VwJeO8yOEgsWtnxcH857pJSvkPkAdHwDwELFI4VeymXmrXof8BQ/MetaEdit22.jpg"&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I released a couple of patches to MetaEdit over the years, and the last version that I released is version 2.2, which you can download through the following URL:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://support.microsoft.com/kb/301386" target=_blank mce_href="http://support.microsoft.com/kb/301386"&gt;How To Install MetaEdit 2.2 on Windows NT 4.0 or Windows 2000&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;When Windows Server 2003 was in development, XML had risen within the development community as a great way to store your data, so the IIS team decided to switch from the metabase.bin format to metabase.xml. This allowed for editing the metabase using any XML editor, even Windows Notepad, and IIS added native export and import functionality, so you could migrate settings from one computer to another by using IIS Manager.&lt;/P&gt;
&lt;P&gt;It was sometime during the Windows 2003 timeframe that I was working on MetaEdit 3.0 when I heard that someone else was working on Metabase Explorer, and the early versions of Metabase Explorer that I was able to test were really cool, so I ceased work on MetaEdit in favor of Metabase Explorer, which was eventually released with the IIS 6.0 Resource Kit Tools. You can download the IIS 6.0 Resource Kit Tools through the following URL:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499" target=_blank mce_href="http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499"&gt;Internet Information Services (IIS) 6.0 Resource Kit Tools&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: MetaEdit should only be used on Windows NT 4.0 with IIS 4.0 or Windows 2000 with IIS 5.0. For IIS 6.0 you should only use Metabase Explorer. (And yes - you can blame me for the totally useless dialog that you get when you try to use MetaEdit with IIS 6.0. Sorry about that. &lt;IMG src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif" mce_src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif"&gt;)&lt;/P&gt;
&lt;H4&gt;What's all this about metabase corruption?&lt;/H4&gt;
&lt;P&gt;It was shortly after the release of IIS 4.0 that I first heard the term "Metabase Corruption" being used. People were using the term "Metabase Corruption" as a catch-all phrase for "I don't what's broken but reinstalling IIS fixed the problem so it must have been a problem with the metabase." Everyone's general opinion seemed to be that if you couldn't find the problem and you can't look at the metabase, then it must be corruption in the metabase if you can't resolve the issue without reinstalling IIS. To me, this seemed like a cop-out for good troubleshooting, and since I was a technical lead in PSS around the time that IIS 5.0 was being released I would generally refuse to accept anyone's claim when they said that they had a customer with "Metabase Corruption." Instead, I would require them to troubleshoot the problem further, and in nearly 100% of those situations the problem was &lt;B&gt;&lt;I&gt;not&lt;/I&gt;&lt;/B&gt; caused by the metabase but by some other problem that could be rectified &lt;I&gt;without&lt;/I&gt; reinstalling IIS.&lt;/P&gt;
&lt;P&gt;Here's a case in point - several years ago I worked on an issue that some of the folks that supported SharePoint Portal Server 2001 on IIS 5.0 were seeing. They first described the problem to me as "IIS was losing settings." What they were actually seeing was that they would make changes in IIS Manager and apply them, but when they would close and reopen IIS Manager they saw that all their settings were gone. They claimed that this was "Metabase Corruption," and because of this they would often make the customer reinstall IIS completely. Needless to say I made them quite angry by stating that "Metabase Corruption" is almost never actual corruption, and that they were wasting the customer's time by making them reinstall IIS. Even though reinstalling IIS made the problem go away, it didn't actually fix the problem and it only hid the actual cause at the customer's expense.&lt;/P&gt;
&lt;P&gt;Since I knew a little more than they did about how the metabase worked, I could theorize reasons why that behavior might occur and I asked them to contact me when they had a customer with this problem again. The next time they saw this problem I asked them to follow these steps:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Stop IIS by using "iisreset /stop" from a command-line&lt;/LI&gt;
&lt;LI&gt;Start just the IISADMIN service by using "net start IISADMIN" from a command-line&lt;/LI&gt;
&lt;LI&gt;Open the IIS Manager, make their changes, then close the IIS Manager&lt;/LI&gt;
&lt;LI&gt;Restart IIS by using "iisreset /restart" from a command-line&lt;/LI&gt;
&lt;LI&gt;Open IIS Manager and verify their settings&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;In 100% of the cases this enabled them to work around the issue, but that was only part of the solution. I theorized that something was causing IIS to crash before the metabase changes were flushed to disk, but the Windows service recovery settings were instructing the server to restart IIS whenever it crashed so it seemed to the customer like nothing had crashed at all. With this in mind, I had them check the Windows System Log in the Event Viewer, and it listed several errors indicating that the IISADMIN service had terminated unexpectedly, which confirmed my suspicions. After some additional troubleshooting I discovered that SharePoint Portal Server 2001 was setting several illegal values in the metabase, and sooner or later that would cause IIS 5.0 to crash. (SharePoint has long since fixed that problem.)&lt;/P&gt;
&lt;H4&gt;Does metabase corruption actually happen? If so, what can I do about it?&lt;/H4&gt;
&lt;P&gt;Sadly - yes it does. It doesn't happen as often as some people seem to think that it does, but it is certainly possible. The metabase is simply a file, and file corruption can occur for any number of reasons. In the early days of IIS 5.0 on the Windows 2000 beta I discovered (quite by accident) how I could overwrite one specific value in the metabase and crash IIS so badly that IIS couldn't even be uninstalled. (Don't worry - that problem has long since been fixed.) I have also seen metabase corruption due to hardware failures, like a drive controller that is going bad and the problem is manifesting itself slowly over time.&lt;/P&gt;
&lt;P&gt;That being said, there are several methods to check if you actually have metabase corruption:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The first and easiest thing to check is - does IIS start and all of your web sites seem to work?&lt;/LI&gt;
&lt;LI&gt;Can you open up the IIS Manager and see all of your sites?&lt;/LI&gt;
&lt;LI&gt;Can you enumerate the metabase without errors using MetaEdit, Metabase Explorer, or "adsutil.vbs enum_all w3svc" from a command line?&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;If the answer to any or all of these questions is yes - then chances are good that you do not have metabase corruption, and whatever problem you have is due to misconfiguration or some other issue.&lt;/P&gt;
&lt;P&gt;The best way that I can think of to prevent metabase corruption is to avoid using a variety of tools to constantly read and write to the metabase outside of normal IIS operations. I have written scripts that perform literally thousands of metabase operations under a nominal load in a lab environment for testing purposes and never had a problem, but a server under heavy load with thousands of read/write operations per minute could theoretically run into problems if left to run like that for an indefinite length of time. But theoretically the same may be true for any file under heavy read/write load for an indefinite length of time.&lt;/P&gt;
&lt;H4&gt;Protecting against metabase corruption&lt;/H4&gt;
&lt;P&gt;The best thing that you can do to prevent against metabase corruption is &lt;B&gt;&lt;I&gt;backup the metabase regularly&lt;/I&gt;&lt;/B&gt;!!! I cannot count the number of times where having a good backup of the metabase saved me from a myriad of issues. Fortunately, there are several KB articles that describe backing up the metabase:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://support.microsoft.com/kb/300672" target=_blank mce_href="http://support.microsoft.com/kb/300672"&gt;KB 300672: How to create a metabase backup in IIS 5&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://support.microsoft.com/kb/324277" target=_blank mce_href="http://support.microsoft.com/kb/324277"&gt;KB 324277: How To Create a Metabase Backup by Using IIS 6.0 in Windows Server 2003&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://support.microsoft.com/kb/234429" target=_blank mce_href="http://support.microsoft.com/kb/234429"&gt;KB 234429: How to manually restore the metabase when no proper backup exists or when the MMC does not start&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://support.microsoft.com/kb/302888" mce_href="http://support.microsoft.com/kb/302888"&gt;KB 302888: How To Schedule Metabase Backups Using WSH&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;The last KB article in the above list is based on a script that I wrote for one of my IIS 5.0 on Windows 2000 servers many years ago that I continue to use on any servers that run IIS 6.0 on Windows Server 2003.&lt;/P&gt;
&lt;H4&gt;Does IIS 7 have metabase corruption?&lt;/H4&gt;
&lt;P&gt;The quick answer to this is "no", but that answer is a little misleading since IIS 7 doesn't have a metabase!&lt;/P&gt;
&lt;P&gt;In IIS 7 we rolled out a brand new XML-based configuration store, and the bulk of your IIS settings are stored in a file named applicationHost.config, which greatly resembles the web.config configuration paradigm that was established with the .NET framework. That being said, applicationHost.config is just another file, and even though its syntax is much easier to understand and manually edit, any file can theoretically become corrupted, so you should always make sure that you backup your applicationHost.config file as well.&lt;/P&gt;
&lt;P&gt;With this in mind I wrote the following blog post some time ago where I discuss creating a script to automate backing up your IIS 7 configuration settings:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/robert_mcmurray/archive/2008/03/08/automating-iis-7-backups.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2008/03/08/automating-iis-7-backups.aspx"&gt;Automating IIS 7 Backups&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;(&lt;B&gt;Note&lt;/B&gt;: I also wrote &lt;A href="http://blogs.msdn.com/robert_mcmurray/archive/2009/07/27/a-little-scripting-saved-my-day.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2009/07/27/a-little-scripting-saved-my-day.aspx"&gt;another blog post&lt;/A&gt; recently about how the script in that blog post saved my day when something did get corrupted on one of my servers.)&lt;/P&gt;
&lt;H4&gt;Parting thoughts&lt;/H4&gt;
&lt;P&gt;I started out this blog post by describing an image that I found on one of my computers where actual metabase corruption had occurred, so I thought that a nice way to close out this blog post would be to show you the actual image:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;IMG src="http://y7b40a.blu.livefilestore.com/y1p8jpY-8nxRp2_6XkjOvo1sOd0SsYtpeL55cAbq3JFGWjWlalXTY9KxMYMDepiitDuHIn0fMwX9Q3VPbOxwC2oLAleYsFd8BAZ/Metabase%20Corruption.jpg" mce_src="http://y7b40a.blu.livefilestore.com/y1p8jpY-8nxRp2_6XkjOvo1sOd0SsYtpeL55cAbq3JFGWjWlalXTY9KxMYMDepiitDuHIn0fMwX9Q3VPbOxwC2oLAleYsFd8BAZ/Metabase%20Corruption.jpg"&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;It's been almost ten years now, so I don't remember the root cause anymore, but I remember what it looked like. I had a mixture of sites that were working and others that were not, and when I opened the metabase in MetaEdit I noticed that scattered throughout the metabase were hundreds of values that shouldn't be there or values that did not make sense. MetaEdit was trying its best to show all of the data, but you'll notice in the highlighted areas of the image that several of the values had bad data in them.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9880070" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category></item><item><title>Merging FTP Extensibility Walkthroughs - Part 2</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/08/19/merging-ftp-extensibility-walkthroughs-part-2.aspx</link><pubDate>Thu, 20 Aug 2009 06:36:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9876281</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9876281.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9876281</wfw:commentRss><description>&lt;P&gt;I had not intended to do a series on this subject when I wrote my original &lt;A href="http://blogs.msdn.com/robert_mcmurray/archive/2009/07/23/merging-ftp-extensibility-walkthroughs.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2009/07/23/merging-ftp-extensibility-walkthroughs.aspx"&gt;Merging FTP Extensibility Walkthroughs&lt;/A&gt; blog post, but I came up with a scenario that I felt was worth sharing. I recently posted the following walkthrough on the &lt;A href="http://learn.iis.net/" target=_blank mce_href="http://learn.iis.net/"&gt;learn.iis.net&lt;/A&gt; web site:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions/" target=_blank mce_href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions/"&gt;How to Use Managed Code (C#) to Create an FTP Authentication Provider with Dynamic IP Restrictions&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;We have had many customer requests for a dynamic IP restrictions provider for the FTP server, and I wanted to get that out to customers as soon as I could. That being said, like several of my extensibility walkthroughs in the past, I wrote and tested the provider in that walkthrough on one of the servers that I manage. To show how effective it was, within the first couple of hours the provider had caught and blocked its first script kiddie who was attempting a brute force attack on my FTP server. Over the next few days the provider caught its next hacker, and over the past few weeks it has continued to do so.&lt;/P&gt;
&lt;P&gt;That being said, I thought that it might be nice to know when an IP address was blocked, and I had already written the following walkthrough:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://learn.iis.net/page.aspx/632/how-to-use-managed-code-c-to-create-an-ftp-provider-that-sends-an-email-when-files-are-uploaded/" target=_blank mce_href="http://learn.iis.net/page.aspx/632/how-to-use-managed-code-c-to-create-an-ftp-provider-that-sends-an-email-when-files-are-uploaded/"&gt;How to Use Managed Code (C#) to Create an FTP Provider that Sends an Email when Files are Uploaded&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;With that in mind, merging the two walkthroughs seemed like a simple thing to do.&lt;/P&gt;
&lt;P&gt;Before continuing I need to reiterate the notice that I added to the dynamic IP restrictions walkthrough:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;B&gt;IMPORTANT NOTE&lt;/B&gt;: The latest version of the FTP 7.5 service &lt;U&gt;&lt;B&gt;must be&lt;/B&gt;&lt;/U&gt; installed in order to use the provider in this walkthrough. A version FTP 7.5 was released on August 3, 2009 that addressed an issue where the local and remote IP addresses in the &lt;B&gt;IFtpLogProvider.Log()&lt;/B&gt; method were incorrect. Because of this, using an earlier version of the FTP service will prevent this provider from working.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;With that warning out of the way, here are the steps that you need to follow in order to merge the two walkthroughs:&lt;/P&gt;
&lt;H5&gt;Step 1 - Create the project&lt;/H5&gt;
&lt;P&gt;Create a new C# project following all of the steps in the &lt;A href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions/" target=_blank mce_href="http://learn.iis.net/page.aspx/673/how-to-use-managed-code-c-to-create-an-ftp-authentication-provider-with-dynamic-ip-restrictions/"&gt;How to Use Managed Code (C#) to Create an FTP Authentication Provider with Dynamic IP Restrictions&lt;/A&gt; walkthrough.&lt;/P&gt;
&lt;H5&gt;Step 2 - Merge global variables&lt;/H5&gt;
&lt;P&gt;In this step you need to merge the global variables from the two walkthroughs. In my provider this looked like the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #008000"&gt;// Define the default values - these are only&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// used if the configuration settings are not set.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;const&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; defaultLogonAttempts = 5;
&lt;SPAN style="COLOR: #0000ff"&gt;const&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; defaultFloodSeconds = 30;
&lt;SPAN style="COLOR: #0000ff"&gt;const&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; defaultSmtpPort = 25;

&lt;SPAN style="COLOR: #008000"&gt;// Define a connection string with no default.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; _connectionString;

&lt;SPAN style="COLOR: #008000"&gt;// Initialize the private variables with the default values.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; _logonAttempts = defaultLogonAttempts;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; _floodSeconds = defaultFloodSeconds;

&lt;SPAN style="COLOR: #008000"&gt;// Flag the application as uninitialized.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;bool&lt;/SPAN&gt; _initialized = &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;;

&lt;SPAN style="COLOR: #008000"&gt;// Define a list that will contain the list of flagged sessions.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; List&amp;lt;&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;&amp;gt; _flaggedSessions;

&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; _smtpServerName;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; _smtpFromAddress;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; _smtpToAddress;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; _smtpServerPort;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;H5&gt;Step 3 - Merge the Initialize() methods&lt;/H5&gt;
&lt;P&gt;In this step you need to merge the Initialize() methods from the two walkthroughs so that all of the settings are retrieved from the IIS configuration file when the provider is loaded by the FTP service. In my provider this looked like the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #008000"&gt;// Initialize the provider.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;protected&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;override&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;void&lt;/SPAN&gt; Initialize(StringDictionary config)
{
    &lt;SPAN style="COLOR: #008000"&gt;// Test if the application has already been initialized.&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (_initialized == &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;)
    {
        &lt;SPAN style="COLOR: #008000"&gt;// Create the flagged sessions list.&lt;/SPAN&gt;
        _flaggedSessions = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; List&amp;lt;&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;&amp;gt;();
        
        &lt;SPAN style="COLOR: #008000"&gt;// Retrieve the connection string for the database connection.&lt;/SPAN&gt;
        _connectionString = config[&lt;SPAN style="COLOR: #800000"&gt;"connectionString"&lt;/SPAN&gt;];
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.IsNullOrEmpty(_connectionString))
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Raise an exception if the connection string is missing or empty.&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ArgumentException(
                &lt;SPAN style="COLOR: #800000"&gt;"Missing connectionString value in configuration."&lt;/SPAN&gt;);
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;else&lt;/SPAN&gt;
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Determine whether the database is a Microsoft Access database.&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (_connectionString.Contains(&lt;SPAN style="COLOR: #800000"&gt;"Microsoft.Jet"&lt;/SPAN&gt;))
            {
                &lt;SPAN style="COLOR: #008000"&gt;// Throw an exception if the database is a Microsoft Access database.&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ProviderException(&lt;SPAN style="COLOR: #800000"&gt;"Microsoft Access databases are not supported."&lt;/SPAN&gt;);
            }
        }
        
        &lt;SPAN style="COLOR: #008000"&gt;// Retrieve the number of failures before an IP&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #008000"&gt;// address is locked out - or use the default value.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt;.TryParse(config[&lt;SPAN style="COLOR: #800000"&gt;"logonAttempts"&lt;/SPAN&gt;], &lt;SPAN style="COLOR: #0000ff"&gt;out&lt;/SPAN&gt; _logonAttempts) == &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;)
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Set to the default if the number of logon attempts is not valid.&lt;/SPAN&gt;
            _logonAttempts = defaultLogonAttempts;
        }
        
        &lt;SPAN style="COLOR: #008000"&gt;// Retrieve the number of seconds for flood&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #008000"&gt;// prevention - or use the default value.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt;.TryParse(config[&lt;SPAN style="COLOR: #800000"&gt;"floodSeconds"&lt;/SPAN&gt;], &lt;SPAN style="COLOR: #0000ff"&gt;out&lt;/SPAN&gt; _floodSeconds) == &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;)
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Set to the default if the number of logon attempts is not valid.&lt;/SPAN&gt;
            _floodSeconds = defaultFloodSeconds;
        }
        
        &lt;SPAN style="COLOR: #008000"&gt;// Test if the number is a positive integer and less than 10 minutes.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; ((_floodSeconds &amp;lt;= 0) || (_floodSeconds &amp;gt; 600))
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Set to the default if the number of logon attempts is not valid.&lt;/SPAN&gt;
            _floodSeconds = defaultFloodSeconds;
        }
        
        &lt;SPAN style="COLOR: #008000"&gt;// Retrieve the email settings from configuration.&lt;/SPAN&gt;
        _smtpServerName = config[&lt;SPAN style="COLOR: #800000"&gt;"smtpServerName"&lt;/SPAN&gt;];
        _smtpFromAddress = config[&lt;SPAN style="COLOR: #800000"&gt;"smtpFromAddress"&lt;/SPAN&gt;];
        _smtpToAddress = config[&lt;SPAN style="COLOR: #800000"&gt;"smtpToAddress"&lt;/SPAN&gt;];
        
        &lt;SPAN style="COLOR: #008000"&gt;// Detect and handle any mis-configured settings.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (!&lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt;.TryParse(config[&lt;SPAN style="COLOR: #800000"&gt;"smtpServerPort"&lt;/SPAN&gt;], &lt;SPAN style="COLOR: #0000ff"&gt;out&lt;/SPAN&gt; _smtpServerPort))
        {
            _smtpServerPort = defaultSmtpPort;
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.IsNullOrEmpty(_smtpServerName))
        {
            &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ArgumentException(
                &lt;SPAN style="COLOR: #800000"&gt;"Missing smtpServerName value in configuration."&lt;/SPAN&gt;);
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.IsNullOrEmpty(_smtpFromAddress))
        {
            &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ArgumentException(
                &lt;SPAN style="COLOR: #800000"&gt;"Missing smtpFromAddress value in configuration."&lt;/SPAN&gt;);
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.IsNullOrEmpty(_smtpToAddress))
        {
            &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ArgumentException(
                &lt;SPAN style="COLOR: #800000"&gt;"Missing smtpToAddress value in configuration."&lt;/SPAN&gt;);
        }
        
        &lt;SPAN style="COLOR: #008000"&gt;// Initial garbage collection.&lt;/SPAN&gt;
        GarbageCollection(&lt;SPAN style="COLOR: #0000ff"&gt;true&lt;/SPAN&gt;);
        
        &lt;SPAN style="COLOR: #008000"&gt;// Flag the provider as initialized.&lt;/SPAN&gt;
        _initialized = &lt;SPAN style="COLOR: #0000ff"&gt;true&lt;/SPAN&gt;;
    }
}&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;H5&gt;Step 4 - Add a SendEmail() method&lt;/H5&gt;
&lt;P&gt;For this step I copied some of my code from the email walkthrough and used it as the foundation for a new SendEmail() method that I added to the provider. In my provider this looked like the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;void&lt;/SPAN&gt; SendEmail(&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; emailSubject, &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; emailMessage)
{
    &lt;SPAN style="COLOR: #008000"&gt;// Create an SMTP message.&lt;/SPAN&gt;
    SmtpClient smtpClient = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; SmtpClient(_smtpServerName, _smtpServerPort);
    MailAddress mailFromAddress = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; MailAddress(_smtpFromAddress);
    MailAddress mailToAddress = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; MailAddress(_smtpToAddress);
    
    &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; (MailMessage mailMessage = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; MailMessage(mailFromAddress, mailToAddress))
    {
        &lt;SPAN style="COLOR: #0000ff"&gt;try&lt;/SPAN&gt;
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Format the SMTP message as UTF8.&lt;/SPAN&gt;
            mailMessage.BodyEncoding = Encoding.UTF8;
            &lt;SPAN style="COLOR: #008000"&gt;// Add the subject.&lt;/SPAN&gt;
            mailMessage.Subject = emailSubject;
            &lt;SPAN style="COLOR: #008000"&gt;// Add the body.&lt;/SPAN&gt;
            mailMessage.Body = emailMessage;
            &lt;SPAN style="COLOR: #008000"&gt;// Send the email message.&lt;/SPAN&gt;
            smtpClient.Send(mailMessage);
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;catch&lt;/SPAN&gt; (SmtpException ex)
        {
            &lt;SPAN style="COLOR: #008000"&gt;// Send an exception message to the debug&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #008000"&gt;// channel if the email fails to send.&lt;/SPAN&gt;
            Debug.WriteLine(ex.Message);
        }
    }
}&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: This uses the settings that you store in your IIS applicationHost.config file and are loaded by the Initialize() method.&lt;/P&gt;
&lt;H5&gt;Step 5 - Add email functionality to the BanAddress() method&lt;/H5&gt;
&lt;P&gt;In this step you add the functionality to send an email whenever an IP address is added to the list of banned IP addresses. In my provider this looked like the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="BACKGROUND-COLOR: #ffffff; COLOR: black"&gt;&lt;SPAN style="COLOR: #008000"&gt;// Mark an IP address as banned.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;void&lt;/SPAN&gt; BanAddress(&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; ipAddress)
{
    &lt;SPAN style="COLOR: #008000"&gt;// Check if the IP address is already banned.&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (IsAddressBanned(ipAddress) == &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;)
    {
        &lt;SPAN style="COLOR: #008000"&gt;// Ban the IP address if it is not already banned.&lt;/SPAN&gt;
        InsertDataIntoTable(&lt;SPAN style="COLOR: #800000"&gt;"[BannedAddresses]"&lt;/SPAN&gt;,
            &lt;SPAN style="COLOR: #800000"&gt;"[IPAddress]"&lt;/SPAN&gt;, &lt;SPAN style="COLOR: #800000"&gt;"'"&lt;/SPAN&gt; + ipAddress + &lt;SPAN style="COLOR: #800000"&gt;"'"&lt;/SPAN&gt;);
        &lt;SPAN style="COLOR: #008000"&gt;// Send an email for the banned address.&lt;/SPAN&gt;
        SendEmail(&lt;SPAN style="COLOR: #800000"&gt;"Banned IP Address"&lt;/SPAN&gt;,
            &lt;SPAN style="COLOR: #800000"&gt;"The IP address "&lt;/SPAN&gt; + ipAddress + &lt;SPAN style="COLOR: #800000"&gt;" was banned."&lt;/SPAN&gt;);
    }
}&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;H5&gt;Step 6 - Methods that are &lt;U&gt;&lt;I&gt;not&lt;/I&gt;&lt;/U&gt; changed&lt;/H5&gt;
&lt;P&gt;I need to point out that there are several methods that require no changes. These methods are listed here for reference:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Dispose() &lt;/LI&gt;
&lt;LI&gt;AuthenticateUser() &lt;/LI&gt;
&lt;LI&gt;Log() &lt;/LI&gt;
&lt;LI&gt;IsValidUser() &lt;/LI&gt;
&lt;LI&gt;IsAddressBanned() &lt;/LI&gt;
&lt;LI&gt;IsSessionFlagged() &lt;/LI&gt;
&lt;LI&gt;FlagSession() &lt;/LI&gt;
&lt;LI&gt;GarbageCollection &lt;/LI&gt;
&lt;LI&gt;GetRecordCountByCriteria() &lt;/LI&gt;
&lt;LI&gt;InsertDataIntoTable() &lt;/LI&gt;
&lt;LI&gt;DeleteRecordsByCriteria() &lt;/LI&gt;
&lt;LI&gt;ExecuteQuery() &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: You could easily add the email functionality to the FlagSession() method so you will see when a banned IP address is trying to access your server, but depending on the number of sessions that are flagged on your server you might receive more emails than you really need.&lt;/P&gt;
&lt;H5&gt;Step 7 - Register the provider and configure your settings&lt;/H5&gt;
&lt;P&gt;In this last step you add the provider to your IIS configuration settings using the AppCmd utility, and you specify the values for the various settings that the provider requires:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;KBD&gt;cd %SystemRoot%\System32\Inetsrv &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"[name='FtpAddressRestrictionAuthentication',type='FtpAddressRestrictionAuthentication,FtpAddressRestrictionAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpServerName',value='localhost']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpServerPort',value='25']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpFromAddress',value='someone@contoso.com']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='smtpToAddress',value='someone@contoso.com']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='connectionString',value='Server=localhost;Database=FtpAuthentication;User ID=FtpLogin;Password=P@ssw0rd']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='logonAttempts',value='5']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='floodSeconds',value='30']" /commit:apphost&lt;/KBD&gt; &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;B&gt;Note&lt;/B&gt;: You need to update the above syntax using the managed type information for your provider and the configuration settings for your SMTP server, email addresses, and database connection string.&lt;/P&gt;
&lt;H5&gt;Step 8 - Add the provider to a site&lt;/H5&gt;
&lt;P&gt;In this last step you add the provider to a site. If you were adding the provider to your Default Web Site that would look like the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;KBD&gt;AppCmd.exe set config -section:system.applicationHost/sites /"[name='Default Web Site'].ftpServer.security.authentication.basicAuthentication.enabled:False" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd.exe set config -section:system.applicationHost/sites /+"[name='Default Web Site'].ftpServer.security.authentication.customAuthentication.providers.[name='FtpAddressRestrictionAuthentication',enabled='True']" /commit:apphost &lt;BR&gt;&lt;BR&gt;AppCmd set site "Default Web Site" /+ftpServer.customFeatures.providers.[name='FtpAddressRestrictionAuthentication',enabled='true'] /commit:apphost &lt;/KBD&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;H4&gt;Summary&lt;/H4&gt;
&lt;P&gt;That wraps it up for today's post, and I hope you find it useful. &lt;IMG alt=Smile src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif" mce_src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif"&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9876281" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>Automatically Creating Checksum Files for FTP Uploads</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/07/30/automatically-creating-checksum-files-for-ftp-uploads.aspx</link><pubDate>Fri, 31 Jul 2009 04:01:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9853914</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9853914.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9853914</wfw:commentRss><description>&lt;P&gt;I had a great question in the publishing forums on &lt;A href="http://forums.iis.net/" mce_href="http://forums.iis.net/"&gt;forums.iis.net&lt;/A&gt;, where someone was asking if FTP 7 supported the XCRC command. The short answer is that the XCRC command is not supported, but I came up with a way to create an FTP provider that supports something &lt;I&gt;like&lt;/I&gt; it. Since it was a rather fun code sample to write, I thought that I'd turn it into a blog. &lt;/P&gt;
&lt;P&gt;The sample FTP provider code in this blog post will automatically calculate an MD5 checksum from a file that is uploaded and store it in a file with a "*.MD5.TXT" file name extension. You can then compare the uploaded checksum with a local checksum on the client to verify the uploaded file's integrity.&lt;/P&gt;
&lt;P&gt;There are a few points that I need to discuss before I present the code sample:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;I chose to use MD5 because it is built-in to the .NET System.Security.Cryptography namespace and I often like to use MD5 for file checksums. I could just have easily implemented SHA1, SHA256, or any of the other built-in hashing algorithms. Unfortunately, CRC32 is not a built-in algorithm for .NET, but a quick search around the Internet yielded several CRC32 samples in C# from various developers, so if you specifically need the CRC32 algorithm you can find it pretty quickly and substitute it for MD5 in my example. (&lt;A href="http://www.bing.com/search?q=C%23+CRC32" target=_blank mce_href="http://www.bing.com/search?q=C%23+CRC32"&gt;You can click here to search for examples&lt;/A&gt;.) You could go one step further and have your provider support multiple checksum algorithms, but that's going way outside the scope of this blog.&lt;/LI&gt;
&lt;LI&gt;There are a couple of security considerations for this provider: 
&lt;UL&gt;
&lt;LI&gt;The provider needs to calculate the path of the uploaded file, and to do so requires calling into the IIS configuration APIs. As I mention in the code remarks: 
&lt;UL&gt;
&lt;LI&gt;The FTP service will host the compiled assembly in the "Microsoft FTP Service Extensibility Host" COM+ package (DLLHOST.EXE), which runs by default as NETWORK SERVICE.&lt;/LI&gt;
&lt;LI&gt;Also by default, the NETWORK SERVICE account does not have sufficient privileges to read the IIS configuration settings. As such, you must either grant READ permissions to NETWORK SERVICE for the IIS configuration files, or configure the COM+ package to run as a user that has at least READ access to the files in the InetSrv\config folder.&lt;/LI&gt;
&lt;LI&gt;By default, the NETWORK SERVICE account may not have WRITE permission to the folder where your files are uploaded, so the checksum files cannot be written. As such, you will need to grant READ/WRITE access to the destination where the checksum files will be written.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;The above steps are not generally recommended practices; but if you choose to grant NETWORK SERVICE permission to the configuration files, the remarks section in the code sample provides the details that you need.&lt;/LI&gt;
&lt;LI&gt;Alternatively, you could skip the path lookup and always store the checksum files in a known location. This allows you to remove the &lt;B&gt;MapSiteRootPath()&lt;/B&gt; and &lt;B&gt;FindElement()&lt;/B&gt; methods from the code sample, and you need only grant the NETWORK SERVICE account permission for the known location.&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;The &lt;B&gt;MapSiteRootPath()&lt;/B&gt; method in the provider sample calculates the path of the site's root, then uses the relative path of the uploaded file to compute the full path to the checksum file. This does not take into account any paths that include virtual directories; as such, you would need to accommodate for any virtual paths in your site's hierarchy. (That's too much code for this blog post.)&lt;/LI&gt;
&lt;LI&gt;The provider defines a 1 GB constant for the maximum file size for computing checksums. I specified this value so that large files would not tie up your system's resources. You can increase or decrease that value, you could make that a parameter that is stored in the provider's settings, or you can remove the functionality completely. This provider runs synchronously, so larger files will obviously take more time. While it's outside the scope of this blog, you could implement some form of asynchronous functionality. (When discussing this provider with &lt;A href="http://blogs.msdn.com/danielvl/" target=_blank mce_href="http://blogs.msdn.com/danielvl/"&gt;Daniel Vasquez Lopez&lt;/A&gt;, he suggested using MSMQ - but that's &lt;I&gt;really&lt;/I&gt; going way beyond the scope of what I wanted to accomplish with this blog.)&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;All of that being said, this provider follows the same development path as the provider in my &lt;A href="http://learn.iis.net/page.aspx/602/how-to-use-managed-code-c-to-create-a-simple-ftp-logging-provider/" target=_blank mce_href="http://learn.iis.net/page.aspx/602/how-to-use-managed-code-c-to-create-a-simple-ftp-logging-provider/"&gt;How to Use Managed Code (C#) to Create a Simple FTP Logging Provider&lt;/A&gt; walkthrough, so if you follow the steps in that walkthrough and substitute "&lt;B&gt;FtpUploadChecksumDemo&lt;/B&gt;" every place that you see "&lt;B&gt;FtpLoggingDemo&lt;/B&gt;" and add a reference to &lt;B&gt;Microsoft.Web.Administration&lt;/B&gt;, you should have all of the steps that you need in order to use this provider.&lt;/P&gt;
&lt;P&gt;So without further discussion, here's the code for the provider:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Configuration.Provider;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.IO;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Security.Cryptography;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; System.Text;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; Microsoft.Web.Administration;
&lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; Microsoft.Web.FtpServer;

&lt;SPAN style="COLOR: #008000"&gt;// NOTE: This code is provided "as-is" and comes with the following security&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// considerations. The FTP service will host the compiled assembly in the&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// "Microsoft FTP Service Extensibility Host" COM+ package (DLLHOST.EXE),&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// which runs by default as NETWORK SERVICE. By default, this account does not&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// have sufficient privileges to read the IIS configuration settings. As such,&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// you must either grant READ permissions to NETWORK SERVICE for the configuration&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// files, or configure the COM+ package to run as a user that has at least READ&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// access to the files in the InetSrv\config folder and READ/WRITE access to the&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// destination where the checksum file will be written. However, these are not&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// generally recommended practices.&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;//&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// If you choose to grant NETWORK SERVICE permission to the configuration files,&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// the following three commands should accomplish the requisite permissions:&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;//&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;//  cacls "%SystemRoot%\System32\inetsrv\config" /G "Network Service":R /E&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;//  cacls "%SystemRoot%\System32\inetsrv\config\redirection.config" /G "Network Service":R /E&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;//  cacls "%SystemRoot%\System32\inetsrv\config\applicationHost.config" /G "Network Service":R /E&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;//&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// NOTE: You will need to do something similar for your content directory so that&lt;/SPAN&gt;
&lt;SPAN style="COLOR: #008000"&gt;// the checksum files can be created.&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #0000ff"&gt;public&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;sealed&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;class&lt;/SPAN&gt; FtpUploadChecksumDemo : BaseProvider, IFtpLogProvider
{
  &lt;SPAN style="COLOR: #008000"&gt;// Implement the logging method.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;void&lt;/SPAN&gt; IFtpLogProvider.Log(FtpLogEntry loggingParameters)
  {
    &lt;SPAN style="COLOR: #008000"&gt;// Test for a successful file upload operation.&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; ((loggingParameters.Command == &lt;SPAN style="COLOR: #800000"&gt;"STOR"&lt;/SPAN&gt;) &amp;amp;&amp;amp; 
      (loggingParameters.FtpStatus == 226))
    {
      &lt;SPAN style="COLOR: #0000ff"&gt;try&lt;/SPAN&gt;
      {
        &lt;SPAN style="COLOR: #008000"&gt;// Define a 1GB maximum length - to prevent system hogging.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;const&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;long&lt;/SPAN&gt; maxLength = 0x3fffffff;

        &lt;SPAN style="COLOR: #008000"&gt;// Map the path to the site root.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; fullPath = MapSiteRootPath(loggingParameters.SiteName);
        &lt;SPAN style="COLOR: #008000"&gt;// Append the relative path of the uploaded file.&lt;/SPAN&gt;
        fullPath += loggingParameters.FullPath;
        &lt;SPAN style="COLOR: #008000"&gt;// Expand any environment variables.&lt;/SPAN&gt;
        fullPath = Environment.ExpandEnvironmentVariables(fullPath);
        &lt;SPAN style="COLOR: #008000"&gt;// Convert forward slashes to back slashes&lt;/SPAN&gt;
        fullPath = fullPath.Replace(&lt;SPAN style="COLOR: #800000"&gt;@"/"&lt;/SPAN&gt;, &lt;SPAN style="COLOR: #800000"&gt;@"\"&lt;/SPAN&gt;);

        &lt;SPAN style="COLOR: #008000"&gt;// Open the uploaded file to create a CRC.&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; (FileStream input = File.Open(
          fullPath,
          FileMode.Open,
          FileAccess.Read,
          FileShare.Read))
        {
          &lt;SPAN style="COLOR: #008000"&gt;// Test the input file length.&lt;/SPAN&gt;
          &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (input.Length &amp;gt; maxLength)
          {
            &lt;SPAN style="COLOR: #008000"&gt;// Throw an execption if the file is too big.&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ProviderException(
              String.Format(&lt;SPAN style="COLOR: #800000"&gt;"Input file is too large: {0}"&lt;/SPAN&gt;,
              input.Length.ToString()));
          }
          &lt;SPAN style="COLOR: #0000ff"&gt;else&lt;/SPAN&gt;
          {
            &lt;SPAN style="COLOR: #008000"&gt;// Open the hash file for output.&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; (StreamWriter output = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; StreamWriter(
              fullPath + &lt;SPAN style="COLOR: #800000"&gt;".MD5.txt"&lt;/SPAN&gt;,
              &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;))
            {
              &lt;SPAN style="COLOR: #008000"&gt;// Create an MD5 object.&lt;/SPAN&gt;
              MD5 md5 = MD5.Create();
              &lt;SPAN style="COLOR: #008000"&gt;// Retrieve the hash byte array.&lt;/SPAN&gt;
              &lt;SPAN style="COLOR: #0000ff"&gt;byte&lt;/SPAN&gt;[] byteArray = md5.ComputeHash(input);
              &lt;SPAN style="COLOR: #008000"&gt;// Create a new string builder for the ASCII hash string.&lt;/SPAN&gt;
              StringBuilder stringBuilder =
                &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; StringBuilder(byteArray.Length * 2);
              &lt;SPAN style="COLOR: #008000"&gt;// Loop through the hash.&lt;/SPAN&gt;
              &lt;SPAN style="COLOR: #0000ff"&gt;foreach&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;byte&lt;/SPAN&gt; byteMember &lt;SPAN style="COLOR: #0000ff"&gt;in&lt;/SPAN&gt; byteArray)
              {
                &lt;SPAN style="COLOR: #008000"&gt;// Append each ASCII hex byte to the hash string.&lt;/SPAN&gt;
                stringBuilder.AppendFormat(&lt;SPAN style="COLOR: #800000"&gt;"{0:x2}"&lt;/SPAN&gt;, byteMember);
              }
              &lt;SPAN style="COLOR: #008000"&gt;// Write the hash string to the output file.&lt;/SPAN&gt;
              output.Write(stringBuilder);
            }
          }
        }
      }
      &lt;SPAN style="COLOR: #0000ff"&gt;catch&lt;/SPAN&gt;(Exception ex)
      {
        &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ProviderException(ex.Message);
      }
    }
  }

  &lt;SPAN style="COLOR: #008000"&gt;// This method is almost 100% from scripts that were created&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #008000"&gt;// by the IIS Manager Configuration Editor admin pack tool.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; MapSiteRootPath(&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; siteName)
  {
    &lt;SPAN style="COLOR: #0000ff"&gt;try&lt;/SPAN&gt;
    {
      &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; (ServerManager serverManager = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ServerManager())
      {
        Configuration config =
          serverManager.GetApplicationHostConfiguration();
        ConfigurationSection sitesSection =
          config.GetSection(&lt;SPAN style="COLOR: #800000"&gt;"system.applicationHost/sites"&lt;/SPAN&gt;);
        ConfigurationElementCollection sitesCollection =
          sitesSection.GetCollection();
        ConfigurationElement siteElement =
          FindElement(sitesCollection, &lt;SPAN style="COLOR: #800000"&gt;"site"&lt;/SPAN&gt;, &lt;SPAN style="COLOR: #800000"&gt;"name"&lt;/SPAN&gt;, siteName);
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (siteElement == &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;)
        {
          &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; InvalidOperationException(&lt;SPAN style="COLOR: #800000"&gt;"Element not found!"&lt;/SPAN&gt;);
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;else&lt;/SPAN&gt;
        {
          ConfigurationElementCollection siteCollection =
            siteElement.GetCollection();
          ConfigurationElement applicationElement =
            FindElement(siteCollection,
            &lt;SPAN style="COLOR: #800000"&gt;"application"&lt;/SPAN&gt;,
            &lt;SPAN style="COLOR: #800000"&gt;"path"&lt;/SPAN&gt;, &lt;SPAN style="COLOR: #800000"&gt;@"/"&lt;/SPAN&gt;);
          &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (applicationElement == &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;)
          {
            &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; InvalidOperationException(&lt;SPAN style="COLOR: #800000"&gt;"Element not found!"&lt;/SPAN&gt;);
          }
          &lt;SPAN style="COLOR: #0000ff"&gt;else&lt;/SPAN&gt;
          {
            ConfigurationElementCollection applicationCollection =
              applicationElement.GetCollection();
            ConfigurationElement virtualDirectoryElement =
              FindElement(applicationCollection,
              &lt;SPAN style="COLOR: #800000"&gt;"virtualDirectory"&lt;/SPAN&gt;,
              &lt;SPAN style="COLOR: #800000"&gt;"path"&lt;/SPAN&gt;, &lt;SPAN style="COLOR: #800000"&gt;@"/"&lt;/SPAN&gt;);
            &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (virtualDirectoryElement == &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;)
            {
              &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; InvalidOperationException(&lt;SPAN style="COLOR: #800000"&gt;"Element not found!"&lt;/SPAN&gt;);
            }
            &lt;SPAN style="COLOR: #0000ff"&gt;else&lt;/SPAN&gt;
            {
              &lt;SPAN style="COLOR: #0000ff"&gt;return&lt;/SPAN&gt; virtualDirectoryElement[&lt;SPAN style="COLOR: #800000"&gt;"physicalPath"&lt;/SPAN&gt;].ToString();
            }
          }
        }
      }
    }
    &lt;SPAN style="COLOR: #0000ff"&gt;catch&lt;/SPAN&gt; (Exception ex)
    {
      &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ProviderException(ex.Message);
    }
  }

  &lt;SPAN style="COLOR: #008000"&gt;// This method is almost 100% from scripts that were created&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #008000"&gt;// by the IIS Manager Configuration Editor admin pack tool.&lt;/SPAN&gt;
  &lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;static&lt;/SPAN&gt; ConfigurationElement FindElement(
    ConfigurationElementCollection collection,
    &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; elementTagName,
    &lt;SPAN style="COLOR: #0000ff"&gt;params&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;[] keyValues)
  {
    &lt;SPAN style="COLOR: #0000ff"&gt;foreach&lt;/SPAN&gt; (ConfigurationElement element &lt;SPAN style="COLOR: #0000ff"&gt;in&lt;/SPAN&gt; collection)
    {
      &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (String.Equals(element.ElementTagName,
        elementTagName,
        StringComparison.OrdinalIgnoreCase))
      {
        &lt;SPAN style="COLOR: #0000ff"&gt;bool&lt;/SPAN&gt; matches = &lt;SPAN style="COLOR: #0000ff"&gt;true&lt;/SPAN&gt;;

        &lt;SPAN style="COLOR: #0000ff"&gt;for&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;int&lt;/SPAN&gt; i = 0; i &amp;lt; keyValues.Length; i += 2)
        {
          &lt;SPAN style="COLOR: #0000ff"&gt;object&lt;/SPAN&gt; o = element.GetAttributeValue(keyValues[i]);
          &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;value&lt;/SPAN&gt; = &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;;
          &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (o != &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;)
          {
            &lt;SPAN style="COLOR: #0000ff"&gt;value&lt;/SPAN&gt; = o.ToString();
          }

          &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (!String.Equals(&lt;SPAN style="COLOR: #0000ff"&gt;value&lt;/SPAN&gt;,
            keyValues[i + 1],
            StringComparison.OrdinalIgnoreCase))
          {
            matches = &lt;SPAN style="COLOR: #0000ff"&gt;false&lt;/SPAN&gt;;
            &lt;SPAN style="COLOR: #0000ff"&gt;break&lt;/SPAN&gt;;
          }
        }
        &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (matches)
        {
          &lt;SPAN style="COLOR: #0000ff"&gt;return&lt;/SPAN&gt; element;
        }
      }
    }
    &lt;SPAN style="COLOR: #0000ff"&gt;return&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;null&lt;/SPAN&gt;;
  }
}
&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;That wraps it up for today's post.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif" mce_src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/regular_smile.gif"&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9853914" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>A Little Scripting Saved My Day (;-])</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/07/27/a-little-scripting-saved-my-day.aspx</link><pubDate>Mon, 27 Jul 2009 21:55:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9850238</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9850238.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9850238</wfw:commentRss><description>&lt;P&gt;I have mentioned in previous blog posts that I tend to write many of my blog posts and walkthroughs for &lt;A href="http://www.iis.net/" target=_blank mce_href="http://www.iis.net/"&gt;IIS.NET&lt;/A&gt; based on code that I’ve written for myself, and today’s blog post is the story of how one of my samples saved my rear over this past weekend.&lt;/P&gt;
&lt;P&gt;One of the servers that I manage is used to host web sites for several friends of mine. (It’s their hobby to have a web site and it’s my hobby to host it for them.) Anyway, sometime on Sunday someone let me know that one of my sites didn’t seem to be behaving correctly, so I browsed it with Internet Explorer and saw that I was getting an HTTP 503 error. I’ve seen this error when an application pool goes offline for some reason, so I didn’t panic – yet – because I knew that the web site was in a separate application pool. With that in mind, I browsed to a web site that is in a different application pool. Same thing – HTTP 503 error. This was beginning to concern me.&lt;/P&gt;
&lt;P&gt;I logged into the web server and ran iisreset from a command-line – this threw the following error - and now I was really starting to become agitated:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;TABLE class="" style="BORDER-COLLAPSE: collapse; BACKGROUND-COLOR: #000000" border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="" style="COLOR: #ffffff"&gt;
&lt;P style="COLOR: #ffffff"&gt;&lt;TT&gt;CMD&amp;gt;iisreset&lt;BR&gt;&lt;BR&gt;Attempting stop...&lt;BR&gt;Internet services successfully stopped&lt;BR&gt;Attempting start...&lt;BR&gt;Restart attempt failed.&lt;BR&gt;The IIS Admin Service or the World Wide Web Publishing Service, or a service dependent on them failed to start. The service, or dependent services, may had an error during its startup or may be disabled.&lt;BR&gt;&lt;BR&gt;CMD&amp;gt;&lt;/TT&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I knew that the cause of the error should be in the Windows Event Viewer, so I opened the System log in Event Viewer and saw the following error:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;TABLE class="" style="BORDER-COLLAPSE: collapse" cellSpacing=1 cellPadding=2 width="100%" border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Log Name:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;System&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Source:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;Microsoft-Windows-WAS&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Date:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;7/26/2009 10:59:52 AM&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Event ID:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;5172&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Task Category:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;None&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Level:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;Error&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Keywords:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;Classic&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;User:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;N/A&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Computer:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;MYSERVER&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left&gt;Description:&lt;/TH&gt;
&lt;TD class="" vAlign=top align=left&gt;The Windows Process Activation Service encountered an error trying to read configuration data from file '\\?\C:\Windows\system32\inetsrv\config\applicationHost.config', line number '308'. The error message is: 'Configuration file is not well-formed XML'. The data field contains the error number.&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TH class="" vAlign=top noWrap align=left colSpan=2&gt;Event Xml:&lt;/TH&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top align=left colSpan=2&gt;
&lt;P style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;TT&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Event&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;xmlns&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="http://schemas.microsoft.com/win/2004/08/events/event"&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;System&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Provider&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Microsoft-Windows-WAS"&lt;/SPAN&gt;&lt;SPAN style="COLOR: #ff0000"&gt; Guid&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="{4E616D65-6F6E-6D65-6973-526F62657274}"&lt;/SPAN&gt;&lt;SPAN style="COLOR: #ff0000"&gt; EventSourceName&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="WAS"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;EventID&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;Qualifiers&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="49152"&amp;gt;&lt;/SPAN&gt;5172&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;EventID&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Version&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;0&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Version&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Level&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;2&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Level&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Task&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;0&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Task&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Opcode&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;0&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Opcode&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Keywords&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;0x80000000000000&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Keywords&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;TimeCreated&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;SystemTime&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="2009-07-26T17:59:52.000Z"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;EventRecordID&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;32807&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;EventRecordID&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Correlation&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Execution&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;ProcessID&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="0"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;ThreadID&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="0"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Channel&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;System&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Channel&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Computer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;MYSERVER&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Computer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Security&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp; &amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;System&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;EventData&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Data&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="File"&amp;gt;&lt;/SPAN&gt;\\?\C:\Windows\system32\inetsrv\config\applicationHost.config&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Data&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Data&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="LineNumber"&amp;gt;&lt;/SPAN&gt;308&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Data&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Data&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="Error"&amp;gt;&lt;/SPAN&gt;Configuration file is not well-formed XML&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Data&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Binary&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;0D000780&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Binary&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;nbsp; &amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;EventData&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;BR&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;Event&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;/TT&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now that I was armed with the file name and line number of the failure in my configuration settings, I was able to go straight to the source of the problem. (I love IIS 7's descriptive error messages - don't you?) Once I opened the file and jumped to the correct location, I saw several lines of unintelligible garbage. For reasons that are still unknown to me – my applicationHost.config file had become corrupted and IIS was dead in the water until I fixed the problem. I looked through the file and removed most of the garbage and saved the edited file&amp;nbsp;to IIS – this got the web sites working, but only partially. Some necessary settings had obviously been removed while I was clearing all of out the unintelligible garbage, and it might take me a long time to discover what those settings were.&lt;/P&gt;
&lt;P&gt;The next thing that I did was to take a look in my two readily-accessible backup drives; I have two external hard drives that keep a backup of the web server - one hard drive is directly plugged into the web server via a USB cable, and the other hard drive is plugged into a physically separate server that rotates drives with off-site storage on a monthly basis. The problem is, my weekly backups had just run, so the copy in each backup location had been overwritten with the corrupted version. (I’m going to have to rethink my backup strategy after this – but that’s another story.) The backup copy in my off-site storage location should be intact, but that copy would be a few weeks old so I would be missing some settings, and I would have to drive an hour or so round-trip in order to pick up the drive. This wasn’t an ideal solution – but it was definitely a feasible strategy.&lt;/P&gt;
&lt;P&gt;It was at this point that I remembered that I had written following blog post some time ago:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;STRONG&gt;Automating IIS 7 Backups&lt;/STRONG&gt; &lt;BR&gt;&lt;A title=http://blogs.msdn.com/robert_mcmurray/archive/2008/03/08/automating-iis-7-backups.aspx href="http://blogs.msdn.com/robert_mcmurray/archive/2008/03/08/automating-iis-7-backups.aspx" target=_blank mce_href="http://blogs.msdn.com/robert_mcmurray/archive/2008/03/08/automating-iis-7-backups.aspx"&gt;http://blogs.msdn.com/robert_mcmurray/archive/2008/03/08/automating-iis-7-backups.aspx&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I wrote the script in that blog post for the server that I was currently managing, and because of this preventative measure I had dozens of backups going back several weeks to choose from. So I was able to quickly find a copy with no corruption and I restored that copy to my IIS config directory. At this point all of my web sites came online with all of their functionality. Having fixed the major issues, I used WinDiff to verify any settings that might have been changed between the restored copy and the corrupted copy.&lt;/P&gt;
&lt;P&gt;So in conclusion, this story had a happy ending, and it left me with a few lessons learned:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;You can never have too many backups &lt;/LI&gt;
&lt;LI&gt;I need to rethink how I roll out my backup strategy with regard to using external hard drives &lt;/LI&gt;
&lt;LI&gt;Writing cool scripts to automate your backups can save your rear end &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;That sums it up for today’s post. ;-]&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9850238" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/Scripting/default.aspx">Scripting</category></item><item><title>Merging FTP Extensibility Walkthroughs</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/07/23/merging-ftp-extensibility-walkthroughs.aspx</link><pubDate>Thu, 23 Jul 2009 19:43:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9846647</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9846647.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9846647</wfw:commentRss><description>&lt;P&gt;Over the past several months I’ve been publishing a series of walkthroughs that use the extensibility in FTP 7.5 to create a several custom providers for a variety of scenarios, and today I posted my most recent entry in the series:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;A href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/" target=_blank mce_href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/"&gt;How to Use Managed Code to Create an FTP Authentication Provider using an XML Database&lt;/A&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;As a piece of behind-the-scenes trivia, some of these walkthroughs were based off custom providers that I had actually written for my FTP servers, and I used the samples that I wrote for some of the other walkthroughs as a starting point for custom providers that I currently use. With that in mind, I’d like to use today’s blog to talk about some of the ways that I combine what you see in a few of these walkthroughs into some useful scenarios.&lt;/P&gt;
&lt;P&gt;One of the common providers that I use is a combination of the code that you see in these two walkthroughs:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/" target=_blank mce_href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/"&gt;How to Use Managed Code to Create an FTP Authentication Provider using an XML Database&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://learn.iis.net/page.aspx/600/how-to-use-managed-code-to-create-a-simple-ftp-home-directory-provider/" target=_blank mce_href="http://learn.iis.net/page.aspx/600/how-to-use-managed-code-to-create-a-simple-ftp-home-directory-provider/"&gt;How to Use Managed Code to Create a Simple FTP Home Directory Provider&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Here's the way that I create the provider - I start with a single provider class that implements the &lt;B&gt;IFtpHomeDirectoryProvider&lt;/B&gt;, &lt;B&gt;IFtpAuthenticationProvider&lt;/B&gt;, and &lt;B&gt;IFtpRoleProvider&lt;/B&gt; interfaces, and I create a few global variables that I'll use later.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;public&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;class&lt;/SPAN&gt; FtpXmlAuthentication : BaseProvider,
    IFtpHomeDirectoryProvider,
    IFtpAuthenticationProvider,
    IFtpRoleProvider
{
    &lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; _XmlFileName;

    &lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; _HomeDirectory;

    &lt;SPAN style="COLOR: #0000ff"&gt;private&lt;/SPAN&gt; Dictionary&amp;lt;&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;, XmlUserData&amp;gt; _XmlUserData =
        &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Dictionary&amp;lt;&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;, XmlUserData&amp;gt;(
            StringComparer.InvariantCultureIgnoreCase);
}&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I add an &lt;B&gt;Initialize()&lt;/B&gt; method to the class, where I load the values named &lt;B&gt;xmlFileName&lt;/B&gt; and &lt;B&gt;homeDirectory&lt;/B&gt; from the configuration settings.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;protected&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;override&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;void&lt;/SPAN&gt; Initialize(StringDictionary config)
{
    _XmlFileName = config[&lt;SPAN style="COLOR: #800000"&gt;"xmlFileName"&lt;/SPAN&gt;];
    _HomeDirectory = config[&lt;SPAN style="COLOR: #800000"&gt;"homeDirectory"&lt;/SPAN&gt;];
    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.IsNullOrEmpty(_XmlFileName))
    {
        &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ArgumentException(&lt;SPAN style="COLOR: #800000"&gt;"Missing xmlFileName value in configuration."&lt;/SPAN&gt;);
    }
}&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I recycle the provider across a bunch of different FTP sites, and I don't always use the custom home directory feature, so my &lt;B&gt;GetUserHomeDirectoryData()&lt;/B&gt; method has to accommodate for that. (Note: this means that your FTP site has to use a method of User Isolation other than "Custom". You can find more information about User Isolation on the &lt;A href="http://technet.microsoft.com/en-us/library/dd722768(WS.10).aspx" target=_blank mce_href="http://technet.microsoft.com/en-us/library/dd722768(WS.10).aspx"&gt;FTP User Isolation Page&lt;/A&gt;.)&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; IFtpHomeDirectoryProvider.GetUserHomeDirectoryData(
    &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; sessionId,
    &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; siteName,
    &lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt; userName)
{
    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (&lt;SPAN style="COLOR: #0000ff"&gt;string&lt;/SPAN&gt;.IsNullOrEmpty(_HomeDirectory))
    {
        &lt;SPAN style="COLOR: #0000ff"&gt;throw&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; ArgumentException(&lt;SPAN style="COLOR: #800000"&gt;"Missing homeDirectory value in configuration."&lt;/SPAN&gt;);
    }
    &lt;SPAN style="COLOR: #0000ff"&gt;return&lt;/SPAN&gt; _HomeDirectory;
}&lt;/PRE&gt;
&lt;P&gt;(Note: While it may seem that I could throw the &lt;B&gt;ArgumentException()&lt;/B&gt; in the &lt;B&gt;Initialize()&lt;/B&gt; method, since I don't always need this value for providers that don't implement the home directory lookup it's best to throw the exception in the &lt;B&gt;GetUserHomeDirectoryData()&lt;/B&gt; method.)&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The last thing that I do for the provider is to copy the &lt;B&gt;AuthenticateUser()&lt;/B&gt;, &lt;B&gt;IsUserInRole()&lt;/B&gt;, &lt;B&gt;ReadXmlDataStore()&lt;/B&gt;, &lt;B&gt;GetInnerText()&lt;/B&gt; methods and &lt;B&gt;XmlUserData&lt;/B&gt; class from the &lt;A href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/" target=_blank mce_href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/"&gt;How to Use Managed Code to Create an FTP Authentication Provider using an XML Database&lt;/A&gt; walkthrough. This gives me a custom FTP authentication provider that provides user, role, and home directory lookups. This means the XML file for the provider registration has to vary a little from the walkthroughs in order to define settings for the &lt;B&gt;xmlFileName&lt;/B&gt; and &lt;B&gt;homeDirectory&lt;/B&gt; values. Here's an example of that that might look like:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;system.ftpServer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerDefinitions&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="ContosoXmlAuthentication"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;type&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="FtpXmlAuthentication,FtpXmlAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;activation&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="ContosoXmlAuthentication"&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="xmlFileName"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.contoso.com\Users.xml"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="homeDirectory"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.contoso.com\ftproot"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;activation&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerDefinitions&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;

    &lt;SPAN style="COLOR: #008000"&gt;&amp;lt;!-- Other XML goes here --&amp;gt;&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;system.ftpServer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The last thing that you need to do is to create the XML file that contains the usernames and passwords, which you can copy from the &lt;A href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/" target=_blank mce_href="http://learn.iis.net/page.aspx/669/how-to-use-managed-code-to-create-an-ftp-authentication-provider-using-an-xml-database/"&gt;How to Use Managed Code to Create an FTP Authentication Provider using an XML Database&lt;/A&gt; walkthrough.&lt;/P&gt;
&lt;P&gt;I use this provider on multiple FTP sites, so I simply re-register the provider under a different name and specify different values for the &lt;B&gt;xmlFileName&lt;/B&gt; and &lt;B&gt;homeDirectory&lt;/B&gt; values:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;&lt;PRE style="COLOR: black; BACKGROUND-COLOR: #ffffff"&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;system.ftpServer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerDefinitions&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="ContosoXmlAuthentication"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;type&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="FtpXmlAuthentication,FtpXmlAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="FabrikamXmlAuthentication"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;type&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="FtpXmlAuthentication,FtpXmlAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="WingTipToysXmlAuthentication"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;type&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="FtpXmlAuthentication,FtpXmlAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;activation&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="ContosoXmlAuthentication"&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="xmlFileName"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.Contoso.com\Users.xml"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="homeDirectory"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.Contoso.com\ftproot"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="FabrikamXmlAuthentication"&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="xmlFileName"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.Fabrikam.com\Users.xml"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="homeDirectory"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.Fabrikam.com\ftproot"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;name&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="WingTipToysXmlAuthentication"&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="xmlFileName"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.WingTipToys.com\Users.xml"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
                &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;add&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;key&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="homeDirectory"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #ff0000"&gt;value&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;="C:\Inetpub\www.WingTipToys.com\ftproot"&lt;/SPAN&gt; &lt;SPAN style="COLOR: #0000ff"&gt;/&amp;gt;&lt;/SPAN&gt;
            &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerData&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
        &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;activation&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;providerDefinitions&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;

    &lt;SPAN style="COLOR: #008000"&gt;&amp;lt;!-- Other XML goes here --&amp;gt;&lt;/SPAN&gt;

&lt;SPAN style="COLOR: #0000ff"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: #800000"&gt;system.ftpServer&lt;/SPAN&gt;&lt;SPAN style="COLOR: #0000ff"&gt;&amp;gt;&lt;/SPAN&gt;&lt;/PRE&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;So in the end I have a provider that provides unique users, roles, and home directory for each FTP site. I point the FTP root to a path that is outside of the HTTP root, so my users can upload files for an application like a photo gallery that I provide them, but they can't access the actual ASP.NET files for the application. Since they're using accounts from the XML file, I don't have to hand out physical accounts on my servers or my domain. (The security-paranoid side of my personality really likes that.)&lt;/P&gt;
&lt;P&gt;For some sites I use the XML file for ASP.NET membership by following the instructions in the &lt;A href="http://learn.iis.net/page.aspx/528/how-to-use-the-sample-read-only-xml-membership-and-role-providers-with-iis-70" target=_blank mce_href="http://learn.iis.net/page.aspx/528/how-to-use-the-sample-read-only-xml-membership-and-role-providers-with-iis-70"&gt;How to use the Sample Read-Only XML Membership and Role Providers with IIS 7.0&lt;/A&gt; walkthrough. In those cases, I move the XML file into the App_Data folder of the web site. Once again, since the FTP root is different than the HTTP root, this prevents any of my FTP users from accessing the XML file and making changes to it. (Although you could do that if you wanted to allow one of your users to update the list of FTP users for their site. But as you can imagine, the security-paranoid side of my personality really &lt;U&gt;&lt;B&gt;does not&lt;/B&gt;&lt;/U&gt; like that.)&lt;/P&gt;
&lt;P&gt;All that being said, I hope that this helps you to get an idea for other ways that you can use some of the walkthroughs that I've been writing. I have several additional providers and walkthroughs that I’m working on for the &lt;A href="http://www.iis.net/" target=_blank mce_href="http://www.iis.net/"&gt;IIS.NET&lt;/A&gt; web site, but I’ll keep those as a secret for now. ;-]&lt;BR&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9846647" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category></item><item><title>Using Apple's WebDavFS for Mac OS X with WebDAV on IIS 7</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/07/01/using-apple-s-webdavfs-for-mac-os-x-with-webdav-on-iis-7.aspx</link><pubDate>Thu, 02 Jul 2009 05:19:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9812594</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9812594.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9812594</wfw:commentRss><description>&lt;P&gt;Today's blog post needs to have a disclaimer right up front - I freely admit I'm not a Mac OS X expert, so I may not have everything 100% correct in this post. But I've seen a lot of questions on &lt;A href="http://forums.iis.net/" target=_blank&gt;forums.iis.net&lt;/A&gt; that discuss using IIS WebDAV with Mac OS X, so I thought that I'd share a few of the things that I've noticed. Just the same, if I were writing a formal walkthrough I would have said something like, "&lt;I&gt;Microsoft is not responsible for the behavior of Apple's Mac family of products. The information that is provided in this topic is provided to assist Mac OS X users that are connecting to IIS using WebDAV.&lt;/I&gt;" &lt;IMG src="http://messenger.msn.com/MMM2006-04-19_17.00/Resource/emoticons/wink_smile.gif"&gt;&lt;/P&gt;
&lt;P&gt;All that being said, here are the prerequisites for getting your environment together:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Your server needs to be running Windows Vista, Windows Server 2008, or Windows 7.&lt;/LI&gt;
&lt;LI&gt;Your server needs to have Internet Information Services 7 and the WebDAV module installed. (&lt;B&gt;Note&lt;/B&gt;: See the &lt;A href="http://go.microsoft.com/fwlink/?LinkId=105146" target=_blank mce_href="http://go.microsoft.com/fwlink/?LinkId=105146"&gt;Installing and Configuring WebDAV on IIS 7.0&lt;/A&gt; topic for more information.)&lt;/LI&gt;
&lt;LI&gt;For best results, your Mac client needs to be running OS X version 10.4 or later.&lt;/LI&gt;&lt;/UL&gt;
&lt;H3&gt;Connecting to a WebDAV server using Mac OS X&lt;/H3&gt;
&lt;UL&gt;
&lt;LI&gt;In Mac OS X, open &lt;B&gt;Finder&lt;/B&gt;.&lt;/LI&gt;
&lt;LI&gt;Choose &lt;B&gt;Go&lt;/B&gt;, then &lt;B&gt;Connect To Server&lt;/B&gt;.&lt;/LI&gt;
&lt;LI&gt;Enter the URL of the WebDAV server in &lt;B&gt;Server Address&lt;/B&gt;. For example: 
&lt;BLOCKQUOTE&gt;&lt;I&gt;http://www.example.com/path/&lt;/I&gt;&lt;/BLOCKQUOTE&gt;&lt;/LI&gt;
&lt;LI&gt;Click &lt;B&gt;Connect&lt;/B&gt;.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;For more information, please see the following help topics that are available on Apple's Web site:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://docs.info.apple.com/article.html?path=Mac/10.4/en/mh1050.html" target=_blank&gt;Mac OS X 10.4 Help: Connecting to a WebDAV server&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://docs.info.apple.com/article.html?path=Mac/10.5/en/8160.html" target=_blank&gt;Mac OS X 10.5 Help: Connecting to a WebDAV server&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;H3&gt;Troubleshooting WebDAV connections using Mac OS X&lt;/H3&gt;
&lt;P&gt;These are some of the issues that I've seen:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;WebDavFS connections are read-only if WebDAV LOCKs are disabled on the server. Because of this: 
&lt;UL&gt;
&lt;LI&gt;If you are using WebDAV 7.0 on IIS 7 you will not be able write files to the server; this is because WebDAV locks were not available in this release.&lt;/LI&gt;
&lt;LI&gt;If you are using WebDAV 7.5 on IIS 7 you will need to enable locks before you can write files to the server; this is because WebDAV locks are disabled by default. (&lt;B&gt;Note&lt;/B&gt;: See the &lt;A href="http://learn.iis.net/page.aspx/596/how-to-use-webdav-locks/" target=_blank&gt;How to Use WebDAV Locks&lt;/A&gt; topic for more information.)&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;
&lt;LI&gt;WebDavFS connections attempt to create files that may be blocked by IIS: 
&lt;UL&gt;
&lt;LI&gt;"&lt;I&gt;Desktop Services Store&lt;/I&gt;" (.DS_Store) files. (See &lt;A href="http://en.wikipedia.org/wiki/.DS_Store" target=_blank&gt;http://en.wikipedia.org/wiki/.DS_Store&lt;/A&gt; for a description.)&lt;/LI&gt;
&lt;LI&gt;"&lt;I&gt;Resource Fork&lt;/I&gt;" (e.g. "._filename.ext") files. (See &lt;A href="http://en.wikipedia.org/wiki/Resource_fork" target=_blank&gt;http://en.wikipedia.org/wiki/Resource_fork&lt;/A&gt; for a description.)&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Allowing unknown MIME types for WebDAV requests should allow these file types, and that setting is located under &lt;B&gt;Web Settings&lt;/B&gt; action for the &lt;B&gt;WebDAV Authoring Rules&lt;/B&gt; feature of IIS Manager.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://bxtqig.blu.livefilestore.com/y1pPrNDkYWHbqzLrclDaaMqC1dlPhS2et-S5L2CPeoBoX77rY2LXMY8Royxui0oJtJddhYcPIW6nxii6ACKKG_EtA/Allow%20Unknown%20MIME%20Types.jpg"&gt;&lt;/P&gt;
&lt;P&gt;For more information, see the following topics that are available on Apple's Web site:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="http://support.apple.com/kb/HT1629" target=_blank&gt;Mac OS X 10.4: How to prevent .DS_Store file creation over network connections&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://support.apple.com/kb/TA24245" target=_blank&gt;Mac OS X 10.4: Windows Server 2003 IIS Web Server fails GET call on .DS_Store and ._ files&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;H4&gt;In Closing...&lt;/H4&gt;
&lt;P&gt;I have to reiterate that I'm not a Mac OS X expert, so this list is probably not all-inclusive, but it's helped to resolve some of the issues that I've seen.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9812594" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/IIS+Topics/default.aspx">IIS Topics</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/WebDAV/default.aspx">WebDAV</category></item><item><title>Using LogParser with FTP 7.x Sessions</title><link>http://blogs.msdn.com/robert_mcmurray/archive/2009/06/11/using-logparser-with-ftp-7-x-sessions.aspx</link><pubDate>Fri, 12 Jun 2009 05:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9728698</guid><dc:creator>robert_mcmurray</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/robert_mcmurray/comments/9728698.aspx</comments><wfw:commentRss>http://blogs.msdn.com/robert_mcmurray/commentrss.aspx?PostID=9728698</wfw:commentRss><description>&lt;P&gt;One of the great features that we added to our W3C logging enhancements in FTP 7.0 and FTP 7.5 is the ability to track unique sessions, which are represented by GUIDs in a field that is named &lt;I&gt;&lt;B&gt;x-session&lt;/B&gt;&lt;/I&gt;. Because of this addition, you can do some interesting things with LogParser when analyzing your FTP logs.&lt;/P&gt;
&lt;P&gt;The purpose of today's blog is to show a couple of the scripts that I use to analyze some of the session-based information that I'm interested in from time to time.&lt;/P&gt;
&lt;H3&gt;Using LogParser to Count FTP Sessions&lt;/H3&gt;
&lt;P&gt;Since the new FTP service tracks unique sessions, it is now possible to generate reports that show the number of unique FTP sessions you served by day. The following batch file accomplishes this in two parts: first it creates a temporary tab-separated-value file that contains the unique sessions by day, then it calculates the number of sessions by day and writes the totals to a tab-separated-value file that is named &lt;I&gt;Sessions.tsv&lt;/I&gt;, which you can open using an application like Microsoft Excel.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;TT&gt;@echo off&lt;BR&gt;&lt;BR&gt;set LOGPATTERN=u_ex*.log&lt;BR&gt;&lt;BR&gt;logparser.exe "SELECT DISTINCT date,x-session INTO '%~n0.tmp' from %LOGPATTERN%" -i:w3c -o:tsv -headers:ON&lt;BR&gt;&lt;BR&gt;if exist "%~n0.tmp" (&lt;BR&gt;logparser.exe "SELECT date,COUNT(x-session) AS sessions INTO sessions.tsv FROM '%~n0.tmp' GROUP BY date" -i:tsv -o:tsv -headers:ON&lt;BR&gt;del "%~n0.tmp"&lt;BR&gt;)&lt;BR&gt;&lt;BR&gt;set LOGPATTERN=&lt;/TT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;H3&gt;Using LogParser to Split FTP Log Files into Unique Session Activity Logs&lt;/H3&gt;
&lt;P&gt;I use the following script when I am testing various FTP scenarios that will split my FTP log files into individual log files that are named after the GUID for each session. (&lt;B&gt;Note&lt;/B&gt;: Please bear in mind, this may generate a lot of log files, so use it sparingly!) You can then analyze the resulting log files to see the list of client activity that was unique to each session.&lt;/P&gt;
&lt;P&gt;This script accomplishes its objective in two parts: first it creates a temporary tab-separated-value file with the list of unique session IDs, then it loops through each session ID and creates a W3C log file for each session's activity.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;TT&gt;@echo off&lt;BR&gt;&lt;BR&gt;set LOGPATTERN=u_ex*.log&lt;BR&gt;&lt;BR&gt;logparser.exe "select distinct x-session into '%~n0.tmp' from '%LOGPATTERN%'" -i:w3c -o:tsv -headers:off &lt;BR&gt;&lt;BR&gt;if exist "%~n0.tmp" (&lt;BR&gt;for /f "delims=|" %%a in (%~n0.tmp) do (&lt;BR&gt;logparser.exe "select date,time,c-ip,cs-username,s-ip,s-port,cs-method,cs-uri-stem,sc-status,sc-win32-status,sc-substatus,x-session,x-fullpath into '%%a.log' from '%LOGPATTERN%' where x-session='%%a' order by date,time" -i:w3c -o:w3c&lt;BR&gt;)&lt;BR&gt;del "%~n0.tmp"&lt;BR&gt;)&lt;BR&gt;&lt;BR&gt;set LOGPATTERN=&lt;/TT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;That about does it for today - I hope this helps!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9728698" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/FTP/default.aspx">FTP</category><category domain="http://blogs.msdn.com/robert_mcmurray/archive/tags/LogParser/default.aspx">LogParser</category></item></channel></rss>