Configure IIS 7

(Also see tips for IIS Hardening.)


Enable CGI Role Service

  • On Windows Server 2008 R2, you do this by going to "Server Manager" -> "Roles" -> "Add Role Services".
  • On Windows Vista, you can access a similar list of features from "Control Panel"->"Programs and Features"->"Turn Windows features on or off"->"Internet Information Services->"World Wide Web Services"->"Application Development Features".

Once you have CGI role enabled, you can start up the "Internet Information Services (IIS) Manager".

Install the update for FastCGI module


The update for IIS 7.0 FastCGI module fixes several known compatibility issues with popular PHP applications. Install the update from one of the following locations:

Install Administration Pack

The Administration Pack for IIS 7.0 is needed to configure FastCGI settings.

Enabling FastCGI Applications

 This requires adding only a Handler Mapping. Select the “Add Module Mapping” action, and specify the configuration settings as below:

 The request path could be *.exe or even *.abc, but it will have to map to a .DLL, .OT, or .EXE file.

Enabling CGI Applications

If you have a lot of CGI functionality, you may still have some CGI applications. These can run alongside FastCGI applications but are enabled in a different way.

First the handler mappings must be enabled for ISAPI and CGI.

Then you must specify which applications can be run as CGI applications. This connects the file extensions with the application.

So now you might wonder, what will happen if I configure *.EXE to run CGI applications and MyFCGIApp.EXE to run a FastCGI application?
The answer is that the table of mappings begins with specific applications first and then wildcard mappings, so MyFCGIApp.EXE will be run.

Application Pool

If you are running the 64-bit version of Windows 2008, ensure that your application pool is configured to run as 32-bit. 
  1. From the Windows 'Start' menu, pick Run, and type "inetmgr" (without the quotes). Click on 'OK".
  2. In the left hand pane of IIS Manager, open the settings for your server. Click on "Application Pools".
  3. In the Application Pools page, select "DefaultAppPool".
  4. In the right hand pane, under "Edit Application Pool", click on "Advance Settings..."
  5. In the Advanced Settings dialog, ensure that "Enable 32-bit Applications" is set to "True".

Setting up CGI

  1. Choose Start - Run - inetmgr - Sites - Default Web Site - Handler Mappings
  2. (From Actions, on the right) Add Module Mapping - *.cgi - CgiModule - <blank> - dot cgi
  3. (From Tree, on the left) <machine name> - ISAPI and CGI Restrictions - <full path to cgi> - <friendly name> - check Allow

Understanding User Accounts

In previous versions of IIS, at install time we had a local account called IUSR_MachineName created. The IUSR_MachineName account was the default identity used by IIS whenever anonymous authentication was enabled. This was used by both the FTP and HTTP services. 

There was also had a group called IIS_WPG, used as a container for all the application pool identities. We made sure all the appropriate resources on the system had the correct permissions set for the IIS_WPG group during IIS setup so that an administrator needed to add only the administrator's identity to that group when creating a new application pool account.

This model worked well but had its drawbacks: the IUSR_MachineName account and IIS_WPG group were both local to the system they were created on. Every account and group within Windows is given a unique number called a SID (security identifier) that distinguishes it from other accounts. When an ACL is created, only the SID is used. As part of our design in previous versions of IIS, we had included the IUSR_MachineName in the METABASE.XML file so that if you tried to copy METABASE.XML from one machine to another, it would not work. The account on the other machine would have a different name. 

In addition, you could not just 'xcopy /o' ACLs from one machine to another since the SIDs were different from machine to machine. A workaround was to use domain accounts--but that required adding an active directory to the infrastructure. The IIS_WPG group had similar issues with permissions. If you set ACLs on one machine's file system for IIS_WPG and tried to 'xcopy /o' those to another machine, doing so would fail. The IIS team heard feedback about this issue and improved this experience by using a built-in account and group in IIS 7.0. 

A built-in account and group are guaranteed by the operating system to always have a unique SID. IIS 7.0 has taken this further and ensured that the actual names used by the new account and group will never be localized. For example, regardless of which language version of Windows you install, the IIS account name will always be IUSR, and the group name will be IIS_IUSRS. 

In summary, IIS 7.0 offers:

  • The IUSR built-in account replaces the IUSR_MachineName account. 
  • The IIS_IUSRS built-in group replaces the IIS_WPG group. 

Since the IUSR account is a built in account, it no longer needs a password. Logically, think of it as being the same as the NETWORKSERVICE or LOCALSERVICE accounts. 

The IUSR_MachineName account will still be created and used only when the FTP 6 compatible server, included with Windows Server 2008, is installed. If the FTP server included with Windows Server 2008 is not installed, then this account is never created.

This built-in account does not need a password and will be the default identity used when anonymous authentication is enabled. If you look in the applicationHost.config file you will see the following definition: 

Practically you need to know which user account is running CGI and FastCGI applications.
  • CGI - IUSR
We can verify this by using Process Monitor:



We need to know this because if your CGI/FastCGI application needs to write to or read from a file, it needs permission to do so in that folder.

Adding Permissions to Folders



In this example, a folder called "New" is created. Right click the folder and select Properties, then the Security tab, and then EDIT. (It doesn't matter what is selected when you click EDIT.)
Now we are ready to add "NETWORK SERVICE" with read and write priviledges to this folder. Click Add, Advanced, Find Now, and select the user "NETWORK SERVICE".

Finally, make sure the read and write check boxes are checked as needed.

Configuring FastCGI

This can only be done using a tool in the administration pack which adds the FastCGI Settings option.

General Considerations

This download contains a summary presentation on Best Practices for hosting a FastCGI application in a shared hosting environment as mentioned by Russ here.

The recommendation for isolating PHP web sites in a shared hosting environment is consistent with all general security isolation recommendations for IIS 7.0. In particular, it is recommended to:

  • Use one application pool per web site.
  • Use a dedicated user account as an identity for the application pool.
  • Configure the anonymous user identity to use the application pool identity.
  • Ensure that FastCGI impersonation is enabled in the PHP.INI file (fastcgi.impersonate=1).

Configuring FastCGI Properties

Under IIS6, this is done in a text file:

There is no such file in IIS7. Instead, a "snap in" must be installed to allow the setting of SOME of the Fast CGI properties.

Most notably missing is ResponseBufferLimit. This is set by default to
4194304  bytes. (It cannot be added to the collection of environmental variables.)

This means that for most FastCGI applications, the entire return will be buffered until the FCGX_Finish_r()  is called (or the application is terminated); then the entire response is sent at once.

This can be a problem for a process that takes more than a few seconds. The user is waiting for some type of progress report, typically a cyclical animation that continues until the result set is ready to be displayed.

To return a small response immediately, this buffer must a zero or some very small number. Setting this parameter turns out to be nontrivial. After five evenings of research, I had decided to use APPCMD.EXE, a command line utility for accessing IIS7:


I tried many combinations that resulted in the error message:

ERROR ( message:Malformed collection indexer; format is [@position,name='value',
name2='value2',...].  The @position specifier is optional, and [can] be '@start', '@en
d', or '@N' where N is a numeric index into the collection. )

I finally had to ask the IIS team for help with the syntax, which turned out to be:

  • appcmd.exe set config /section:handlers "/[name='FCGIEcho'].ResponseBufferLimit:0"
(My FastCGI application was "FCGIEcho.exe" which I named "FCGIEcho" in the handler mappings.)
This should produce the response:

Applied configuration changes to section "system.webServer/handlers" for "MACHINE/WEBROOT/APPHOST" at configuration commit path "MACHINE/WEBROOT/APPHOST"

The full list of handler properties that can be set is:
  • .path                                                
  • .verb                                                
  • .type                                                
  • .modules                                             
  • .scriptProcessor                                     
  • .resourceType                                        
  • .requireAccess                                       
  • .allowPathInfo                                       
  • .preCondition                                        
  • .responseBufferLimit

Another property that you might want to tweak in the new versions of FastCGI that support SIGTERM is signalBeforeTerminateSeconds. This attribute lets you configure IIS to wait for a specified period of time after IIS signals a FastCGI application that it needs to shut down. Doing so lets a FastCGI application clean up any settings before IIS terminates the process. The signalBeforeTerminateSeconds setting can be used to specify how long the module will wait before it forcefully shuts down the FastCGI process that does not respond to the termination signal.

As mentioned here, this feature is disabled by default.

To enable and set it, use:

  • set config /section:system.webServer/fastCgi /[fullPath='c:\inetpub\wwwroot\MySite\cgi-bin\FCGIEcho.exe'].signalBeforeTerminateSeconds:5

The full list of FastCGI properties that can be set is:
  • .arguments                                 
  • .monitorChangesTo                          
  • .stderrMode                                
  • .maxInstances                              
  • .idleTimeout                               
  • .activityTimeout                           
  • .requestTimeout                            
  • .instanceMaxRequests                       
  • .signalBeforeTerminateSeconds              
  • .protocol                                  
  • .queueLength                               
  • .flushNamedPipe                            
  • .rapidFailsPerMinute                       
  • .environmentVariables.[name='string'].name 

As covered here, if you are mapping IIS6 setting to IIS7, some are not transfered.

INI file settings are mapped to a FastCGI process pool as below.
fcgiext.ini configuration setting system.webServer/fastCgi/application property
ExePath fullPath
Arguments arguments
QueueLength queueLength
MaxInstances maxInstances
IdleTimeout idleTimeout
ActivityTimeout activityTimeout
RequestTimeout requestTimeout
InstanceMaxRequests instanceMaxRequests
FlushNamedPipe flushNamedPipe
Protocol protocol
RapidFailsPerMinute rapidFailsPerMinute
EnvironmentVars environmentVariables
ResponseBufferLimit Ignored
IgnoreExistingFiles Ignored
IgnoreDirectories Ignored
UnhealthyOnQueueFull Ignored

If the source version is FastCGI ISAPI 1.5, some additional properties get picked and mapped as below.

StderrMode stderrMode
MonitorChangesTo MonitorChangesTo
SignalBeforeTerminateSeconds signalBeforeTerminateSeconds

More discussion of this can be found here.

Auto Migrate

This is a lifesaving feature of Windows Server R2. It was on the table for some time but didn't make into release until this version. In action, it's a wonder to behold. It works and works well, as I found out recently when one of my new whiz bang Intel server motherboards went south.

The first sign of trouble was the phone ringing. The monitor in the data center was displaying no desktop or windows screens at all. The only thing on the monitor was a 24 point line that said "HARDWARE FAILURE". Ouch. That in itself was impressive. So the unit came out, and after all the usual testing of power supply, mouse, keyboard, motherboard battery voltage--nothing. Would not boot past a line that said "Hit F2 for SETUP or F12 to boot from network". Clearly the processor was working. Next I threw in a boot disk. Still nothing. So now I was down to motherboard and RAM.

This machine had been in service for 18 months and was still under warranty, so it was covered, but due to a few "budget considerations" it was going to put users out of action. Desperate measures were needed. A quick trip to Frys (a desperate measure for sure) produced a new motherboard for $49, leaving us with one time-honored problem...the dreaded migration.

First, we imaged the hard drive so we could restore it when the replacement motherboard arrived. It took a couple of hours and a hand full of DVDs, but that insurance is always well worth the trouble. Then we plugged everything together, shorted out the power switch pins on the motherboard with a screwdriver, and off we went.

Since this was a live test of new feature, and with the support phones as a soundtrack, the tension was a little high. A window immediately appeared along with a flickering task bar icon, and while it was booting, the OS was configured for the new motherboard.  Fairly quickly we got a dialog that offered us the option of rebooting, but since more installs were in progress,  we dismissed it by choosing "later".

Once the boot and install were complete, we installed the LAN drivers from the CD that come with the motherboard and then rebooted. We instantly had an internet connection through a browser, we could ping the server (after enabling the option to allow this in IIS7), and the websites came up. Back in business. Wow!

Some things to note. The MAC address changed and possibly the IP address too, but the machine came up with the same machine name in the router as a second entry. We should have changed the machine name to avoid confusion when configuring it anew in the router. 

If we had run into trouble, we would have used the three step approach outlined here. I will quote it here in case it disappears, as often happens to so many great articles when interest wains or domains expire.

Sometimes, after provisioning a new machine or making configuration changes, you may find that your IIS server has gone completely missing without so much as leaving you a note.

STEP 1: Make sure your client can resolve the hostname to an IP address and connect to the server. 

Test that it can by running the following from a command line:

STEP 2: Make sure IIS is started.

In order for IIS to accept incoming requests, the W3SVC service needs to be running.  Make sure it's running by executing this from command line:

net start w3svc

STEP 3: Make sure that your web site is started and is listening to requests.

A site has one or more network bindings, each defined by a combination of IP address, port, and hostnames on which the site should receive its requests.  Starting with Windows 2003, IIS uses the http.sys kernel driver to listen for requests, and the W3SVC service to configure it to listen for requests on all binding endpoints associated with your site.  On IIS7, the service doing most of the work is now called WAS (even though W3SVC is till needed).  A configuration error can cause WAS/W3SVC to fail to start a site, and therefore http.sys will not receive requests on its endpoints.  Also, there is the off chance that the site definition itself is missing, or that the site does not define the right bindings.

There's a quick way to check whether the site is started and what end-points it's configured to run on. Run the following from a command line:

%windir%\system32\inetsrv\AppCmd.exe list sites

At this point, you may get a configuration error indicating malformed configuration in the sites section.  If so, you need to fix this to proceed.  Once the error has been fixed, the resulting output should include your site as follows:

SITE "Default Web Site" (id:1,bindings:http/*:80:,state:Started)

If you site is not listed, well, then you know the problem.  If the site definition does not contain the proper binding (in the form of protocol/[ip-address-list-or-*]:port:[host-header-list-or-blank]), then you need to add the missing binding.

If the state shows as Unknown or Stopped, a configuration error is preventing your site from being started.  Most of the time, the event log holds the key to the exact error. 

Start the event viewer (Start -> Run -> eventvwr.exe), and look for an error in the System event log under “Windows Logs”, with “WAS” as the event source.  You may see something like this:

Event 5161:

Site 1 has no root application defined, so the site will be ignored.

This should most of the time hold the key to what you need to do to make sure the site is valid.  The following may cause your site not to be valid:

  1. The site does not contain a root application (with path = “/”).
  2. The site does not contain a single valid binding.
  3. A binding is invalid or conflicts with another binding on the machine. 

Once you’ve made the required configuration changes, you can start the site like this:

%windir%\system32\inetsrv\AppCmd.exe start site "Default Web Site"

(Replace "Default Web Site" with the name of your site.)

Windows Server 2008 R2 Freeze

An issue has been founded where a computer running Windows Server 2008 R2  stops responding randomly and freezes for no reason. Remote Desktop also stops working. The problem is a deadlock condition between the LSASS.EXE process, the Redirected Drive Buffering Subsystem driver (RDBSS.SYS), and the Winsock kernel.

There is a hotfix, but it might receive "additional testing". Hence, if you are not severely affected by this problem, you should probably wait for the next software update that contains the final hotfix.