Windows Server IIS and Web Hosting: Enterprise Web Services

Windows Server IIS and Web Hosting: Enterprise Web Services

Introduction

Internet Information Services (IIS) powers enterprise web applications on Windows Server. This guide covers IIS installation and configuration, application pool isolation, SSL/TLS certificate management, URL rewrite rules, load balancing with ARR, web farm deployment, security best practices, and performance optimization.

IIS Installation and Configuration

Installing IIS

# Install IIS with common features
Install-WindowsFeature -Name Web-Server, Web-Mgmt-Tools, Web-Asp-Net45, Web-Windows-Auth, Web-Url-Auth, Web-Filtering, Web-IP-Security

# Install additional features
Install-WindowsFeature -Name Web-WebSockets, Web-AppInit, Web-Http-Redirect

# Import WebAdministration module
Import-Module WebAdministration

# Verify IIS installation
Get-Service W3SVC
Get-Website

Creating Websites

# Create website
New-Website -Name "ContosoWeb" `
    -PhysicalPath "C:\inetpub\wwwroot\ContosoWeb" `
    -Port 80 `
    -HostHeader "www.contoso.com" `
    -ApplicationPool "ContosoAppPool"

# Create website with SSL
New-Website -Name "SecureWeb" `
    -PhysicalPath "C:\inetpub\wwwroot\SecureWeb" `
    -Port 443 `
    -HostHeader "secure.contoso.com" `
    -Ssl `
    -ApplicationPool "SecureAppPool"

# Add additional binding
New-WebBinding -Name "ContosoWeb" -Protocol "http" -Port 80 -HostHeader "contoso.com"

# View websites
Get-Website | Select-Object Name, ID, State, PhysicalPath, Bindings

# Start/stop website
Start-Website -Name "ContosoWeb"
Stop-Website -Name "ContosoWeb"

# Remove website
Remove-Website -Name "OldSite" -Confirm:$false

Managing Virtual Directories and Applications

# Create virtual directory
New-WebVirtualDirectory -Site "ContosoWeb" -Name "Documents" -PhysicalPath "C:\SharedDocs"

# Create application
New-WebApplication -Site "ContosoWeb" -Name "API" -PhysicalPath "C:\WebApps\API" -ApplicationPool "APIAppPool"

# View virtual directories and applications
Get-WebVirtualDirectory -Site "ContosoWeb"
Get-WebApplication -Site "ContosoWeb"

Application Pool Management

Creating Application Pools

# Create application pool
New-WebAppPool -Name "ContosoAppPool"

# Configure application pool
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "managedRuntimeVersion" -Value "v4.0"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "managedPipelineMode" -Value "Integrated"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "startMode" -Value "AlwaysRunning"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "enable32BitAppOnWin64" -Value $false

# Set identity
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.identityType" -Value "ApplicationPoolIdentity"

# Or use custom account
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.identityType" -Value "SpecificUser"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.userName" -Value "CONTOSO\WebAppUser"
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.password" -Value "P@ssw0rd123!"

# View application pools
Get-IISAppPool | Select-Object Name, Status, ManagedRuntimeVersion, ManagedPipelineMode

Recycling Configuration

# Configure recycling
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.time" -Value "1.05:00:00"  # 29 hours
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.memory" -Value 0  # Disabled
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.privateMemory" -Value 2000000  # 2 GB

# Schedule recycling times
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "recycling.periodicRestart.schedule" -Value @{value="02:00:00"}

# Set idle timeout
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "processModel.idleTimeout" -Value "00:20:00"  # 20 minutes

# Configure rapid-fail protection
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "failure.rapidFailProtection" -Value $true
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "failure.rapidFailProtectionMaxCrashes" -Value 5
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "failure.rapidFailProtectionInterval" -Value "00:05:00"

# Recycle application pool
Restart-WebAppPool -Name "ContosoAppPool"

SSL/TLS Certificate Management

Installing SSL Certificates

# Generate Certificate Signing Request (CSR)
$cert = New-SelfSignedCertificate -DnsName "www.contoso.com" `
    -CertStoreLocation "cert:\LocalMachine\My" `
    -KeyLength 2048 `
    -KeyExportPolicy Exportable `
    -KeyUsage DigitalSignature, KeyEncipherment `
    -NotAfter (Get-Date).AddYears(2)

# Export CSR (for production, get certificate from CA)
# For self-signed testing:
$certPath = "cert:\LocalMachine\My\$($cert.Thumbprint)"

# Bind certificate to website
New-WebBinding -Name "ContosoWeb" -Protocol "https" -Port 443 -HostHeader "www.contoso.com" -SslFlags 1

$cert = Get-ChildItem -Path "cert:\LocalMachine\My" | Where-Object { $_.Subject -like "*contoso.com*" }
Get-Item -Path "IIS:\SslBindings\0.0.0.0!443" -ErrorAction SilentlyContinue | Remove-Item
New-Item -Path "IIS:\SslBindings\0.0.0.0!443" -Value $cert

# Bind with SNI (Server Name Indication)
New-WebBinding -Name "ContosoWeb" -Protocol "https" -Port 443 -HostHeader "www.contoso.com" -SslFlags 1  # SNI

$cert | New-Item "IIS:\SslBindings\!443!www.contoso.com"

# View SSL bindings
Get-WebBinding -Name "ContosoWeb" -Protocol "https"

TLS Configuration

# Disable TLS 1.0 and 1.1 (enable only TLS 1.2 and 1.3)
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Name 'Enabled' -Value 0 -PropertyType 'DWord'
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Name 'DisabledByDefault' -Value 1 -PropertyType 'DWord'

New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Name 'Enabled' -Value 0 -PropertyType 'DWord'
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Name 'DisabledByDefault' -Value 1 -PropertyType 'DWord'

# Enable TLS 1.2
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Name 'Enabled' -Value 1 -PropertyType 'DWord'
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Name 'DisabledByDefault' -Value 0 -PropertyType 'DWord'

# Configure HSTS (HTTP Strict Transport Security)
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.applicationHost/sites/site[@name='ContosoWeb']/hsts" -Name "." -Value @{enabled=$true;max-age=31536000;includeSubDomains=$true}

URL Rewrite Module

Installing URL Rewrite

# Download and install URL Rewrite module
# https://www.iis.net/downloads/microsoft/url-rewrite

# Or use Web Platform Installer
# Install-Module -Name WebPlatformInstaller
# Install-Package -Name UrlRewrite2

Creating Rewrite Rules

# HTTP to HTTPS redirect
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/rewrite/rules" `
    -Name "." `
    -Value @{name='HTTP to HTTPS';stopProcessing='True'}

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/match" `
    -Name "url" -Value "(.*)"

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/conditions" `
    -Name "." `
    -Value @{input='{HTTPS}';pattern='off'}

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/action" `
    -Name "type" -Value "Redirect"
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/rewrite/rules/rule[@name='HTTP to HTTPS']/action" `
    -Name "url" -Value "https://{HTTP_HOST}/{R:1}"

# Canonical hostname (www to non-www)
# Example web.config snippet:
$rewriteConfig = @"
<rewrite>
  <rules>
    <rule name="Canonical Hostname" stopProcessing="true">
      <match url="(.*)" />
      <conditions>
        <add input="{HTTP_HOST}" pattern="^www\.contoso\.com$" />
      </conditions>
      <action type="Redirect" url="https://contoso.com/{R:1}" redirectType="Permanent" />
    </rule>
  </rules>
</rewrite>
"@

Load Balancing with ARR

Installing Application Request Routing

# Download and install ARR
# https://www.iis.net/downloads/microsoft/application-request-routing

# Enable proxy mode
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.webServer/proxy" -Name "enabled" -Value $true

Creating Server Farm

# Server farm configuration requires IIS Manager or web.config editing

# Example: Configure server farm in applicationHost.config
$serverFarmConfig = @"
<webFarms>
  <webFarm name="ContosoFarm" enabled="true">
    <server address="web01.contoso.com" enabled="true" />
    <server address="web02.contoso.com" enabled="true" />
    <applicationRequestRouting>
      <affinity useCookie="true" cookieName="ARRAffinity" />
      <loadBalance algorithm="WeightedRoundRobin" />
      <healthCheck url="http://localhost/health" interval="00:00:30" />
    </applicationRequestRouting>
  </webFarm>
</webFarms>
"@

# URL rewrite rule to route to farm
$farmRouting = @"
<rule name="ARR_ContosoFarm_loadbalance" patternSyntax="Wildcard" stopProcessing="true">
  <match url="*" />
  <action type="Rewrite" url="http://ContosoFarm/{R:0}" />
</rule>
"@

Health Monitoring

# Health check configuration
# Create health endpoint in application (e.g., /health)
# Returns HTTP 200 when healthy

# Monitor server farm health
# Get-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "webFarms/webFarm[@name='ContosoFarm']/server" | Select-Object address, enabled, state

Web Farm Configuration

Shared Configuration

# Export IIS configuration
Export-IISConfiguration -PhysicalPath "\\fileserver\IISConfig" `
    -DontExportKeys $false `
    -Force

# On additional web servers, import configuration
Enable-IISSharedConfig -PhysicalPath "\\fileserver\IISConfig" `
    -KeyEncryptionPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) `
    -Force

# View shared config status
Get-IISSharedConfig

Content Synchronization

# Synchronize content using Robocopy
$syncScript = @"
# Sync web content from primary to secondary servers
`$source = '\\web01\C$\inetpub\wwwroot\ContosoWeb'
`$destinations = @('\\web02\C$\inetpub\wwwroot\ContosoWeb', '\\web03\C$\inetpub\wwwroot\ContosoWeb')

foreach (`$dest in `$destinations) {
    robocopy `$source `$dest /MIR /R:3 /W:5 /MT:8 /LOG:C:\Logs\Sync.log
}
"@

$syncScript | Out-File "C:\Scripts\SyncContent.ps1"

# Schedule content sync
$action = New-ScheduledTaskAction -Execute 'PowerShell.exe' `
    -Argument '-ExecutionPolicy Bypass -File C:\Scripts\SyncContent.ps1'
$trigger = New-ScheduledTaskTrigger -Daily -At 3am
Register-ScheduledTask -TaskName "IIS Content Sync" -Action $action -Trigger $trigger -User "SYSTEM"

Security Best Practices

Request Filtering

# Configure request filtering
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/requestFiltering" `
    -Name "allowDoubleEscaping" -Value $false

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/requestFiltering" `
    -Name "allowHighBitCharacters" -Value $false

# Block file extensions
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/requestFiltering/fileExtensions" `
    -Name "." -Value @{fileExtension='.config';allowed=$false}

# Block dangerous strings
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/requestFiltering/denyUrlSequences" `
    -Name "." -Value @{sequence='..'}

Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/requestFiltering/denyUrlSequences" `
    -Name "." -Value @{sequence='<script'}

IP Restrictions

# Allow only specific IP addresses
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/ipSecurity" `
    -Name "allowUnlisted" -Value $false

Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/ipSecurity" `
    -Name "." -Value @{ipAddress='192.168.1.0';subnetMask='255.255.255.0';allowed=$true}

# Block specific IP
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/ipSecurity" `
    -Name "." -Value @{ipAddress='10.0.0.50';allowed=$false}

Authentication Configuration

# Disable anonymous authentication
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/authentication/anonymousAuthentication" `
    -Name "enabled" -Value $false

# Enable Windows authentication
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/security/authentication/windowsAuthentication" `
    -Name "enabled" -Value $true

# Configure authorization
Add-WebConfiguration -Filter "/system.webServer/security/authorization" `
    -Value @{accessType='Allow';roles='CONTOSO\Web Users'} `
    -PSPath "IIS:\" -Location "ContosoWeb"

Remove-WebConfiguration -Filter "/system.webServer/security/authorization/add[@users='*']" `
    -PSPath "IIS:\" -Location "ContosoWeb"

Performance Optimization

Static Compression

# Enable static compression
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
    -Filter "system.webServer/httpCompression" `
    -Name "staticCompressionEnableCpuUsage" -Value 90

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
    -Filter "system.webServer/httpCompression/scheme[@name='gzip']" `
    -Name "staticCompressionLevel" -Value 9

# Add file types to compress
Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
    -Filter "system.webServer/httpCompression/staticTypes" `
    -Name "." -Value @{mimeType='application/json';enabled=$true}

Output Caching

# Enable output caching
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/caching" `
    -Name "enabled" -Value $true

# Add cache rule
Add-WebConfiguration -Filter "/system.webServer/caching/profiles" `
    -Value @{extension='.html';policy='CacheUntilChange';kernelCachePolicy='CacheUntilChange'} `
    -PSPath "IIS:\" -Location "ContosoWeb"

# Set cache duration
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/staticContent/clientCache" `
    -Name "cacheControlMode" -Value "UseMaxAge"

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/staticContent/clientCache" `
    -Name "cacheControlMaxAge" -Value "7.00:00:00"  # 7 days

HTTP/2 Configuration

# Enable HTTP/2 (enabled by default on Windows Server 2016+)
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
    -Filter "system.applicationHost/sites/site[@name='ContosoWeb']/bindings/binding[@protocol='https']" `
    -Name "sslFlags" -Value 0  # HTTP/2 requires SNI

# Verify HTTP/2 is enabled
Get-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
    -Filter "system.webServer/serverRuntime" `
    -Name "enabled"

Application Initialization

# Enable application initialization
Set-ItemProperty -Path "IIS:\AppPools\ContosoAppPool" -Name "startMode" -Value "AlwaysRunning"

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/applicationInitialization" `
    -Name "doAppInitAfterRestart" -Value $true

# Add initialization page
Add-WebConfiguration -Filter "/system.webServer/applicationInitialization/add" `
    -Value @{initializationPage='/warmup.aspx'} `
    -PSPath "IIS:\" -Location "ContosoWeb"

Monitoring IIS

IIS Logs

# Configure logging
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.webServer/httpLogging" `
    -Name "dontLog" -Value $false

Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST/ContosoWeb" `
    -Filter "system.applicationHost/sites/site[@name='ContosoWeb']/logFile" `
    -Name "directory" -Value "D:\IISLogs"

# View recent errors
Get-Content "C:\inetpub\logs\LogFiles\W3SVC1\*.log" -Tail 50 | Where-Object { $_ -like '*500*' }

Performance Counters

# Monitor IIS performance counters
$counters = @(
    '\Web Service(_Total)\Current Connections'
    '\Web Service(_Total)\Bytes Total/sec'
    '\ASP.NET Applications(__Total__)\Requests/Sec'
    '\ASP.NET Applications(__Total__)\Request Execution Time'
    '\Process(w3wp)\% Processor Time'
    '\Process(w3wp)\Private Bytes'
)

Get-Counter -Counter $counters -SampleInterval 5 -MaxSamples 10 | 
    ForEach-Object { $_.CounterSamples | Format-Table -AutoSize }

Key Takeaways

  • IIS provides enterprise web hosting on Windows Server
  • Application pools isolate applications for security and stability
  • SSL/TLS certificates secure web traffic with HTTPS
  • URL Rewrite handles redirects and URL normalization
  • ARR delivers load balancing across web servers
  • Web farms share configuration and content
  • Security features include request filtering and IP restrictions
  • Performance optimization includes compression, caching, and HTTP/2

Next Steps

  • Deploy IIS with application pool isolation
  • Configure SSL certificates for HTTPS
  • Implement URL rewrite rules
  • Set up load balancing with ARR
  • Configure web farm with shared configuration
  • Harden IIS security settings
  • Optimize performance with compression and caching

Additional Resources


Host. Secure. Scale. Optimize.