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

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

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

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

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

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

Using AppCmd

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

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

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

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

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

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

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

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

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

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

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

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

Using VBScript

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

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

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

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

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

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

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

More Information

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


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

My SharePoint 2007 Custom Membership Provider Adventure

Sometime last year I wanted to set up a SharePoint 2007 website for my family members to exchange information. That being said, I was using a custom membership provider, and I ran into a few issues while I was setting things up. I had kept detailed notes while I was configuring my server and troubleshooting the problems that I encountered, and with that in mind, I thought that I would share my experiences. ;-]


Getting Started

Specifying My Environment

My web server is only an older 32-bit Windows Server 2008 computer, so I couldn't install SharePoint 2010 (which is 64-bit only) and I had to install SharePoint 2007. Taking that into account, there were a few additional considerations that I had for my environment:

  • I wanted to use Forms-Based Authentication (FBA). Even though I run my own active directory domain, I avoid giving out physical accounts if I don't have to, so FBA seemed like a great idea.
  • I didn't want to use the built-in ASP.NET membership and roles provider. This is for two reasons:
    • I was already using the built-in ASP.NET membership provider on other websites, and I didn't feel like researching whether I should share the membership database between SharePoint and my other websites, or if I should set up unique membership databases.
    • If you've been reading my previous blogs and you think that I'd be content with using a built-in provider, then you haven't been paying attention. Usually I find myself wanting to do things the hard way, and other times I simply want to write code, but either way I decided to use the sample read-only XML membership and role providers that I documented in the following article:
  • I decided that I could use FBA over HTTP, and therefore I didn't worry about setting up SSL. (I run my own certificate server, so I could have issued myself a certificate and given the root CA certificate to everyone; but this wasn't necessary, so I didn't bother with it.)

Researching My Scenario

With my specific considerations in mind, I took a look at the following article to get started:

That being said, I did not use the following articles, even though they are related to my scenario and they looked interesting:


Creating the SharePoint Website

Here are the brief details on how I created my SharePoint website:

  1. I followed the steps in the following walkthrough in order to create and register the read-only XML membership and role providers with IIS 7:
  2. I created the following physical paths for my website:
    • Website root folder: C:\Inetpub\SharePointSite\wwwroot
    • Application data folder: C:\Inetpub\SharePointSite\wwwroot\App_Data
  3. I created the following user/role XML file for my website:
    • I created an XML file in the location: C:\Inetpub\SharePointSite\wwwroot\App_Data\MyUsers.xml
    • I added the following XML to the file:
      <Users>
         <User>
            <UserName>Alice</UserName>
            <Password>P@ssw0rd</Password>
            <EMail>alice@contoso.com</EMail>
            <Roles>Admins</Roles>
         </User>
         <User>
            <UserName>Bob</UserName>
            <Password>P@ssw0rd</Password>
            <EMail>bob@contoso.com</EMail>
            <Roles>Authors</Roles>
         </User>
      </Users>
  4. I opened the Internet Information Services (IIS) Manager and created a new website; I used the C:\Inetpub\SharePointSite\wwwroot folder for the home directory.
  5. I opened SharePoint 3.0 Central Administration to convert my website into a SharePoint 2007 site:
    1. I clicked the Application Management tab, then clicked Create or extend Web application, and then clicked Create a new Web application:
      • In my case I chose Use an existing IIS web site because I had already created the website that I wanted to use.
      • I chose Create new application pool, I used "Network Service" for the identification, and then I specified all of the requisite database information.
    2. When that completed, I clicked the Application Management tab, and then clicked Create site collection:
      • I specified all options, and I used a valid Active Directory account as the administrator for now.
    3. Once the site was created, I modified the web.config file for the website and the SharePoint Central Administration web.config file. (See the following notes for the details.) Note: The SharePoint Central Administration website needs to know the information about your membership provider in order to add administrators.
    4. After that, I clicked the Application Management tab, and then clicked Authentication Providers:
      • I verified that I was using the correct "Web Application" in the drop-down menu.
      • I clicked on the Default zone.
      • I set the Authentication Type to Forms.
      • I specified the appropriate Membership provider name and Role manager name.
    5. When that completed, I needed to restart IIS before continuing. (NOTE: I used "iisreset" from a command line.)
    6. After IIS had restarted, I clicked the Application Management tab, and then clicked Site collection administrators:
      • I added a user (like Alice or Bob) from the membership provider.

Web.Config Entries

There are a few additions that you have to make to your website's web.config file, as well as the SharePoint Central Administration web.config file for SharePoint 2007:

  • Here's the XML that you need to add to the <system.web> section of your website's web.config; in my example that file would be located at "C:\Inetpub\SharePointSite\wwwroot\web.config":
    <!-- added on 05/31/2011 -->
    <membership defaultProvider="ReadOnlyXmlMembershipProvider">
      <providers>
        <add name="ReadOnlyXmlMembershipProvider"
          type="ReadOnlyXmlMembershipProvider, ReadOnlyXmlMembershipProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73"
          description="Read-only XML membership provider"
          xmlFileName="~/App_Data/MyUsers.xml" />
      </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="ReadOnlyXmlRoleProvider">
      <providers>
        <add name="ReadOnlyXmlRoleProvider"
          type="ReadOnlyXmlRoleProvider, ReadOnlyXmlRoleProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73"
          description="Read-only XML role provider"
          xmlFileName="~/App_Data/MyUsers.xml" />
      </providers>
    </roleManager>
    <!--/added on 05/31/2011 -->
  • Here's the XML that you need to add to the <system.web> section of your SharePoint Central Administration web.config file; on my server that file is located at "C:\inetpub\wwwroot\wss\VirtualDirectories\6087\web.config":
    <!-- added on 05/31/2011 -->
    <membership defaultProvider="ReadOnlyXmlMembershipProvider">
      <providers>
        <add name="ReadOnlyXmlMembershipProvider"
          type="ReadOnlyXmlMembershipProvider, ReadOnlyXmlMembershipProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=426f62526f636b73"
          description="Read-only XML membership provider"
          xmlFileName="~/App_Data/MyUsers.xml" />
      </providers>
    </membership>
    <!--/added on 05/31/2011 -->

IMPORTANT!!!

SharePoint Central Administration needs to be able to find the MyUsers.xml file, so I created an App_Data folder under physical path of the SharePoint Central Administration website, and I added a symbolic link in that folder that pointed to the physical MyUsers.xml file. Here's how I did that:

  1. I opened a command prompt.
  2. I changed directory to the path where my SharePoint global web.config file was located; for example:
    cd C:\inetpub\wwwroot\wss\VirtualDirectories\6087
  3. I created a symbolic link to the physical path of the XML file; for example:
    mklink MyUsers.xml C:\Inetpub\SharePointSite\wwwroot\App_Data\MyUsers.xml
  4. I closed the command prompt.

Note: I could have copied the XML file, but I preferred to use the symbolic link instead of having to manage two copies of the file.

Optional People Picker Settings

If you were installing a membership provider that can perform lookups, you could add an additional entry to your SharePoint Central Administration web.config file:

<PeoplePickerWildcards>
  <clear />
  <add key="AspNetSqlMembershipProvider" value="%" />
  <!-- added on 05/31/2011 -->
  <add key="ReadOnlyXmlMembershipProvider" value="%" />
  <!--/added on 05/31/2011 -->
</PeoplePickerWildcards>

Problems that I Encountered

Okay - I admit that I everything that I did so far was probably making things harder that they needed to be, but I love a good challenge. ;-]

That being said, I ran into some problems that I thought would be worth mentioning, just in case someone else ran into them.

HTTP 403 Errors

When browsing to my SharePoint website, I received several HTTP 403 errors. I used Process Monitor to troubleshoot the problem, and I discovered that IUSR could not access the "bin" folder in my website. (I'm still not quite sure why it was trying.) To resolve these errors, I used the following steps:

  1. I opened a command prompt.
  2. I changed directory to the SharePoint website's path; for example:
    cd C:\Inetpub\SharePointSite\wwwroot
  3. I changed permissions for the "bin" folder; for example:
    icacls bin /grant IIS_IUSRS:r
  4. I closed the command prompt.

In my situation the problem was for IUSR, but if you are using a different anonymous identity or your application pool is running as a unique identity then it might be a different user. In any case, Process Monitor will let you know who needs permissions.

Later I discovered the following blog post by John Powell:

http://blogs.msdn.com/b/johnwpowell/archive/2008/05/23/sharepoint-intermittent-403-forbidden-errors.aspx

In that blog, John suggests adding the following permissions for the "bin" folder:

icacls bin /grant users:r

I'm not sure if that's necessary, but it's worth pointing out.

HTTP 404.8 Errors

When browsing to my SharePoint website, I received several HTTP 404.8 errors. Those errors mean that the built-in IIS 7 Request Filtering feature was blocking something, so I did the following:

  • I opened my website's web.config file; on my server that file was located at "C:\Inetpub\SharePointSite\wwwroot\web.config":
  • I added the following XML before the closing </configuration> tag:
    <system.webServer>
      <!-- added on 05/31/2011 -->
      <security>
        <requestFiltering>
          <hiddenSegments>
           <clear />
           <add segment="web.config" />
           <add segment="bin" />
           <add segment="App_code" />
           <add segment="App_GlobalResources" />
           <add segment="App_LocalResources" />
           <add segment="App_WebReferences" />
           <add segment="App_Data" />
           <add segment="App_Browsers" />       
          </hiddenSegments>
        </requestFiltering>
      </security>
    <!--/added on 05/31/2011 -->
    </system.webServer>
  • I saved and closed the web.config file.

Note: This removes all of the hidden segments from the global IIS 7 Request Filtering settings, which may be overkill. I have a lot of custom global request filtering settings, and I didn't want to go through each individual setting to see which setting was blocking files that I needed, so I used settings for my website that cleared the inherited request filtering settings and added the default settings.

Annoying Message: "The Web site wants to run the following add-on: 'Name ActiveX Control'"

When browsing to my SharePoint website, the information bar in Internet Explorer kept prompting me with the following message:

The Web site wants to run the following add-on: 'Name ActiveX Control' from 'Microsoft Corporation'. If you trust the Web site and the add-on and want to allow it to run, click here...

This message was highly frustrating, so I did some digging around the Internet and discovered that I could hack the INIT.JS file for SharePoint to suppress this message. Here's how I did that:

  • I opened my server's INIT.JS file; on my server that file was located at "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\1033\INIT.JS".
  • I located the ProcessImn() and ProcessImnMarkers() functions, and I remarked out the contents. Here's what this looked like when I was done:
    function ProcessImn()
    {
    // if (EnsureIMNControl() && IMNControlObj.PresenceEnabled)
    // {
    // imnElems=document.getElementsByName("imnmark");
    // imnElemsCount=imnElems.length;
    // ProcessImnMarkers();
    // }
    }
    function ProcessImnMarkers()
    {
    // for (i=0;i<imnMarkerBatchSize;++i)
    // {
    // if (imnCount==imnElemsCount)
    // return;
    // IMNRC(imnElems[imnCount].sip,imnElems[imnCount]);
    // imnCount++;
    // }
    // setTimeout("ProcessImnMarkers()",imnMarkerBatchDelay);
    }
  • I saved and closed the INIT.JS file.

I should note that this solution is unsupported; and a few months I hacked my INIT.JS file, Microsoft published the following Knowledge Base article with a couple of different methods:

KB 931509: Message in the Information bar in Internet Explorer 7 when you browse to a Windows SharePoint Services 3.0 site or to a SharePoint Server 2007 site: "The Web site wants to run the following add-on: 'Name ActiveX Control'"

That being said, I like my solution better. ;-]


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

Cascading Style Sheet (CSS) Color Negatizing Script

The Customer Scenario

I ran into an interesting situation recently - I host a website for a friend of mine, and he was shopping around for a new website template. He found one that he liked, but he didn't like the colors. In fact, he wanted the exact opposite of the colors in the website template, so he asked what I could do about it.

I looked at the website template, and thankfully it was using linked Cascading Style Sheets (CSS) files for all of the color definitions, so I told him that changing the colors would probably be a pretty easy thing to do. However, once I cracked open the CSS files from the website template, I found that they had hundreds of color definitions. Changing every color definition by hand would have taken hours, so I decided to write some Windows Script Host (WSH) code to do the work for me. ;-]

Negatizing a CSS File

With the above scenario in mind, here's the script that I wrote to negatize every color in a CSS file - all that you need to do is replace the paths to the input and output files and run the script to create the negatized version of the input CSS file.

Option Explicit

Const strInputFile = "c:\inetpub\wwwroot\style-dark.css"
Const strOutputFile = "c:\inetpub\wwwroot\style-light.css"

' ------------------------------------------------------------

Dim objFSO
Dim objInputFile
Dim objOutputFile
Dim strInputLine
Dim strLeft, strMid, strRight, strArray
Dim blnFound

' ------------------------------------------------------------

Const strTempRGB = "[|[TMPRGBSTR1NG]|]"
Const strTempHEX = "[|[TMPHEXSTR1NG]|]"

' ------------------------------------------------------------

Set objFSO = CreateObject("scripting.filesystemobject")
Set objInputFile = objFSO.OpenTextFile(strInputFile)
Set objOutputFile = objFSO.CreateTextFile(strOutputFile)

Do While Not objInputFile.AtEndOfStream
    strInputLine = objInputFile.ReadLine
    blnFound = True
    
    Do While blnFound
        If InStr(1,strInputLine,"rgb(",vbTextCompare) Then
            strLeft = Left(strInputLine,InStr(1,strInputLine,"rgb(",vbTextCompare)-1)
            strMid = Mid(strInputLine,InStr(1,strInputLine,"rgb(",vbTextCompare)+4)
            strRight = Mid(strMid,InStr(strMid,")")+1)
            strMid = Left(strMid,InStr(strMid,")")-1)
            strArray  = Split(strMid,",")
            strMid = InvertOctet(CInt(strArray(0))) & _
                "," & InvertOctet(CInt(strArray(1))) & _
                "," & InvertOctet(CInt(strArray(2)))
            strInputLine = strLeft & strTempRGB & "(" & strMid & ")" & strRight
        Else
            blnFound = False
        End If
    Loop
    
    strInputLine = Replace(strInputLine,strTempRGB,"rgb")
    
    blnFound = True

    Do While blnFound
        If InStr(strInputLine,"#") Then
            strLeft = Left(strInputLine,InStr(strInputLine,"#")-1)
            strMid = Mid(strInputLine,InStr(strInputLine,"#")+1)
            If Len(strMid)>6 Then
                strRight = Mid(strMid,7)
                strMid = Left(strMid,6)
            ElseIf Len(strMid)>3 Then
                strRight = Mid(strMid,4)
                strMid = Left(strMid,3)
            Else
                strRight = ""
            End If
            
            If IsHexString(strMid) Then            
                If Len(strMid) = 6 Then
                    strMid = Right("0" & Hex(InvertOctet(CInt("&h" & Left(strMid,2)))),2) & _
                        Right("0" & Hex(InvertOctet(CInt("&h" & Mid(strMid,3,2)))),2) & _
                        Right("0" & Hex(InvertOctet(CInt("&h" & Right(strMid,2)))),2)
                Else
                    strMid = Hex(InvertByte(CInt("&h" & Left(strMid,2)))) & _
                        Hex(InvertByte(CInt("&h" & Mid(strMid,3,2)))) & _
                        Hex(InvertByte(CInt("&h" & Right(strMid,2))))
                End If
            End If

            strInputLine = strLeft & strTempHEX & strMid & strRight
        Else
            blnFound = False
        End If
    Loop
    
    strInputLine = Replace(strInputLine,strTempHEX,"#")
    
    objOutputFile.WriteLine strInputLine
Loop


' ------------------------------------------------------------

Function IsHexString(ByVal tmpString)
    Dim blnHexString, intHexCount, intHexByte
    blnHexString = True
    If Len(tmpString)<>3 and Len(tmpString)<>6 Then
        blnHexString = False
    Else
        tmpString = UCase(tmpString)
        For intHexCount = 1 To Len(tmpString)
            intHexByte = Asc(Mid(tmpString,intHexCount,1))
            If (intHexByte < 48 Or intHexByte > 57) And (intHexByte < 65 Or intHexByte > 70) Then
                blnHexString = False
            End If
        Next
    End If
    IsHexString = blnHexString
End Function

' ------------------------------------------------------------

Function InvertByte(ByVal tmpByte)
    tmpByte = tmpByte And 15
    tmpByte = 15 - tmpByte
    InvertByte = tmpByte
End Function

' ------------------------------------------------------------

Function InvertOctet(ByVal tmpOctet)
    tmpOctet = tmpOctet And 255
    tmpOctet = 255 - tmpOctet
    InvertOctet = tmpOctet
End Function

' ------------------------------------------------------------

Negatizing a SharePoint 2007 Theme

After I wrote the above script, I found myself using it for a bunch of different websites that I manage for other people. One of the websites that I host is based on SharePoint 2007, so I wondered how difficult it would be negatize a SharePoint 2007 theme. As it turns out, it's pretty easy. The following steps will walk you through the steps that are required to create a negatized version of the built-in "Classic" SharePoint 2007 theme.

(NOTE: The steps in this section do not work with SharePoint 2010 or office 14; SharePoint 2010 and Office 14 store their themes in a different format, so these steps will not work.)

  1. Copy the folder:
    "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSIC"
    To the following folder:
    "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSICNEGATIVE"
  2. Rename the file:
    "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSICNEGATIVE\CLASSIC.INF"
    To the following:
    "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSICNEGATIVE\CLASSICNEGATIVE.INF"
  3. Open the following file:
    "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSIC\CLASSICNEGATIVE.INF"
    • Replace all instances of "Classic" with "Classic Negative".
    • Save and close the INF file.
  4. Open the following file:
    "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\1033\SPTHEMES.XML"
    • Add the following entry to the <SPThemes> collection:
      <Templates>
      <TemplateID>classicnegative</TemplateID>
      <DisplayName>Classic Negative</DisplayName>
      <Description>Classic Negative</Description>
      <Thumbnail>images/thclassic.gif</Thumbnail>
      <Preview>images/thclassic.gif</Preview>
      </Templates>
    • Save and close the XML file.
  5. Edit the color negatizing WSH script from earlier in this blog for each of the following files and run it:
    • theme.css
      • Input File:
        "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSIC\theme.css"
      • Output File:
        "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSICNEGATIVE\theme.css"
    • mossExtension.css
      • Input File:
        "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSIC\mossExtension.css"
      • Output File:
        "%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\THEMES\CLASSICNEGATIVE\mossExtension.css"

That's all it takes to negate the colors that are defined in the CSS files for a SharePoint 2007 theme. (NOTE: This does not modify the colors of the images in the SharePoint theme; you will need a graphics program to update the colors in the images.)

Closing Thought

Before I receive any comments, I am perfectly aware that "negatize" is not an actual word in the English language, but it seemed appropriate, and new words have to start somewhere. ;-]


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

WordPerfect versus Word

A friend of mine just sent me the following news article, along with the subtitle, "Can we just let this die, geez..."

Novell Antitrust Lawsuit Against Microsoft Revived by Court
Bloomberg Businessweek - May 03, 2011
By Tom Schoenberg

Personally, I find articles like this depressing - not just because they are frivolous lawsuits that do little more than wasting millions of dollars for everyone concerned, but because they send the wrong messages to the business world. Let me explain:

I love quotes that are worded like this: "WordPerfect's share of the word-processing market fell to less than 10 percent in 1996 from almost 50 percent in 1990." This statement is an excerpt from a section in that article which suggests that Microsoft is the bad guy in this situation.

Has anyone ever bothered to consider that whatever happened to WordPerfect occurred because the executive leadership at WordPerfect made a plethora of poor business choices and their applications ceased to be good products? This entire lawsuit reminds me of when Metallica sued Napster over the decline in their album sales - did it ever occur to them (Metallica) that maybe they had passed their prime and perhaps no one wanted to buy their albums anymore?

Here's another question: did anyone else actually try to use WordPerfect for Windows 3.x through Windows 98? Well, I did - because back in my DOS days I was an avid WordPerfect 4.x through 6.x user. So take my word for it, every version of WordPerfect starting from 5.x through 8.x on Windows platforms were simply awful, while at the same time the versions of Word for Windows got better and better.

I can give you several reasons behind this dichotomy, but the primary cause is simple - WordPerfect didn't have a clue how to make a Windows-based product. As the world transitioned from a DOS-based environment to a Windows realm, WordPerfect shipped products that were technologically inferior, way behind schedule, and badly engineered. By the time that the folks at WordPerfect quit wasting money and figured out what they were doing, it was way too late - they owned less than 10% of the market, and the damage was irrevocable.

Here's just one example: instead of leveraging Windows' built-in printing capabilities and investing in better application features and functionality, the people at WordPerfect continued to develop and ship their own printing subsystem, which bypassed the built-in Windows printing features. Even if WordPerfect's alternate printing subsystem had been better, (which I can honestly say from personal experience that it was not), that's not the way that you're supposed to do things in a Windows world, and WordPerfect threw away millions of dollars and countless thousands of man hours on this colossal failure.

Here's another oldie but goodie - WordPerfect bought into the fantasy from the now-defunct Sun Microsystems that Java was the up-and-coming, be-all/end-all of computer languages and the dawn of write-once/run-everywhere software. This was a wonderful theory, and I personally spent some time writing simple applications in Java back in the mid-to-late-1990s because I, too, bought into Sun's hype. (I still wear a Java baseball cap that I got from Sun back in 1996.) But it wasn't long before I, like many others, realized that Java was mostly hype, and writing software in Java was an experience that was more like rewrite-often/debug-everywhere. But I realized my mistake before I had wasted over $400 million on a failed word processing application in Java like WordPerfect did.

But the folks at WordPerfect continued to press on in their self-delusions - all the while falling behind Word, which was now integrating wonderfully with Windows, Microsoft Office, and a host of other applications through technologies like DDE, OLE, and ActiveX. By this point WordPerfect's losses were enormous, then along came Novell, who was already a sinking ship; this was due to the fact that their difficult-to-use and expensive flagship NetWare operating system was taking a serious beating from Windows NT's ease-of-use and significantly reduced barrier-to-entry pricing.

Novell realized that WordPerfect had once been a major cash cow, and I guess they hoped that they could turn around both of these massive sinking ships and get them headed back from the Red Sea into the Black Sea. But Novell's delusions proved to be worse than WordPerfect's, and eventually Novell had to sell WordPerfect to Corel for a pittance just to keep their ship from being dragged under as WordPerfect rocketed toward the bottom in a technology fate that was worse than the demise of the Titanic. And yet, very much like the sinking of the Titanic and the untimely deaths of technology giants like Netscape and Sun Microsystems, WordPerfect's downfall was ultimately caused by a series of gargantuan blunders and the terminal hubris of their leadership, and not by any action on Microsoft's part.

Not that any of this will matter in court - Microsoft will probably still have to shell out a few hundred million dollars in "damages," thereby rewarding former executives at WordPerfect for their incompetence, and reinforcing the message to the business world that just because you're a colossal failure and you ruined the lives of thousands of your loyal employees, that doesn't mean that you shouldn't be able to buy a large mansion and luxury yacht by cashing in on the profits of your successful competitors.


Additional Reading

At the time of this writing, Wikipedia has a great write-up on the history of WordPerfect, including blunt analysis of WordPerfect's many failures. But pages on Wikipedia are subject to change, and they're not always accurate.

With that in mind, you might want to take a look at the book titled Almost Perfect by W. E. Peterson, who had been one of the senior executives at WordPerfect. Sometimes it's nice to have an insider's view of the breakdown and failure.

Bad Characters to Use in Web-based Filenames

My good friend Wade Hilmo recently posted an excellent blog titled "How IIS blocks characters in URLs" that discusses some of the internal workings for how IIS deals with several characters in file names that do not work well in URLs. Wade’s blog does a great job explaining all of the internal IIS URL parsing logic in detail, and his post reminded me of some related notes that I had published on an internal website at Microsoft. As a complement to Wade’s outstanding blog post, I’m reposting my notes in this blog.

Recently a Microsoft Technical Account Manager (TAM) mentioned that he was working on an issue with a customer that was using SharePoint 2007 on IIS 7. The customer's problem was this: his company had several Word documents that were stored in SharePoint that had the plus sign (+) in the filenames, and HTTP requests for these documents were failing. The TAM configured IIS 7's request filtering feature to allow doubly-escaped characters by setting the allowDoubleEscaping attribute to true. This seemed to alleviate the problem, but I had to point out that this probably wasn't the right thing to do. As a general rule, I don't like changing many of the default configuration options for the IIS 7 request filtering feature, because they are designed to keep my servers safe. But in this specific scenario, modifying those settings is simply looking in the wrong place.

Let me explain:

There are several characters that are perfectly valid in a Windows filename that are really bad when you post those files on websites, and either the server or the client could wreak havoc with them. In most scenarios the HTTP requests will receive an HTTP 404 File Not Found error, but in some cases that might cause an HTTP 400 Bad Request error. As such, even though you might find a way to work around the problem, it's a really bad idea to use those characters when you are posting files to a website.

RFC 2396 is the governing document for "Uniform Resource Identifiers (URI): Generic Syntax." This RFC defines what can and can't be used in a URI, as well as what shouldn't be used.

First, section "2.2. Reserved Characters" contains the following list of reserved characters:

reserved = ";" | "/" | "?" | ":" | "@" |
           "&" | "=" | "+" | "$" | ","

Second, section "2.4.3. Excluded US-ASCII Characters" contains the following lists of delimiter and unwise characters:

delims = "<" | ">" | "#" | "%" | <">

unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"

Several of the characters in those lists cannot be used in Windows filenames, but the remaining characters should not be used for filenames if you intend to upload those files to a website.

Here are my explanations for why some of those characters will cause problems if you attempt to use them in filenames that you upload to a website:

  • Plus Sign (+) - this character is often translated as a URI-encoded space, so the URI "http://localhost/foo+bar.doc" could be misinterpreted as the URI "http://localhost/foo bar.doc".
  • Percent Sign (%) - this character is used for URI escaping, and I've seen this cause a lot of problems because the two characters that follow the percent sign are assumed to be hex digits for an escaped ASCII code, so the URI "http://localhost/foo%bar.doc" could be misinterpreted as the URI "http://localhost/fooºr.doc".
  • Number/Pound Sign (#) - this character is used to delimit a URI from a fragment identifier (aka bookmarks), so the URI "http://localhost/foo#bar.doc" could be misinterpreted as the URI "http://localhost/foo" with a bookmark of "bar.doc".

So once again, just because you might be able to get this to work on your server doesn't mean that you should be using a character in a web-based filename that's reserved for something else. It's like building an atomic bomb - just because you can doesn't mean that you should. Your best answer in this scenario is to rename your files to something else.

;-]


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

Happy 40th Birthday FTP!

Following on the heels of Clive Webster's article that FTP is 40 years old, let me be the next person to wish FTP a wonderful 40th birthday!

Happy Birthday FTP!

Yeah, that was kind of silly, wasn't it...? ;-]

Farewell to Zune? (Or not?)

I'm a little slow on this bit of news, but I just stumbled across the Goodbye from Seattle: Microsoft ending Zune device article from a month ago on GeekWire. This was really bad news for me - I own several Zune devices, so I would be extremely sad to see them go; personally I think that Zunes are great media devices that have a lot of potential. Given the existence of a large body of anti-Apple users, which has led to the creation of websites like anythingbutipod.com, there are a lot of people that don't want to settle for an iPod.

As I was lamenting the untimely demise of my favorite media player, I came across the Zune Is Not Dead article on anythingbutipod.com, which was published the day after the GeekWire article was published. The anythingbutipod.com article contained a statement from Dave McLauchlan, who is the Senior Business Development Manager for Zune, and he stated rather emphatically that the Zune is not dead.

So this leaves me a bit confused, at least for the moment; I'm not sure what to think about the future for Zune devices.

FWIW - I have two Zunes, a Zune 120 (black) and a Zune 30 (red), both of which have been great devices for me. I use my Zune 30 every day during my commute to listen to audiobooks, and I use my Zune 120 when I'm traveling in order to listen to music and watch movies.


In addition to my Zunes, my wife, my son, and my daughter all use a Zune. (I also own an HTC HD7 that is running Zune on Windows Phone 7, but that's another story.)

All that being said, the Zune never reached a critical mass, and I could easily make a wish list of features that I think would help the Zune in the long run. Here are just a couple:

  • Transferring files to and from a Zune should not require using the Zune software.
    This is an annoying limitation, and I realize that it's the same model that is used by the iPod with iTunes. But Microsoft already makes Windows Media Player, and you can use that to transfer files to/from third-party players, so I find it somewhat limiting that you have to use the Zune software.
  • The Zune should show up as a removable device in Windows Explorer like most third-party media players.
    This is somewhat related to the previous comment, but here's an example: my dad and my aunt both own media players from Creative, and it's great that their media players show up as removable devices in Windows Explorer when you plug them in. There's an old adage that says, "You can't teach an old dog new tricks," and it's great when you don't need to try. My dad is in his early 70's and my aunt is in her early 80's, so it's great that they can stick with the simple drag & drop or copy & paste functionality in Windows when they want to update the media on their players. (For that matter, I wrote a batch file that my dad uses when he adds MP3 files to his media player that automatically updates the metadata; all he has to do is run the batch file when he adds new files to his media player and everything is up-to-date. You can't do that on an iPod or a Zune.)

Anyway, that's my $.02 on the subject. I love my Zunes, and I hope that Microsoft decides to keep them around.

Credential Caching in FTP 7.0 and FTP 7.5

I've seen a few situations where people that are using the FTP 7.0 and FTP 7.5 service have noticed that it takes a while for their password changes to be reflected by the FTP service. To put this another way, here are the typical symptoms that people describe to me:

  • A user successfully logs into their FTP site using their username and password
  • The user logs out of their FTP site
  • The user changes their password
  • The user attempts to log into their FTP site using their username and their new password, but this fails
  • The user can successfully log into their FTP site using their username and their old password
  • (Note: As a corollary, restarting the FTP service fixes the symptoms)

Here's why this happens: to help improve the performance for authentication requests, the FTP service caches the credentials for successful logins. (The cache duration is set to 15 minutes by default.) This means that if you change your password, your changes may not be reflected for the cache duration.

The good news is, the FTP credential cache settings can be changed easily, and I have documented all of the settings for FTP caching in the IIS configuration reference at the following URLs:

Quoting and paraphrasing the above documentation, there are the two settings that you can configure on the <credentialsCache> element:

AttributeDescription
enabled Optional Boolean attribute.

true if credential caching is enabled; otherwise, false.

The default value is true.
flushInterval Optional uint attribute.

Specifies the cache lifetime, in seconds, for credentials that are stored in the cache.

Note: This value must be between 5 and 604,800 seconds.

The default value is 900.

What this means to you is - you can completely disable credential caching, or you can specify a different timeout. For example, on several of my development servers I often disable credential caching; this allows me to change passwords whenever I want, which is very useful when I am creating custom authentication providers. For my production servers I tend to stick with the default values, although I might change those values when I'm troubleshooting a problem.

I usually configure the settings from a command line or a batch file, although the articles that I listed earlier have steps for using the IIS Manager to change the settings for FTP credential caching. Just the same, here are some examples for setting the values by using appcmd.exe:

How to Disable FTP Credential Caching

cd /d "%SystemRoot%\System32\Inetsrv"
appcmd.exe set config -section:system.ftpServer/caching /credentialsCache.enabled:"False" /commit:apphost
net stop FTPSVC
net start FTPSVC

How to Specify a Custom Timeout for FTP Credential Caching

cd /d "%SystemRoot%\System32\Inetsrv"
appcmd.exe set config -section:system.ftpServer/caching /credentialsCache.enabled:"True" /commit:apphost
appcmd.exe set config -section:system.ftpServer/caching /credentialsCache.flushInterval:"300" /commit:apphost
net stop FTPSVC
net start FTPSVC

I hope this helps. ;-]


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

Why I Won't Buy Another HP Computer

First of all, I have to point out that I have a few friends that work for Hewlett-Packard, so I have to apologize up front for what I'm about to write in this blog. But I just had such a horrible customer support experience with HP that I won't buy from them again.

Why I Bought an HP Computer

I have nothing against HP computers; for several years I used two beefy dual-CPU HP/Compaq ProLiant servers for my web hosting machines. (I loved those computers, and I only replaced those when Windows Server 2008 was released and I thought that it was time to upgrade my servers.)

Recently I decided to replace my aging Dell desktop computer with a newer model. I'm quite partial to Dell computers, because I've always had great experiences with their computers and their company. I had a chance to buy a refurbished HP P6510F computer for a great price, so I decided to take a chance with HP since that particular computer model had a lot of great reviews.

When the computer arrived I did what I always do - I reformatted the hard drive and I installed a brand new copy of Windows from scratch. (I have to do this because all computer companies - HP, Dell, Gateway, etc. - install a bunch of useless garbage software whenever you buy one of their new computers.) The computer ran fine for several weeks, but I'm a person that likes to keep their computer up-to-date, so this past weekend I browsed to HP's website to see if there were any updates.

Upgrading the BIOS

As it turns out, there was a new version of their BIOS that was supposed to resolve issues when waking the computer from sleep mode if you have more than 4GB of memory. I only had 4GB of RAM in the computer, but I was already shopping for another 4GB, so it seemed prudent to install the BIOS update. I downloaded the update and ran their installer. After a couple of minutes a dialog box popped up saying that the update had applied successfully and I needed to reboot my computer, which I did.

That's when everything started to go wrong.

All Heck Breaks Loose

When my computer restarted it immediately hit the infamous Blue Screen of Death (BSOD); something very much like the following illustration:

A problem has been detected and Windows has been shut down to
prevent damage to your computer.

If this is the first time you've seen this Stop error screen,
restart your computer. If this screen appears again, follow
these steps:

Check for viruses on your computer. Remove any newly installed
hard drives or hard drive controllers. Check your hard drive
to make sure it is properly configured and terminated.
Run CHKDSK /F to check for hard drive corruption, and then
restart your computer.

Technical information:

*** STOP: 0x0000007B (0xFFFFF880009A9928,0xFFFFFFFFC0000034,
0x0000000000000000,0x0000000000000000)

It didn't matter how many times I tried to reboot, I still got the BSOD. I knew that BIOS updates changed some of the settings, so my natural suspicion was to assume that something in the new BIOS settings was causing the problem. I tweaked a few settings like disabling hardware virtualization and such - but there was still no joy in Mudville. After this I started to assume that perhaps the BIOS updated hadn't actually applied successfully, so I started trying to see if I could get my computer to boot from one of my several WinPE-based utility CD-ROMs and reapply the patch, but all of those also fell victim to the vicious BSOD.

I'll spare you the details of everything else that I tried - both hardware and software - but I finally gave up and decided to call HP's 24x7 technical support number.

The Technical Support Nightmare Begins

For geeks like me, having to call technical support is humiliating enough, but it's made so much worse by having to deal with front-line technical support people. Having spent 10 years in technical support myself, I have a great deal of patience with technical support engineers, but it can still be an aggravating experience. I spent the next half-hour answering mundane questions and following every instruction from HP's Tier 1 technical support script - all of which I had tried before. (At least the parts that actually applied to my situation.) I'm sure that the engineer with whom I was working meant well, but it was clear that she was floundering.

After a while she began to tell me that I didn't need the BIOS patch and that this was all my fault, to which I replied that she was correct - I didn't actually need the BIOS patch right now, but I would need it in the future, but that didn't really matter - the BIOS patch should not cause the BSOD. Besides - I always updated the BIOS in my Dell computers with no problems. (There's a good jab at HP to try yourself sometime.) Then she started to tell me that since I had a different version of Windows than HP had installed on my computer, the BIOS patch was not compatible. I asked her incredulously, "Do you mean to tell me that HP expects their customers to never install a new version of Windows?" She hesitated before replying "No," and then I reiterated my earlier assertion that no matter what, the BIOS patch should not cause the BSOD.

Then she began to tell me that I needed to purchase a system restore DVD from HP to rebuild my system. I was quick to point out that doing so would reformat my hard drive - thereby erasing all of my files - and that I was willing to bet that the problem wouldn't go away since the system restore DVD was probably not going to reset the BIOS back to an earlier version. So in my estimation I would be wasting my money and my time on a suggestion that would ultimately achieve nothing. This is where I lost her - she had no idea what I meant; so after more than an hour of basic troubleshooting with Tier 1 support and lots of time spent on hold, my patience was finally gone, and I asked to speak with someone in HP's Tier 2 support.

The Technical Support Nightmare Continues

I was transferred to a guy in Tier 2 support who discussed my predicament with me, and he seemed to have a much better handle on things. One of the first things that he did was verify that there was no reason that the BIOS update shouldn't work with my version of Windows, to which I replied that I had been trying to tell the earlier engineer the same thing. We looked at several settings, but the problem persisted, and then he suggested that I needed to purchase a system restore DVD from HP to rebuild my system. I restated my earlier claim that I would be wasting my money and my time since I was 99.9% sure that the system restore DVD would not roll back the BIOS version, so he put me on hold while he checked on that.

When he came back he informed me that the system restore DVD would not roll back the BIOS version, so I needed to return the computer to HP in order for them to reset the computer's BIOS to the original factory version. He pointed out that this would be free since the computer was under warranty, and he took my address so HP could send me a box in order to send the computer back to HP for repairs. Once all that was taken care of, we hung up.

My total time on the phone was about two hours. Ugh.

Problem Resolved

The next day I went out to lunch with my good friend, Wade Hilmo, and I related my experience to him. Once I described the symptoms he said, "I'll bet the BIOS update changed the mode for your SATA controller. Switch it from IDE to AHCI or vice-versa and the problem should go away."

Darn. I should have thought of that. ;-]

Sure enough, when I got home that night and I pulled up my BIOS settings, the SATA mode was set to RAID; I switched it to IDE and the BSOD went away. Once I knew what the problem was I found the following Microsoft Knowledge Base article that allowed me to enable AHCI:

Error message when you start a Windows 7 or Windows Vista-based computer after you change the SATA mode of the boot drive: "STOP 0x0000007B INACCESSABLE_BOOT_DEVICE"

http://support.microsoft.com/kb/922976

My thanks to Wade for pointing that out, but Wade's follow-up comment was apropos, "I'm still a bit surprised that neither of the HP folks suggested it." So I decided that I should call HP and let them know what it took to fix the problem.

Back to Technical Support

The next day I called HP Customer Care to have them cancel my open work ticket, which was the polite thing to do since the problem was resolved. Having taken care of that, I thought that I'd give their technical support people the details of what caused the issue and how to fix it. Having worked in technical support, I always liked to know what it took to resolve an issue.

This seemed like such a good idea at the time, but it didn't turn out that way. When I called HP's Customer Care folks transferred the call to their technical support people, one of their idiots support engineers put me on hold for 20-30 minutes while he read the case notes.

Are you kidding me? It doesn't take 20-30 minutes to read the case notes, even if you're in your first year of Hooked on Phonics.

Once he took me off hold, I was pleading with him to listen to my explanation that the problem was already resolved and it was not caused by whatever stupid idea kept popping out of his wild imagination - I just wanted to share the details of how to resolve the issue if another customer calls in with the same problem, which is undoubtedly going to happen. I pointed out that I was trying to help HIM, for Pete's sake, and he just wouldn't listen. (I started hoping that HP was recording the call.)

After all that, I made it abundantly pretty clear that what he did was very unprofessional, and I asked to speak to a manager. He informed me that he'd see if a manager was available - then he put me back on hold. Fortunately I was calling from work where I have a headset for my telephone, this way I could keep working while I was on hold. (Otherwise this would have really aggravated me.)

After another 20-30 minutes I realized that this idiot engineer was not going to find a manager, he was waiting for me to hang up and go away. So I decided to put that call on hold and try to call back into technical support, but my @#$% LG-Nortel phone won't let me call a phone number if I already have that number on hold. Argh. While I was browsing HP's website to see if I could locate a different phone number for technical support I accidently hung up the original call.

Crap, crap, crap.

So I called HP again and I got another engineer - and I asked to speak to a manager right off the bat. I profusely apologized to the new engineer, and I stated emphatically that it was nothing that he did. He asked for my name and such, but I told him that I had a support ticket number and I gave him that instead. Then I started to explain what happened with the other idiot and how I resolved the issue, but this new engineer attempted to defend the earlier idiot engineer and started to change the subject. I politely cut him off and simply pointed out that the first guy took 30 minutes to read the case notes, whereas he took less than 30 seconds - even this guy had to admit that the first guy's behavior was uncalled for.

Cutting the rest of the story short, I did finally tell the new engineer what it took to fix the problem, which was simply resetting the SATA configuration back to the pre-update BIOS value. I also gave him the information about how to enable AHCI using Microsoft's KB 922976. He thanked me for the information, and after he tried unsuccessfully to upsell me on a new warranty for my computer we ended the call.

Closing Remarks

So there you have it - a thoroughly bad HP customer support experience. If either Hewlett or Packard somehow manage to read this blog, they should be ashamed on behalf of their employees. I'd give you the names of those employees, but no one that I talked to had a name that I could pronounce.

 

Of course, I never did get to speak to a manager at HP.

IIS 6: Setting up SSL - Part 3: Installing the Certificate

In part three of my series on setting up SSL on IIS 6, I'll describe the steps that are necessary to install an SSL certificate. Simply out of convenience I broke this process into two sections:


Installing Your Certificate

  1. Bring up the properties for a website:

  2. Switch to the "Directory Security" tab and click "Server Certificate:"

  3. Click "Next" to bypass the first page:

  4. Choose to process the request and click "Next":

  5. Click "Browse" to the locate your certificate request:

  6. Browse to the location of your certificate, highlight it, and click "Open":

  7. Verify the location of your certificate and click "Next":

  8. Choose your SSL port and click "Next":

  9. Review the information to make sure it is correct and click "Next":

  10. Click "Finish" to close the wizard:

  11. Notice that you now have all the buttons available for SSL.


Verifying Your Certificate

  1. Click the "View Certificate" button:

  2. On the "General" tab, if the certificate is good you will see a normal certificate icon. (If no, you will see a warning or error icon.)

  3. On the "Certification Path" tab you will see your certificate hierarchy:

That wraps it up for creating, submitting, obtaining, and installing a certificate. In subsequent blogs I'll post some appendices with instructions about setting up Certificate Services on Windows Server 2003.


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