Friday, January 31, 2014

Automated installation and configuration of IIS Advanced logging module using Powershell

Recently I had a chance to work on a web application which is hosted in the AWS environment. Throughout the development life cycle we have built an automated deployment process using Powershell. The automated build and deploy process takes care of spinning up new EC2 instances using Windows 2012 base AMI and installs both runtime requirements and application itself.

During this process, I had a chance to build few automation modules in Powershell. I am going to share some snippets of the modules in upcoming posts which I found not so straight forward to build. So that it would be useful to someone who is trying perform the same task.
In this post I am going to cover the automated installation and configuration of IIS Advanced logging module.

Why Advanced Logging module?
Since all of our web servers are behind Elastic Load Balancers (ELB), the remote host address is always set to the internal IP address of the load balancer. ELB puts the actual client IP address is in the X-Forwarded-For request header. The stock standard IIS HTTP logging module doesn't have access to these custom headers therefore we chose to use Advanced Logging module to capture and log additional customer headers.

We only need Advanced Logging module for IIS 7, IIS 7.5 and IIS 8.0. In IIS 8.5 (i.e.: In Windows 2012 R2) the HTTP logging has native support to capture custom fields as part of stock standard HTTP logging.

It was pretty straight forward to install and configure manually but not so easy through Powershell because installation and configuration not fully supported through Powershell alone I had to use appcmd.exe as well.

The Goal
  1. Install Advanced Logging module
  2. Create custom logging folder and assign permissions 
  3. Disable stock standard IIS logging
  4. Add additional headers like X-Forwarded-For, X-Forwarded-Proto to the available logging fields
  5. Disable server level Advanced logging
  6. Enable Advanced logging
  7. Configure custom log directory at server level and default site level 
  8. Configure Advanced Logging by site on the server
Lets dive into detail.....

1. Install Advanced Logging module
msiexec.exe /i C:\data\AdvancedLogging64.msi /passive /log C:\Data\advancedlogging.log
Start-Sleep -Seconds 10
2. Create custom logging folder and assign permissions
   
Give Read & Write access to IIS_IUSERS group (I found out about this in the hard way)
#grant permission to IIS_IUSERS group to the log directory
$Command = "icacls C:\logs /grant BUILTIN\IIS_IUSRS:(OI)(CI)RXMW"
cmd.exe /c $Command
3. Disable stock standard IIS logging
Import-Module WebAdministration 

# Disables http logging module
Set-WebConfigurationProperty -Filter system.webServer/httpLogging -PSPath machine/webroot/apphost -Name dontlog -Value true
4. Add additional headers like X-Forwarded-For, X-Forwarded-Proto to the available logging fields
# Adds AWS ELB aware X-Forwarded-For logging field
Add-WebConfiguration "system.webServer/advancedLogging/server/fields" -value @{id="X-Forwarded-For";sourceName="X-Forwarded-For";sourceType="RequestHeader";logHeaderName="X-Forwarded-For";category="Default";loggingDataType="TypeLPCSTR"}

# Adds AWS ELB aware X-Forwarded-Proto logging field
Add-WebConfiguration "system.webServer/advancedLogging/server/fields" -value @{id="X-Forwarded-Proto";sourceName="X-Forwarded-Proto";sourceType="RequestHeader";logHeaderName="X-Forwarded-Proto";category="Default";loggingDataType="TypeLPCSTR"} 
5. Disable default server level Advanced logging
# Disables the default advanced logging config
Set-WebConfigurationProperty -Filter "system.webServer/advancedLogging/server/logDefinitions/logDefinition[@baseFileName='%COMPUTERNAME%-Server']" -name enabled -value false
6. Enable Advanced logging
   
By default the Advanced Logging is disabled.
# Enable Advanced Logging
Set-WebConfigurationProperty -Filter system.webServer/advancedLogging/server -PSPath machine/webroot/apphost -Name enabled -Value true
7. Configure custom log directory at server level and default site level
# Set log directory at server level
Set-WebConfigurationProperty -Filter system.applicationHost/advancedLogging/serverLogs -PSPath machine/webroot/apphost -Name directory -Value $logDirectory

# Set log directory at site default level
Set-WebConfigurationProperty -Filter system.applicationHost/sites/siteDefaults/advancedLogging -PSPath machine/webroot/apphost -Name directory -Value $logDirectory
8. Configure Advanced Logging by site on the server
function AdvancedLogging-GenerateAppCmdScriptToConfigureAndRun()
{
    param([string] $site) 

    #Get current powershell execution folder
    $currentLocation = Get-Location

    #Create an empty bat which will be populated with appcmd instructions
    $stream = [System.IO.StreamWriter] "$currentLocation\$site.bat"

    #Create site specific log definition
    $stream.WriteLine("C:\windows\system32\inetsrv\appcmd.exe set config `"$site`" -section:system.webServer/advancedLogging/server /+`"logDefinitions.[baseFileName='$site',enabled='True',logRollOption='Schedule',schedule='Daily',publishLogEvent='False']`" /commit:apphost")

    #Get all available fields for logging
    $availableFields = Get-WebConfiguration "system.webServer/advancedLogging/server/fields"

    #Add appcmd instruction to add all the selected fields above to be logged as part of the logging
    #The below section can be extended to filter out any unwanted fields
    foreach ($item in $availableFields.Collection) 
    {
        $stream.WriteLine("C:\windows\system32\inetsrv\appcmd.exe set config `"$site`" -section:system.webServer/advancedLogging/server /+`"logDefinitions.[baseFileName='$site'].selectedFields.[id='$($item.id)',logHeaderName='$($item.logHeaderName)']`" /commit:apphost")
    }

    $stream.close()

    # execute the batch file create to configure the site specific Advanced Logging
    Start-Process -FilePath $currentLocation\$site.bat
    Start-Sleep -Seconds 10
}
#Call the above method by passing in the IIS site names
AdvancedLogging-GenerateAppCmdScriptToConfigureAndRun 'foo.com'

And that’s it :).

I found following two posts to be helpful in building the above.

http://forums.iis.net/t/1193633.aspx?Configure+Advanced+Logging+through+powershell
http://forums.iis.net/t/1161699.aspx?IIS+Advanced+Logging+1+0+Feedback

Comments and suggestions are welcome.

Tuesday, March 31, 2009

MVC chapter ebook

Scott Guthrie has released a chapter of the upcoming "Asp.net MVC 1.0" and it can be found on the following URL:

http://aspnetmvcbook.s3.amazonaws.com/aspnetmvc-nerdinner_v1.pdf

Tuesday, February 26, 2008

TRULY Understanding ViewState, the comment index

This article gives a very good coverage of the problems faced in usage of ViewState.

http://weblogs.asp.net/infinitiesloop/archive/2008/02/
19/truly-understanding-viewstate-comment-index.aspx

User Administration Tool (Part 3)

In Part 2 of this series of the article covered user management features. This final part will deal with role management and profile management.

http://www.dotnetbips.com/articles/
30775db7-379a-4b37-beff-cb32d3734a04.aspx

ASP.NET AJAX Controls and Extenders Tutorial

What are the differences between the Server Control, the ASP.NET AJAX Server Control and the ASP.NET AJAX Server Control Extender, and when should each be used?

This article gives good comparison between each of them.

http://www.codeproject.com/kb/
ajax/ajaxcontrolsandextenders.aspx