Friday, 12 February 2016

Automating IIS App Pool Compliance


Background

With many servers comes the need to simply configuration management. I find application pools to be a constant source of headaches when it comes to keeping things consistent. They have loads of settings, which often get changed when trying to troubleshoot a problem (and not changed back). There’s also many other situations where they need to be configured; moving an application to another server, operating system upgrades, migrations, rationalisation, enforcing consistency - to name a few.

This has led me to automate the process of app pool configuration.

There are two scenarios to be covered – an ‘Online’ and an ‘Offline’ version. The Online version uses PowerShell remoting to connect to another IIS server that contains all the app pools and their required configurations. It then takes those settings and applies them to the target server.

The Offline version uses an existing IIS server and exports the app pool settings to a csv file, and then uses that to apply the settings to a target server.

Note that this process does not cover how to create app pools. This can easily be done by using MS Web Deploy if migrating a site, or New-WebAppPool if setting up a new site.

The process only works for IIS versions 7 and above. It is possible to migrate app pool settings from IIS v6 to IIS v7 onwards but it is more complicated than the process described here. The app pool settings are not the same between these versions, plus standard PowerShell commands and the IIS: drive are not available due to the limitations of PowerShell v2 (which is the highest version that can be used on an IIS 6.0 server).


Preparation

The first step is to decide which settings need to be configured. I like to set my app pool defaults first, and then explicitly set anything which hasn’t been caught by the default settings.

Setting the app pool defaults will change any app pool setting that isn’t the same as the new default setting or hasn’t been explicitly set, so use with caution. Setting app pool defaults is usually a one off setting when the server is new, or for a new global setting where there is a requirement to overwrite all existing settings.

To keep things simple I won’t go into the details of how to set app pool defaults, what I use for defaults, and how to revert an explicit app pool setting to inherit the default, I’ll try and cover that in a future blog post.

For now, what is important is that I want to set some app pool settings to a value that is different to the default.

The following settings need to be assessed as they may need to be different to the defaults:

.NET CLR Version
Enable 32 bit Applications
Managed Pipeline Mode
Identity

I also want to change the Recycling > Specific Times setting. My default is to recycle at 02:00am every day. I want to change this so that each app pool recycles at a random time between 02:00 – 02:59. I already have a separate script for this, see here for a blog post.
 

Offline Mode – export

This method uses an existing IIS server and exports the app pool settings to a csv file, and then uses that to apply the settings to a target server.

The following script is used to obtain and export these settings to csv file.

Import-Module webadministration

$AppPoolProperties = @()

foreach ( $pool in (get-item IIS:\AppPools\*) ) {

$Properties = New-Object System.Object

$Properties | Add-Member -type NoteProperty -name AppPoolName `
    -Value $($pool.name)

$Properties | Add-Member -type NoteProperty -name Enable32bit `
    -Value $($pool.enable32BitAppOnWin64)

$Properties | Add-Member -type NoteProperty -name Runtime `
    -Value $($pool.managedRuntimeVersion)

$Properties | Add-Member -type NoteProperty -name Pipeline `
    -Value $($pool.managedPipelineMode)

$Properties | Add-Member -type NoteProperty -name ProcessModel `
    -Value $($pool | Get-ItemProperty -name processModel.identityType)

$AppPoolProperties += $Properties
}

$AppPoolProperties | Export-Csv D:\AppPools.csv -NoTypeInformation

 
Breakdown

Import-Module WebAdministration
This loads the WebAdministration module which is required to make the IIS: drive available.

    $AppPoolProperties = @()
Creates an empty variable called AppPoolProperties and declares it as an array

Foreach loop

    foreach ( $pool in (get-item IIS:\AppPools\*) )

$pool is a temporary variable that only exists in the loop. This variable represents each member of the second command in the brackets –

    (get-item IIS:\AppPools\*)

This returns a list of app pool objects, so $pool will represent each app pool object in turn.

    $Properties = New-Object System.Object

Creates a new temporary object, see this link for an excellent explanation of custom objects:


The next section creates a variable for each of the app pool settings I want. This is generally straightforward and can be done by using $pool.property

    $pool.enable32BitAppOnWin64

To see all the app pool properties available use

    get-item IIS:\AppPools\* | Select-Object –first 1| Format-List –property *

Some of the properties cannot be obtained by using $pool.property

ProcessModel is an object type Microsoft.IIs.PowerShell.Framework.ConfigurationElement

To get its value I have to use Get-ItemProperty. This command will return the Identity Type for Process Model

    $pool | Get-ItemProperty -name processModel.identityType

Next I need to create on object that has each app pool value as a property. I use Add-Member to assign each of the app pool properties to the $properties object. Piping the $properties object created earlier to Add-Member assigns a name and value for each app pool property.

Viewing the $properties object after adding the properties for a single app pool looks like this

PS D:\scripts\modules> $Properties
AppPoolName  : .NET v2.0 Classic
Enable32bit  : False
Runtime      : v2.0
Pipeline     : Classic
ProcessModel : ApplicationPoolIdentity

    $AppPoolProperties += $Properties

Finally the $properties object is added to the $AppPoolProperties variable, and each time the foreach loop runs, another set of app pool properties are added to this variable. Using += adds the current object to the variable rather than overwriting the existing contents of the variable.

Once the loop is complete the final command uses Export-CSV to write $AppPoolProperties to CSV

    $AppPoolProperties | Export-Csv -Path D:\AppPools.csv –NoTypeInformation

Copy the CSV file to your target server, then run the Import script.
 
 
Offline Mode - import

The following script uses the exported CSV file to set each app pool property. It takes the App Pool name, and looks for a matching app pool, then sets the properties according to the setting in the CSV file. Once the app pool settings are in the CSV file, and the CSV file is stored on the server, this script can be run as a one off, or repeatedly to enforce the settings that are stored in the CSV file.
 

Import-Module webadministration

$AppPools = (Import-Csv D:\AppPools.csv)

foreach ( $pool in ( Get-Item IIS:\AppPools\* )) {

Set-ItemProperty IIS:\AppPools\$($pool.name) -Name enable32BitAppOnWin64 -Value `
    "$($AppPools | where { $_.AppPoolName -eq $pool.name } | Select-Object -ExpandProperty Enable32bit)"

Set-ItemProperty IIS:\AppPools\$($pool.name) -Name managedRuntimeVersion -Value `
    "$($AppPools | where { $_.AppPoolName -eq $pool.name } | Select-Object -ExpandProperty Runtime)"

Set-ItemProperty IIS:\AppPools\$($pool.name) -Name managedPipelineMode -Value `
    "$($AppPools | where { $_.AppPoolName -eq $pool.name } | Select-Object -ExpandProperty Pipeline)"

Set-ItemProperty IIS:\AppPools\$($pool.name) -Name processModel.identityType -Value `
    "$($AppPools | where { $_.AppPoolName -eq $pool.name } | Select-Object -ExpandProperty ProcessModel)"

}


Breakdown

    Import-Module WebAdministration

This loads the WebAdministration module which is required to make the IIS: drive available.

  $AppPools = (Import-Csv D:\AppPools.csv)

Creates a variable from the contents of the CSV file

Foreach loop

This is the same as the previous script. The purpose is to cycle through each local app pool, then change the settings accordingly.

    foreach ( $pool in (get-item IIS:\AppPools\*) )

The Set-ItemProperty command is quite long, so I will break it down.

    Set-ItemProperty IIS:\AppPools\$($pool.name)

The item property to be set is defined as the app pool in the current $pool variable, and is selected using the name property

    -Name enable32BitAppOnWin64

The name parameter is the name of the property being changed

    -Value

For the Value parameter I obtain the app pool value stored in the CSV file for the –name property

    "$($AppPools | where { $_.AppPoolName -eq $pool.name } | Select-Object -ExpandProperty Runtime)"

    $AppPools
Starts with the CSV contents in the variable

    where { $_.AppPoolName -eq $pool.name }
Filter with Where-Object to select only the row in the CSV file that matches the current app pool ($pool object) using the name property.

    Select-Object -ExpandProperty Runtime
Select the Property value with Select-Object, in this case the Runtime value, which is the managedRuntimeVersion

The whole command is then wrapped in $(), this returns the contents of the value object rather than the object itself.


Online mode - tbc ....