前一篇文章成功將 IIS 6 網站設定匯出成 JSON,不過原始資料太過龐雜,每筆虛擬目錄屬性超過140條,讓人眼花瞭亂。事實上因 IIS 設定具有繼承性,父目錄與子目錄的屬性絕大部分是相同的,針對某個虛擬目錄做的額外設定才是觀注焦點。例如:掛在可匿名存取 P 目錄下的 C 目錄被設成整合式驗證,描述 C 目錄設定時時只要列出 AuthNTLM = true 就好,與 P 目錄相同的設定可以全部省略。為實現這點,我想到一個簡單有效的演算法:拿子目錄的所有屬性跟父目錄比較,只顯示有差異部分。

WMI 匯出的 IISWebVirtualDirSetting 資料為 JSON 格式,轉為強型別處理起來才順手。講到這個不得不推 Visual Studio 強大的 Paste JSON As Classes 功能:選取 IISWebVirtualDirSetting 匯出的全部 JSON 內容,點選「Edit / Paste Special / Paste JSON As Classes」:

見證奇蹟的時刻… Visual Studio 自動依 JSON 資料產生對應型別,連 HttpCustomHeader 這種物件陣列屬性,陣列元素物件也被轉成強型別!

將 public class Class1 更名並轉為部分宣告 public partial class VirDirSetting,我另外再補上額外屬性及方法方便後續處理:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
 
namespace WmiDataAnalyzer
{
    public partial class VirDirSetting
    {
        //所屬子網站
        public List<VirDirSetting> Children = new List<VirDirSetting>();
        //父網站名稱
        public string ParentName;
        //父網站物件
        public VirDirSetting Parent;
        //所有可能的父網站名稱
        public string[] AncestorNames
        {
            get
            {
                var p = this.Name.Split('/');
                return Enumerable.Range(1, p.Length - 1)
                    .Select(i => string.Join("/", p.Take(i).ToArray())).ToArray();
            }
        }
        //層級深度
        public int Level
        {
            get { return this.Name.Split('/').Length - 2; }
        }
        //動態比對屬性用屬性集合
        static Dictionary<string, PropertyInfo> Properties
        {
            get
            {
                return typeof(VirDirSetting)
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where(o => !"AncestorNames,Level,ScriptMaps".Split(',').Contains(o.Name))
                    .ToDictionary(o => o.Name, o => o);
 
            }
        }
        //與Parent比較,找出有差異的設定
        public Dictionary<string, string> GetExplicitSettings()
        {
            var diff = new Dictionary<string, string>();
            if (this.Parent == null)
            {
                diff.Add("Remark", "**Root**");
            }
            else
            {
                foreach (var p in VirDirSetting.Properties.Values)
                {
                    var pv = JsonConvert.SerializeObject(p.GetValue(this.Parent));
                    var cv = JsonConvert.SerializeObject(p.GetValue(this));
                    if (pv.CompareTo(cv) != 0)
                        diff.Add(p.Name, cv);
                }
            }
            return diff;
        }
    }
}

寫一小段程式將 JSON 反序列化為物件集合,並找出彼此從屬關係,再印出網站結構及差異設定:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace WmiDataAnalyzer
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, VirDirSetting> data = 
                JsonConvert.DeserializeObject<VirDirSetting[]>(
                File.ReadAllText("Sample.json")).ToDictionary(o => o.Name, o => o);
            var roots = new List<VirDirSetting>();
            //建立從屬關係
            foreach (var vds in data.Values)
            {
                foreach (var anc in vds.AncestorNames)
                {
                    if (data.ContainsKey(anc))
                    {
                        vds.ParentName = anc;
                        vds.Parent = data[anc];
                        vds.Parent.Children.Add(vds);
                        break;
                    }
                }
                if (string.IsNullOrEmpty(vds.ParentName))
                {
                    roots.Add(vds);
                }
            }
            //印出從屬關係
            foreach (var root in roots)
            {
                DumpStructure(root);
            }
            Console.Read();
        }
 
        static void DumpStructure(VirDirSetting vds)
        {
            Console.WriteLine("{0}[{1}]", 
                new string(' ', vds.Level * 4), vds.Name);
            foreach (var kv in vds.GetExplicitSettings())
                Console.WriteLine("{2} *{0}:{1}", kv.Key, kv.Value,
                    new string(' ', vds.Level * 4 ));
            foreach (var child in vds.Children)
            {
                DumpStructure(child);
            }
        }
    }
}

我弄了一個測試網站結構如下,建立多個虛擬目錄並故意加入設定差異,例如:驗證方式、IP限制、不同ApplicationPool… 等等。

產生結果如下:

有少部分設定細節未包含在 IISWebVirtualDirSetting 中,要藉由其他 WMI 資料才能拼湊出完整設定。例如:虛擬目錄是否為網站應用程式可由  IISWebVirtualDir "AppRoot": null 與否判斷;IP 限制則要查詢 IISIpSecuritySetting,限定 IP 時會得到如下結果:

  {
    "Caption": null,
    "Description": null,
    "DomainDeny": [],
    "DomainGrant": [],
    "GrantByDefault": false,
    "IPDeny": [],
    "IPGrant": [
      "127.0.0.1, 255.255.255.255",
      "192.168.1.78, 255.255.255.255"
    ],
    "Name": "W3SVC/1/ROOT/ParentWebApp/UNCVirtualDir",
    "SettingID": null
  },

由這些 WMI 資料,我們就能描繪現有網站的結構,進行檢視調整,並做為撰寫設定 IIS 網站自動腳本的依據。


Comments

Be the first to post a comment

Post a comment