手動在 IIS 管理員建立虛擬目錄、設定權限、預設文件、SSL、驗證方式,既繁瑣又易出錯。System.DirectoryServices 命名空間封裝了 ADSI(Active Directory Service Interfaces),允許以 LDAP 風格路徑(IIS://server/W3SVC/1/ROOT)操作 IIS Metabase。本文提供的 IISManager 類別封裝了連線、虛擬目錄 CRUD、批次更新、屬性同步等核心功能,可直接整合至部署腳本、安裝精靈、DevOps 自動化流程。
using System;
using System.Collections;
using System.DirectoryServices;
namespace ASPcn.Management
{
public class IISManager
{
private string _server = "localhost";
private string _siteId = "1";
private VirtualDirectories _virDirs;
private DirectoryEntry _root;
private bool _batchMode;
public string Server { get => _server; set => _server = value; }
public string WebSite { get => _siteId; set => _siteId = value; }
public VirtualDirectories VirDirs => _virDirs;
public IISManager() { }
public IISManager(string server) { _server = server; }
public IISManager(string server, string siteId) { _server = server; _siteId = siteId; }
public void Connect()
{
string path = $"IIS://{_server}/W3SVC/{_siteId}/ROOT";
try
{
_root = new DirectoryEntry(path);
_virDirs = GetVirDirs(_root.Children);
}
catch (Exception ex)
{
throw new Exception($"無法連線至 IIS 伺服器 [{_server}]", ex);
}
}
public bool Exists(string name) => _virDirs.Contains(name);
public void Create(VirtualDirectory vdir)
{
string path = $"IIS://{_server}/W3SVC/{_siteId}/ROOT/{vdir.Name}";
if (!_virDirs.Contains(vdir.Name) || _batchMode)
{
try
{
var newDir = _root.Children.Add(vdir.Name, "IIsWebVirtualDir");
newDir.Invoke("AppCreate", true);
newDir.CommitChanges();
_root.CommitChanges();
UpdateDirInfo(newDir, vdir);
}
catch (Exception ex) { throw new Exception("建立虛擬目錄失敗", ex); }
}
else throw new Exception("虛擬目錄已存在");
}
public VirtualDirectory GetVirDir(string name)
{
if (!_virDirs.Contains(name)) throw new Exception("虛擬目錄不存在");
var vdir = _virDirs.Find(name);
vdir.Flag = 2; // 標記為更新狀態
return vdir;
}
public void Update(VirtualDirectory vdir)
{
if (!_virDirs.Contains(vdir.Name)) throw new Exception("虛擬目錄不存在");
var de = _root.Children.Find(vdir.Name, "IIsWebVirtualDir");
UpdateDirInfo(de, vdir);
}
public void Delete(string name)
{
if (!_virDirs.Contains(name)) throw new Exception("虛擬目錄不存在");
_root.Invoke("Delete", new object[] { "IIsWebVirtualDir", name });
_root.CommitChanges();
}
public void UpdateBatch() => BatchUpdate(_virDirs);
public void UpdateBatch(VirtualDirectories vdirs) => BatchUpdate(vdirs);
private void BatchUpdate(VirtualDirectories vdirs)
{
_batchMode = true;
foreach (VirtualDirectory vd in vdirs.Values)
{
switch (vd.Flag)
{
case 1: Create(vd); break;
case 2: Update(vd); break;
}
}
_batchMode = false;
}
private void UpdateDirInfo(DirectoryEntry de, VirtualDirectory vd)
{
de.Properties["AccessRead"][0] = vd.AccessRead;
de.Properties["AccessWrite"][0] = vd.AccessWrite;
de.Properties["AccessExecute"][0] = vd.AccessExecute;
de.Properties["AccessScript"][0] = vd.AccessScript;
de.Properties["AccessSSL"][0] = vd.AccessSSL;
de.Properties["AuthAnonymous"][0] = true; // 隱含
de.Properties["AnonymousUserName"][0] = vd.AnonymousUserName;
de.Properties["AnonymousUserPass"][0] = vd.AnonymousUserPass;
de.Properties["AuthBasic"][0] = vd.AuthBasic;
de.Properties["AuthNTLM"][0] = vd.AuthNTLM;
de.Properties["ContentIndexed"][0] = vd.ContentIndexed;
de.Properties["EnableDefaultDoc"][0] = vd.EnableDefaultDoc;
de.Properties["EnableDirBrowsing"][0] = vd.EnableDirBrowsing;
de.Properties["DefaultDoc"][0] = vd.DefaultDoc;
de.Properties["Path"][0] = vd.Path;
de.CommitChanges();
}
private VirtualDirectories GetVirDirs(DirectoryEntries entries)
{
var dict = new VirtualDirectories();
foreach (DirectoryEntry de in entries)
{
if (de.SchemaClassName == "IIsWebVirtualDir")
{
var vd = new VirtualDirectory(de.Name)
{
AccessRead = (bool)de.Properties["AccessRead"][0],
AccessWrite = (bool)de.Properties["AccessWrite"][0],
AccessExecute = (bool)de.Properties["AccessExecute"][0],
AccessScript = (bool)de.Properties["AccessScript"][0],
AccessSSL = (bool)de.Properties["AccessSSL"][0],
AuthBasic = (bool)de.Properties["AuthBasic"][0],
AuthNTLM = (bool)de.Properties["AuthNTLM"][0],
ContentIndexed = (bool)de.Properties["ContentIndexed"][0],
EnableDefaultDoc = (bool)de.Properties["EnableDefaultDoc"][0],
EnableDirBrowsing = (bool)de.Properties["EnableDirBrowsing"][0],
DefaultDoc = (string)de.Properties["DefaultDoc"][0],
Path = (string)de.Properties["Path"][0],
AnonymousUserName = (string)de.Properties["AnonymousUserName"][0],
AnonymousUserPass = (string)de.Properties["AnonymousUserPass"][0],
Flag = 0
};
dict.Add(vd.Name, vd);
}
}
return dict;
}
}
public class VirtualDirectory
{
public int Flag { get; set; }
public string Name { get; set; }
public string Path { get; set; } = @"C:\";
public string DefaultDoc { get; set; } = "default.htm,default.aspx,default.asp,index.htm";
public bool AccessRead { get; set; } = true;
public bool AccessWrite { get; set; }
public bool AccessExecute { get; set; }
public bool AccessScript { get; set; }
public bool AccessSSL { get; set; }
public bool AuthBasic { get; set; }
public bool AuthNTLM { get; set; } = true;
public bool ContentIndexed { get; set; }
public bool EnableDefaultDoc { get; set; } = true;
public bool EnableDirBrowsing { get; set; }
public string AnonymousUserName { get; set; } = "";
public string AnonymousUserPass { get; set; } = "";
public VirtualDirectory() { }
public VirtualDirectory(string name) { Name = name; }
}
public class VirtualDirectories : Hashtable
{
public VirtualDirectory Find(string name) => this[name] as VirtualDirectory;
}
}
var iis = new IISManager("192.168.1.100", "1"); // 遠端伺服器,站點 ID 1
iis.Connect();
// 建立新虛擬目錄
var vdir = new VirtualDirectory
{
Name = "MyApp",
Path = @"D:\WebApps\MyApp",
AccessRead = true,
AccessScript = true,
AccessWrite = false,
AuthNTLM = true,
AuthBasic = false,
EnableDefaultDoc = true,
DefaultDoc = "index.aspx,default.aspx",
AnonymousUserName = "IUSR_WEB",
AnonymousUserPass = "P@ssw0rd"
};
iis.Create(vdir);
// 批次更新多個虛擬目錄
iis.VirDirs["OldApp"].AccessWrite = true;
iis.VirDirs["OldApp"].Flag = 2; // 標記更新
iis.UpdateBatch();
Console.WriteLine("部署完成");
IIS://server/W3SVC/siteId/ROOT/vdirName
// 範例:IIS://localhost/W3SVC/1/ROOT/MyApp
W3SVC:Web 服務siteId:網站識別碼(預設網站通常為 "1")ROOT:網站根節點IIsWebVirtualDir:虛擬目錄 Schema 類別名newDir.Invoke("AppCreate", true) 將虛擬目錄標記為 IIS 應用程式(獨立應用程式域、獨立 web.config、可設定應用程式池)。對應操作:AppDelete、AppUnload。
| VirtualDirectory 屬性 | Metabase 屬性 | 說明 |
|---|---|---|
| AccessRead | AccessRead | 讀取靜態檔案 |
| AccessWrite | AccessWrite | 寫入/上傳 |
| AccessScript | AccessScript | 執行腳本 |
| AccessExecute | AccessExecute | 執行可執行檔 |
| AccessSSL | AccessSSL | 要求 SSL |
| AuthBasic | AuthBasic | 基本驗證 |
| AuthNTLM | AuthNTLM | 整合 Windows 驗證 |
| EnableDirBrowsing | EnableDirBrowsing | 目錄瀏覽 |
| DefaultDoc | DefaultDoc | 預設文檔清單 |
| Path | Path | 實體路徑 |
Flag 欄位標記狀態:0=無變更、1=新建、2=更新。UpdateBatch 統一提交,減少 Metabase 寫入次數,提升效能。
IIS 7 引入新設定系統,建議使用 Microsoft.Web.Administration (nuget: Microsoft.Web.Administration):
using Microsoft.Web.Administration;
var server = new ServerManager();
var site = server.Sites["Default Web Site"];
var app = site.Applications.Add("/MyApp", @"D:\WebApps\MyApp");
app.VirtualDirectories[0].PhysicalPath = @"D:\WebApps\MyApp";
app.VirtualDirectories[0].LogonMethod = LogonMethod.ClearText;
app.VirtualDirectories[0].UserName = "IUSR_WEB";
app.VirtualDirectories[0].Password = "P@ssw0rd";
server.CommitChanges();
新 API 強型別、支援應用程式池管理、模組管線模式、集中式 XML 設定,為現代 IIS 自動化首選。
IISManager 以 System.DirectoryServices 封裝 IIS 6 Metabase 操作,提供物件導向、批次提交、屬性同步的完整介面。理解其原理後,可輕鬆遷移至 IIS 7+ 的 Microsoft.Web.Administration,實現從傳統部署腳本到現代 IaC(基礎設施即代碼)的演進。