首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 操作 Active Directory 完整指南:用戶與 OU 的增刪改查

     編輯:關於C語言

前置知識:AD 結構與權限

Active Directory (AD) 採用階層結構:網域 (Domain) → 組織單位 (OU) → 用戶/群組/電腦。所有用戶最終都隸屬於 Users 容器下的 Domain Users 群組。

權限關鍵:

  • 域管理員:可執行所有操作(建立、刪除、修改、查詢)
  • 一般用戶:僅能查詢自身屬性,無寫入權限

程式碼中需使用域管理員憑證建立 DirectoryEntry 才能執行寫入操作。

核心命名空間

using System.DirectoryServices;

查詢用戶:搜尋並讀取屬性

public void SearchUser(string domainName, string adminUser, string adminPassword)
{
    // 1. 取得 Domain Users 群組成員
    string groupPath = $"WinNT://{domainName}/Domain Users,group";
    using (DirectoryEntry group = new DirectoryEntry(groupPath, adminUser, adminPassword))
    {
        foreach (object member in (IEnumerable)group.Invoke("Members"))
        {
            using (DirectoryEntry userEntry = new DirectoryEntry(member))
            {
                string samAccountName = userEntry.Name;
                
                // 2. 使用 LDAP 過濾器精確查找
                string ldapPath = $"LDAP://{domainName}";
                string filter = $"(sAMAccountName={samAccountName})";
                
                using (DirectorySearcher searcher = new DirectorySearcher(ldapPath) { Filter = filter })
                {
                    SearchResult result = searcher.FindOne();
                    if (result != null)
                    {
                        using (DirectoryEntry user = result.GetDirectoryEntry())
                        {
                            // 3. 以管理員身分讀取完整屬性
                            string ouPath = user.Parent.Path;
                            using (DirectoryEntry ouEntry = new DirectoryEntry(ouPath, adminUser, adminPassword))
                            {
                                foreach (DirectoryEntry child in ouEntry.Children)
                                {
                                    if (child.SchemaClassName == "user" && 
                                        child.Properties["sAMAccountName"].Value?.ToString().ToLower() == samAccountName.ToLower())
                                    {
                                        foreach (string propName in child.Properties.PropertyNames)
                                        {
                                            object val = child.Properties[propName][0];
                                            Console.WriteLine($"{propName}: {val}");
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

新增用戶至指定 OU

public void AddUser(string ouLdapPath, string username, string chineseName, string adminUser, string adminPassword)
{
    try
    {
        using (DirectoryEntry ouEntry = new DirectoryEntry(ouLdapPath, adminUser, adminPassword))
        using (DirectoryEntry newUser = ouEntry.Children.Add($"CN={chineseName}", "user"))
        {
            newUser.Properties["userPrincipalName"].Value = username;
            newUser.Properties["name"].Value = chineseName;
            newUser.Properties["sAMAccountName"].Value = username;
            // 66048 = NORMAL_ACCOUNT (512) + DONT_EXPIRE_PASSWD (65536)
            newUser.Properties["userAccountControl"].Value = 66048;
            
            newUser.CommitChanges();
            Console.WriteLine($"用戶 {username} 建立成功");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"建立用戶失敗: {ex.Message}");
    }
}

新增組織單位 (OU)

public void AddOU(string parentOuLdapPath, string ouName, string adminUser, string adminPassword)
{
    try
    {
        using (DirectoryEntry parent = new DirectoryEntry(parentOuLdapPath, adminUser, adminPassword))
        using (DirectoryEntry newOu = parent.Children.Add($"OU={ouName}", "organizationalUnit"))
        {
            newOu.Properties["name"].Value = ouName;
            newOu.Properties["instanceType"].Value = 4;
            newOu.Properties["ou"].Value = ouName;
            // 選填屬性
            newOu.Properties["postalCode"].Value = "777";
            
            newOu.CommitChanges();
            Console.WriteLine($"OU {ouName} 建立成功");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"建立 OU 失敗: {ex.Message}");
    }
}

修改用戶屬性

public void ModifyUser(string domainName, string samAccountName, string propertyName, string newValue, string adminUser, string adminPassword)
{
    try
    {
        string filter = $"(sAMAccountName={samAccountName})";
        using (DirectorySearcher searcher = new DirectorySearcher($"LDAP://{domainName}") { Filter = filter })
        {
            SearchResult result = searcher.FindOne();
            if (result != null)
            {
                string ouPath = result.GetDirectoryEntry().Parent.Path;
                using (DirectoryEntry ouEntry = new DirectoryEntry(ouPath, adminUser, adminPassword))
                {
                    foreach (DirectoryEntry user in ouEntry.Children)
                    {
                        if (user.SchemaClassName == "user" && 
                            user.Properties["sAMAccountName"].Value?.ToString().ToLower() == samAccountName.ToLower())
                        {
                            user.Properties[propertyName].Value = newValue;
                            user.CommitChanges();
                            Console.WriteLine($"用戶 {samAccountName} 的 {propertyName} 已更新為 {newValue}");
                            break;
                        }
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"修改失敗: {ex.Message}");
    }
}

刪除用戶或 OU

public void DeleteObject(string ldapPath, string adminUser, string adminPassword)
{
    try
    {
        using (DirectoryEntry obj = new DirectoryEntry(ldapPath, adminUser, adminPassword))
        using (DirectoryEntry parent = obj.Parent)
        {
            parent.Children.Remove(obj);
            Console.WriteLine($"物件 {ldapPath} 已刪除");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"刪除失敗: {ex.Message}");
    }
}

最佳實務提醒

  • 使用 using 區塊:確保 DirectoryEntry/DirectorySearcher 正確釋放非託管資源
  • LDAP 路徑格式:LDAP://OU=部門,OU=分公司,DC=domain,DC=com,層級由下而上
  • 屬性名稱大小寫:AD 屬性不區分大小寫,但建議使用標準 PascalCase(如 sAMAccountName)
  • 分頁查詢:大量資料時設定 searcher.PageSize = 1000 避免伺服器限制
  • 例外處理:捕獲 DirectoryServicesCOMException 可取得詳細錯誤碼
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved