Cartographie des application pools au travers d’IIS, de Performance Monitor et du système/Mapping of the application pools across IIS, Performance Monitor and the System.
[MAJ 10/03/2017] Performance Monitor (ou PerfMon) est l'outil indispensable pour monitorer les performances de vos serveurs Windows. L'un des problèmes les plus fréquents avec Perfmon est d'identifier une instance particulière d'un processus. En effet PerfMon les numérotent de manière séquentielle (ici pour des worker processes IIS) : w3wp, w3wp#1, w3wp#2, w3wp#3 ... Il est donc difficile de faire la correspondance avec un ID de processus (PID). Heureusement un workaround existe : https://blogs.technet.microsoft.com/askperf/2010/03/29/perfmon-identifying-processes-by-pid-instead-of-instance/.
Dans le cadre d'un serveur IIS, il ne nous reste plus qu'à faire le lien entre le PID et l'application concernée via le task manager (ou autres outil comme pslist, PowerShell).
Deux possibilités :
- Pour le peu que l'on utilise l'identité par défaut depuis IIS 7.5 (cf. ApplicationPoolIdentity), il est alors facile de faire la correspondance (Identité = Nom de l'application pool - voire le nom du site web si celui ci a été créé via la console IIS) comme en atteste la capture d'écran ci-dessous:
- Dans d'autres cas cela parait un peu plus compliqué :)
Je vous propose dans cette article un script PowerShell (disponible ici) qui vous offrira un état des lieux comme celui-ci et sans devoir effectuer par le workaround précédemment évoqué (une solution universelle en somme) :
On constate rapidement que :
- Nous avons 6 applications réparties sur 5 sites (le site www.northwindtraders.com hébergent 2 applications : www.northwindtraders.com et www.northwindtraders.com/HR)
- L'application pool www.northwindtraders.com est utilisé par les sites www.northwindtraders.com et intranet.northwindtraders.com
- Le site www.contoso.com est configuré en mode "web gardening" et actuellement 2 worker processes prennent en charge les requêtes à destination de ce site.
- Les colonnes PID et Instance nous permettent de faire le lien entre les PID et les instances sous PerfMon. Dans le cas de la deuxième ligne le processus 1512 correspond à l'instance w3wp#1 et le processus 1520 correspond à l'instance w3wp#2 (l'ordre est préservé)
Ce script est aussi disponible dans le TechNet Script Center : https://gallery.technet.microsoft.com/Mapping-of-the-application-ee96fb1f
[Update 03/10/2017] Performance Monitor (or Perfmon) is the essential tool to monitor the performance of your Windows servers. One of the most common problems with Perfmon is to identify a particular instance of a process. Indeed PerfMon numbers them sequentially (here for the IIS worker processes): w3wp, w3wp # 1, w3wp # 2, # 3 w3wp ... It is difficult to match a process ID (PID). Fortunately, a workaround exists: https://blogs.technet.microsoft.com/askperf/2010/03/29/perfmon-identifying-processes-by-pid-instead-of-instance/.
As part of an IIS server, it remains for us to make the connection between the PID and the relevant application via the task manager (or other tool like pslist, PowerShell).
Two possibilities :
- If the default identity for IIS 7.5 (cf. ApplicationPoolIdentity) is used, it is easy to make the connection (ID = Name of the application pool - even the name of the website if it has created via the IIS console) as illustrated in the screenshot below:
- In other cases it seems a little more complicated :)
I propose in this article a PowerShell script (available here) that offers an inventory like this and without having to perform the previously mentioned workaround (an universal solution):
We quickly found that:
- We have 6 applications spread over 5 sites (The site www.northwindtraders.com hosts 2 applications www.northwindtraders.com and www.northwindtraders.com/HR)
- The www.northwindtraders.com application pool is used by the www.northwindtraders.com and intranet.northwindtraders.com sites
- The site www.contoso.com is configured for "web gardening" mode and 2 worker processes currently support queries to this site.
- The PID and Instance columns allow us to make the connection between the PID and the instances in PerfMon. In the case of the second line the PID 1512 matches the instance w3wp#1 and the PID 1250 matches the instance w3wp#2 (the order is preserved)
The script file is available at the TechNet Script Center repository, at: https://gallery.technet.microsoft.com/Mapping-of-the-application-ee96fb1f
#requires -version 4 -Module WebAdministration
Import-Module -Name WebAdministration
#Region Function definition
<#Get the pair Instance, PID for the running worker processes from Performance Data Collection
Instance PID
-------- ---
w3wp#6 4504
w3wp#5 5848
w3wp#4 5088
w3wp#3 5520
w3wp#2 3884
w3wp#1 5476
w3wp 5964
#>
function Get-W3WPDataFromPerformanceMonitor
{
[CmdletBinding()]
Param()
#Returned results will be stored into this array
$Data = @()
#Regular expression pattern to find the instance name in the counter path
$Pattern = @([regex]'^.*\((?<INSTANCE>(.*))\).*$')
#Get Path a,d PID from running worker processes from Performance Monitor
$Counters = Get-Counter -Counter '\Process(w3wp*)\ID Process' -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty CounterSamples |
Select-Object -Property CookedValue, Path
#Processing the returned collection
foreach ($CurrentCounter in $Counters)
{
#Regular expression matching to keep only the instance name (for instance: w3wp#1)
$CurrentMatches = $CurrentCounter.Path |
Select-String -Pattern $Pattern |
Select-Object -ExpandProperty matches
if ($CurrentMatches)
{
#Getting the instance name (for instance: w3wp#1)
$CurrentInstance = $CurrentMatches.Groups[$currentMatches.Groups.Count-1].Value
#Creating an object with the instance name and the path
$CurrentData = New-Object -TypeName PSObject -Property @{
PID = $CurrentCounter.CookedValue
Instance = $CurrentInstance
}
#Storing the object into the array
$Data += $CurrentData
}
}
#Returning the data
return $Data
}
<#Get the link between the applications, the sites and the site ids from ServerManager
Applications Site SiteId
------------ ---- ------
{Default Web Site/} Default Web Site 1
{www.contoso.com/} www.contoso.com 2
{www.northwindtraders.com/, www.northwindtraders.com/HR} www.northwindtraders.com 3
{www.microsoft.com/} www.microsoft.com 4
{intranet.northwindtraders.com/} intranet.northwindtraders.com 5
#>
function Get-WebsitesFromServerManager
{
[CmdletBinding()]
Param()
#Loading the Web Administration DLL for handling ServerManager
#$null = [System.Reflection.Assembly]::LoadFrom( "$env:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll" )
Add-Type -Path "$env:systemroot\system32\inetsrv\Microsoft.Web.Administration.dll"
#Creating a ServerManager Object
$ServerManager = New-Object -TypeName Microsoft.Web.Administration.ServerManager
#Getting applications and related sites and site ids.
$WebApplications = $ServerManager.sites | Select-Object -Property Applications, @{
Name = 'Site'
Expression = {
$_.Name
}
}, @{
Name = 'SiteId'
Expression = {
$_.Id
}
}
#Return them
return $WebApplications
}
<#Get the pair ApplicationPoolName, PID for the running worker processes from Web Configuration
ApplicationPoolName PID
------------------- ---
DefaultAppPool 5964
HR 5476
www.contoso.com 3884
www.northwindtraders.com 5520
www.microsoft.com 5088
www.contoso.com 5848
www.contoso.com 4504
#>
function Get-W3WPDataFromWebConfiguration
{
return Get-WebConfiguration system.applicationHost/applicationPools/workerProcesses/* | Select-Object -Property @{Name="ApplicationPoolName"; Expression={$_.appPoolName}}, @{Name="PID"; Expression={$_.ProcessId}}
}
<#Get the data by using the 3 previous functions to have a clear overview of the hosted web applications
Instance ApplicationPool SiteId Site Application PID W3SVCPath
-------- --------------- ------ ---- ----------- --- ---------
{w3wp} DefaultAppPool 1 Default Web Site Default Web Site/ {5964} _LM_W3SVC1_ROOT
{w3wp#2, w3wp#5, w3wp#6} www.contoso.com 2 www.contoso.com www.contoso.com/ {3884, 5848, 4504} _LM_W3SVC2_ROOT
{w3wp#3} www.northwindtraders.com 3 www.northwindtraders.com www.northwindtraders.com/ {5520} _LM_W3SVC3_ROOT
{w3wp#1} HR 3 www.northwindtraders.com www.northwindtraders.com/HR {5476} _LM_W3SVC3_ROOT_HR
{w3wp#4} www.microsoft.com 4 www.microsoft.com www.microsoft.com/ {5088} _LM_W3SVC4_ROOT
{w3wp#3} www.northwindtraders.com 5 intranet.northwindtraders.com intranet.northwindtraders.com/ {5520} _LM_W3SVC5_ROOT
#>
function Get-W3WPData
{
[CmdletBinding()]
Param()
#the pair Instance, PID for the running worker processes from Performance Data Collection
$W3WPDataFromPerformanceMonitor = Get-W3WPDataFromPerformanceMonitor
#Getting the pair ApplicationPoolName, PID for the running worker processes from WMI
#$W3WPDataFromWebConfiguration = Get-W3WPDataFromWMI
$W3WPDataFromWebConfiguration = Get-W3WPDataFromWebConfiguration
#Getting the link between the applications, the sites and the site ids from ServerManager
$WebsitesFromServerManager = Get-WebsitesFromServerManager
#Returned results will be stored into this array
$Data = @()
#Hastable to get the worker process instance (from performance monitor) by using the PID as a key
$W3WPDataFromPerformanceMonitorHT = $W3WPDataFromPerformanceMonitor | Group-Object -Property PID -AsHashTable -AsString
#Processing each website
foreach ($CurrentWebsite in $WebsitesFromServerManager)
{
#Processing each application for the processed website (from ServerManager)
foreach ($CurrentWebApplication in $CurrentWebsite.Applications)
{
#Creating an object with the application, the website, the website id, the application pool name, the pids (can be multiple in case of web gardening) and the instances (can be multiple in case of web gardening) from performance monitor
$ApplicationData = New-Object -TypeName PSObject -Property @{
Application = $($CurrentWebsite.Site+$CurrentWebApplication.Path)
Site = $CurrentWebsite.Site
SiteId = $CurrentWebsite.SiteId
ApplicationPool = $CurrentWebApplication.ApplicationPoolName
PID = @()
Instance = @()
}
#Processing each worker process from data coming from WMI
foreach ($CurrentW3WPData in $W3WPDataFromWebConfiguration)
{
#If the application pools are matching between the WMI and ServerManager data
if ($ApplicationData.ApplicationPool -eq $CurrentW3WPData.ApplicationPoolName)
{
#Adding the PID to the PID collection
$ApplicationData.PID += $CurrentW3WPData.PID
#Adding the worker process instance to the worker process instance collection
$ApplicationData.Instance += $W3WPDataFromPerformanceMonitorHT[$CurrentW3WPData.PID -as [string]].Instance
#The PID and the associated instance are stored in the same order into the two differents collections
}
}
#region Generating the W3SVC Path under the form _LM_W3SVC<ID>_ROOT[_APPLICATION] like _LM_W3SVC3_ROOT_HR
$W3SVCPath = $ApplicationData.Application.Substring($ApplicationData.Application.IndexOf('/')+1)
$W3SVCPath = $W3SVCPath -replace '/', '_'
if ($W3SVCPath)
{
$W3SVCPath = '_LM_W3SVC' + $ApplicationData.SiteId + '_ROOT_'+ $W3SVCPath
}
else
{
$W3SVCPath = '_LM_W3SVC' + $ApplicationData.SiteId + '_ROOT'
}
$W3SVCPath = $W3SVCPath.ToUpper()
#endregion
#Adding the W3SVC Path as a property of the object we have previously created
$ApplicationData | Add-Member -MemberType NoteProperty -Name 'W3SVCPath' -Value $W3SVCPath
#Storing the object into the array
$Data += $ApplicationData
}
}
#Returning the data
return $Data
}
#endregion
Clear-Host
$Data = Get-W3WPData
$Data | Format-Table -Property * -Force -AutoSize
Laurent.