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 ApplicationsManaged 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 ClassicEnable32bit : 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.