【潛盾機】列出IIS上所有ASP.NET網站應用程式

實務上常有多個Web Application裝在同一台IIS主機的情形,於是我們常會有列舉IIS上所有Web Application清單以便進一步管理、維護的需求。

在IIS7上,微軟推出了Microsoft.Web.Administration.dll,支援用LINQ的方式查詢及修改網站設定(使用方法可參考這篇文章)。無奈在我的工作環境裡,還有很多IIS 6,甚至IIS 5,仍健壯地活著。因此我也只能含淚送Microsoft.Web.Administration一張好人卡,乖乖用ADSI或WMI解決問題。

我寫了一段工具函數,透過ADSI將整個IIS Meta資料轉成XDocument,之後便可用LINQ to XML進行各式複雜查詢,算是"雖不滿意但可接受"的簡便做法。

產生的XML長得像這樣:

為了延續.NET好威的傳統,程式照例要維持在100行以內解決。其實主要不過就是將DirectoryEntry的結構依樣轉換對照成XElement的結構,運用一下只應天上有的遞迴(To iterate is human, to recurse is divine. by L. Peter Deutsch,有人翻成"遞迴只應天上有、凡人該當用迴圈",妙哉!),三兩下就將整個IIS資料轉成XML文件囉! 因為轉資料過程有些漫長,我設計了一個回呼函數Action<string> progressCallback讓呼叫端可以持續收到處理過程的資訊,好讓使用者感受到電腦真的很忙,不是當掉了。

我在應用時,格外關心ASP.NET版本(有不少IIS上是ASP.NET 1.1與ASP.NET 2.0程式並存)以及網站所在目錄,因此擷取屬性資料時會額外判讀ASP.NET runtime版本,轉存為IIsWebVirtualDir XML元素的屬性,以方便查詢應用。(關於ASP.NET Runtime版本(v1.1, v2.0 or v4.0),IIS6用aspx註冊的ISAPI DLL路徑判斷,IIS7則要由依AppPool設定而定,不過針對IIS7我懶得安裝v1.1環境,所以程式中只區分v2.0及v4.0,未考慮v1.1,如果有朋友願意提供判斷邏輯,請留言。)

using System;
using System.Linq;
using System.Xml.Linq;
using System.DirectoryServices;
using System.Collections.Generic;
 
 
class IISDataHelper
{
    public static XDocument ReadSettings(string ip,
        string uid, string pwd, Action<string> progressCallback)
    {
        string path = String.Format("IIS://{0}/W3SVC", ip);
        DirectoryEntry w3svc =
            string.IsNullOrEmpty(uid) ?
            new DirectoryEntry(path) :
            new DirectoryEntry(path, uid, pwd);
        XDocument xd = XDocument.Parse("<root />");
        pools.Clear();
        exploreTree(w3svc, xd.Root, progressCallback);
        return xd;
    }
 
    static Dictionary<string, string> pools = 
        new Dictionary<string, string>();
 
    private static void exploreTree(
        DirectoryEntry de, XElement xe, Action<string> cb)
    {
        foreach (DirectoryEntry childEntry in de.Children)
        {
            XElement childElement = new XElement(
                childEntry.SchemaClassName,
                new XAttribute("Name", childEntry.Name),
                new XAttribute("Path", childEntry.Path)
                );
            //Get properties
            XElement propNode = new XElement("Properties");
            foreach (PropertyValueCollection pv in childEntry.Properties)
            {
                //Array
                if (pv.Value != null &&
                    pv.Value.GetType().IsArray)
                {
                    XElement propCol = new XElement(pv.PropertyName);
                    foreach (object obj in pv.Value as object[])
                    {
                        string v = Convert.ToString(obj);
                        //Set ASP.NET version
                        if (pools.Count == 0 && pv.PropertyName == "ScriptMaps"
                            && v.StartsWith(".aspx,"))
                        {
                            string aspNetVer =
                                v.Split(',')[1].Split('\\')
                                .Single(o => o.StartsWith("v"));
                            childElement.Add(
                                new XAttribute("AspNetVer", aspNetVer));
                        }
                        propCol.Add(new XElement("Entry", v));
                    }
                    propNode.Add(propCol);
                }
                else
                {
                    string v = Convert.ToString(pv.Value);
                    propNode.Add(new XElement(pv.PropertyName, v));
                //Set home directory
                if (pv.PropertyName == "Path")
                    childElement.Add(new XAttribute("HomeDir", v));
                //Try to find the runtime version
                else if (pools.Count > 0 && 
                    pv.PropertyName == "AppPoolId" && pools.ContainsKey(v))
                    childElement.Add(
                        new XAttribute("AspNetVer", pools[v]));
                }
            }
            //For IIS 7, use AppPool to decide ASP.NET runtime version
            if (childEntry.SchemaClassName == "IIsApplicationPool")
            {
                XElement runtimeVer = propNode.Element("ManagedRuntimeVersion");
                string ver = runtimeVer != null ? runtimeVer.Value : "v2.0";
                pools.Add(childEntry.Name, ver);
            }
            childElement.Add(propNode);
            
            xe.Add(childElement);
            if (cb != null) cb(childEntry.Name);
            exploreTree(childEntry, childElement, cb);
        }
    }
}

最後附上應用範例。以下程式會列出IIS上所有ASP.NET的版本、名稱及所在目錄:

using System;
using System.Linq;
using System.Xml.Linq;
 
namespace ListAspNetWebApp
{
    class Program
    {
        static void Main(string[] args)
        {
            XDocument xd =
                IISWebAppViewer.IISDataHelper.ReadSettings(
                    "192.168.1.101", @"domain\user", "password", 
                    (s) => 
                    {
                        //利用\r在同一列顯示目前處理進度
                        Console.Write("\rProcessing " + s.PadRight(50));
                    }
                );
            Console.WriteLine("\rDone!{0}", new String(' ', 30));
            var q = from o in xd.Root.Descendants()
                    where o.Attribute("HomeDir") != null &&
                          o.Attribute("AspNetVer") != null
                    select o;
            foreach (var o in q)
                Console.WriteLine(
                    "ASP.NET {0} Web [{1}] at {2}",
                    o.Attribute("AspNetVer").Value,
                    o.Attribute("Name").Value,
                    o.Attribute("HomeDir").Value);
            Console.Read();
        }
    }
}

結果如下,很方便吧!

Done!
ASP.NET v1.1.4322 Web [Root] at D:\wwwroot
ASP.NET v1.1.4322 Web [Scripts] at c:\inetpub\scripts
ASP.NET v1.1.4322 Web [IISHelp] at c:\winnt\help\iishelp
ASP.NET v1.1.4322 Web [IISAdmin] at C:\WINNT\System32\inetsrv\iisadmin
ASP.NET v1.1.4322 Web [IISSamples] at c:\inetpub\iissamples
ASP.NET v1.1.4322 Web [MSADC] at c:\program files\common files\system\msadc
ASP.NET v1.1.4322 Web [_vti_bin] at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\40\isapi
ASP.NET v1.1.4322 Web [Printers] at C:\WINNT\web\printers

歡迎推文分享:
Published 02 September 2010 09:10 AM 由 Jeffrey
Filed under: , ,
Views: 11,146



意見

# Amos said on 21 September, 2011 05:45 AM

那請問一下,有辦法取得IIS站台上,所有client或某個站台的連線資訊,例如session id 、remote ip等資訊嗎?

# Amolo said on 11 September, 2017 01:42 AM

在運行此代碼前,本機要先有安裝IIS 6 Metabase Compatiblity,不然運行時會出現0x80005000的不明錯誤

# Amolo said on 11 September, 2017 02:45 AM

再補充下

if (childEntry.SchemaClassName == "IIsApplicationPool")

以上的判斷是IIS 7.0以上版本,如果要肩容IIS 6的話,需改寫為

if (childEntry.SchemaClassName == "IIsApplicationPool" || childEntry.SchemaClassName == "IIsApplicationPools")

這樣就可以抓到像是.NET 1.1的版本囉~

# Jeffrey said on 12 September, 2017 09:43 AM

to Amolo,感謝補充。一併補上 PowerShell 做法: blog.darkthread.net/post-2016-10-22-wmi-export-iis6-setting.aspx

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication