Showing posts with label WebAdministration. Show all posts
Showing posts with label WebAdministration. Show all posts

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 ....
 

Wednesday, 6 January 2016

Scripting IIS App Pools (v8.5) with PowerShell



App Pools

It's quite common to automate the process for creating a web site and its associated application pools. Creating an app pool is a simple process, but the many different settings that can be used is where things can get complicated. In my experience I found that PowerShell's Set-ItemProperty can get complicated when it's used with IIS app pools, so here's a few things I found out.

The IIS:\ provider doesn't work unless the WebAdministration module is first imported. So the following command must be run at the beginning of any script

Import-Module WebAdministration or ipmo webadministration

To fetch a list of app pools, run

Get-Item IIS:\AppPools\*


Whenever I'm working with something for the first time, I select one object, then pass it to the Format-List cmdlet which enables me to see every available property.

Get-Item IIS:\AppPools\* | select-object -First 1 | Format-List -Property * 


This displays all the properties that were hidden from the results of the first Get-Item command. I could also select a specific app pool

Get-Item IIS:\AppPools\MyAppPool | Format-List -Property *

I can see several results display Microsoft.IIs.PowerShell.Framework.ConfigurationElement rather than an actual value.

App Pool properties


 


This is where we need to switch to the Get-ItemProperty cmdlet to see the property details in full.

Get-Item IIS:\AppPools\* | select-object -First 1 | Get-ItemProperty -name processmodel

or for a specific App Pool

Get-Item IIS:\AppPools\MyAppPool | Get-ItemProperty -name processmodel
this can also be written as

(Get-ItemProperty IIS:\AppPools\MyAppPool).processmodel
or

Get-ItemProperty IIS:\AppPools\MyAppPool -name processmodel
The results show that processmodel has several properties.

processModel properties









I expected to be able to change one of these properties by running a command like this

Set-ItemProperty IIS:\AppPools\MyAppPool -name processmodel.identitytype -Value ApplicationPoolIdentity

When I run this command, it executes without an error, but no changes are made.

Similarly with other App Pool settings the Set-ItemProperty command does not make changes.

Set-ItemProperty IIS:\AppPools\MyAppPool -name managedPipeLineMode -value Integrated

After some experimentation I found that the name parameter of Set-ItemProperty is case sensitive

When I go back and examine my previous commands, I can see that processmodel is in fact processModel, and identitytype is identityType. Also I should've been using managedPiplineMode.

Once I change my commands accordingly, the Set-ItemProperty command correctly makes the changes.

Set-ItemProperty IIS:\AppPools\MyAppPool -name processModel.identityType -Value ApplicationPoolIdentity

Set-ItemProperty IIS:\AppPools\MyAppPool -name managedPipelineMode -value Integrated