1. 程式人生 > >C#操作AD及Exchange Server總結(一)

C#操作AD及Exchange Server總結(一)

這篇部落格的目的:根據親身專案經歷,總結對AD及Exchange Server的操作,包括新建AD使用者,設定密碼,為AD使用者建立郵箱等。

本文完全原創,轉載請說明出處,希望對大家有用。

文件目錄:

  • 測試環境及需求簡述
  • 對AD操作
    • 引入DLL及方法簡述
    • 新增OU或Security Group
    • 新建AD User
    • 新增使用者到組或從組中刪除使用者
    • 使用者資訊更新
    • Enable/Disable使用者賬號
  • 對Exchange Server操作
    • 為AD使用者新建郵箱
    • 配置客戶端和伺服器
  • 總結歸納

一、測試環境及需求簡述

1、測試環境

伺服器:windows server 2008R2

Exchange:Exchange Server 2010 sp1

開發工具:Visual Studio 2010

一臺exchange伺服器+一臺AD伺服器+一臺承載測試程式的伺服器

2、需求簡述

  • 根據提供的資訊建立對應的OU
  • 根據提供的使用者資訊新建AD使用者
  • 根據提供的資訊修改AD使用者
  • 為AD使用者

二、AD操作

1、引入DLL及方法簡述

MS提供了遠端操作AD的DLL:System.DirectoryServices(新增引用中有);

其中,我們使用LDAP協議訪問AD,LDAP翻譯為輕量目錄訪問協議。

在使用的時候,需要注意一些問題:

  • 如果在WEB應用中使用,在資料量大的時候回產生超時的問題,建議採取其他方式如MS MQ等方式處理資訊,避免超時的問題。
  • 使用LDAP會有安全風險,畢竟通過網路傳輸使用者憑證並不是很安全,最好是執行在企業內網。

DirectoryServices其實提供了其他很多操作,如對IIS的操作,對本地使用者的操作,有興趣的可以瞭解下。

2、新增OU或Security Group

 首先新建一個控制檯應用程式

新增服務引用:

在專案中新建一個ADHelper.cs用來提供AD操作的公用方法

複製程式碼
public class ADHelper
    {
        /// <summary>
        /// 建立連線
        /// </summary>
        /// <returns></returns>
        public static DirectoryEntry GetDirectoryEntry()
        {
            DirectoryEntry de = new DirectoryEntry();
            de.Path = "LDAP://AD伺服器地址/OU=CompanyA,DC=contoso,DC=com";
            de.Username = @"contoso\管理員賬號";
            de.Password = "管理員密碼";
            return de;
        }

        /// <summary>
        /// 帶有一個引數的建立連線過載
        /// </summary>
        /// <param name="DomainReference"></param>
        /// <returns></returns>
        public static DirectoryEntry GetDirectoryEntry(string DomainReference)
        {
            DirectoryEntry entry = new DirectoryEntry(DomainReference, "管理員賬號", "管理員密碼", AuthenticationTypes.Secure);
            return entry;
        }
    }
複製程式碼 ADHelper

 ADHelper程式碼解釋:

  1. 新建一個DirectoryEntry類,也就是活動目錄的入口類
  2. 指定要連線到的Path,在稍後的新建OU例項中會詳細解釋Path的組成
  3. 用來連線AD的管理員賬號,此管理員賬號必須有操作AD的許可權
  4. 管理員的密碼,同樣是為了連線AD
  5. 過載的GetDirectoryEntry是為了根據輸入的路徑引用此路徑的入口,稍後會用到

 新建一個ADManage.cs操作類,用來定義具體的操作方法:

複製程式碼
/// <summary>
        /// 新建OU
        /// </summary>
        /// <param name="path"></param>
        public void CreateOU(string name)
        {
            if (!ObjectExists(name, "OU"))
            {
                DirectoryEntry dse = ADHelper.GetDirectoryEntry();
                DirectoryEntries ous = dse.Children;
                DirectoryEntry newou = ous.Add("OU=" + name, "OrganizationalUnit");
                newou.CommitChanges();
                newou.Close();
                dse.Close();
            }
            else
            {
                Console.WriteLine("OU已存在");
            }
        }
複製程式碼 CreateOU

新建OU程式碼解釋:

  1. ObjectExists方法判斷新增的OU是否已經存在,程式碼下面會附上
  2. 利用ADHelper生成目錄入口,本例是在一個測試的CompanyA OU中
  3. Children屬性獲取所有子項,並使用Add方法新增OU
  4. 提交更改,發回伺服器
複製程式碼
 /// <summary>
        /// 新建Security Group
        /// </summary>
        /// <param name="path"></param>
        public void CreateGroup(string name)
        {
            if (!ObjectExists(name, "Group"))
            {
                DirectoryEntry dse = ADHelper.GetDirectoryEntry();
                DirectoryEntries Groups = dse.Children;
                DirectoryEntry newgroup = Groups.Add("CN=" + name, "group");
                newgroup.CommitChanges();
                newgroup.Close();
                dse.Close();
            }
            else
            {
                Console.WriteLine("使用者組已存在");
            }
        }
複製程式碼 CreateGroup 複製程式碼
/// <summary>
       /// 判斷是否存在
       /// </summary>
       /// <param name="objectName"></param>
       /// <param name="catalog"></param>
       /// <returns></returns>
        public bool ObjectExists(string objectName, string catalog)
        {
            DirectoryEntry de = ADHelper.GetDirectoryEntry();
            DirectorySearcher deSearch = new DirectorySearcher();
            deSearch.SearchRoot = de;
            switch (catalog)
            {
                case "User": deSearch.Filter = "(&(objectClass=user) (cn=" + objectName + "))"; break;
                case "Group": deSearch.Filter = "(&(objectClass=group) (cn=" + objectName + "))"; break;
                case "OU": deSearch.Filter = "(&(objectClass=OrganizationalUnit) (OU=" + objectName + "))"; break;
                default: break;
            }
            SearchResultCollection results = deSearch.FindAll();
            if (results.Count == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
複製程式碼 ObjectExists

操作方法寫好後,我們就來測試一下

在Program中寫測試程式碼:

複製程式碼
static void Main(string[] args)
        {
            ADManage manage=new ADManage();
            //Test create ou
            Console.WriteLine("Create OU Start...");
            try
            {
                manage.CreateOU("NewOU01");
                Console.WriteLine("Create OU Finish...");
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Create OU Error...");
                Console.WriteLine(ex);
                Console.ReadLine();
            }

            //Test create group
            Console.WriteLine("Create Group Start...");
            try
            {
                manage.CreateGroup("NewGroup01");
                Console.WriteLine("Create Group Finish...");
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Create Group Error...");
                Console.WriteLine(ex);
                Console.ReadLine();
            }
        }
複製程式碼 測試程式碼

執行程式碼

 

域控中檢視結果:

補充:

如何在多層OU下建立新的OU?

OU類似於資料夾,在不同的目錄中可以有相同名字的OU,想要在多層OU下建立OU,首先要確認路徑Path

假設有一個OU,path為:

CompanyA

BranchB

DepartmentC

則GetDirectoryEntry例項中的path屬性應該修改為:

“LDAP://AD伺服器地址/OU=DepartmentC,OU=BranchB,OU=CompanyA,DC=contoso,DC=com”,這樣新建的OU就會在DepartmentC目錄下。

3、新建AD User

新建User與新建OU或組有一些不同,先看程式碼:

複製程式碼
/// <summary>
        /// 新建使用者
        /// </summary>
        /// <param name="name"></param>
        /// <param name="login"></param>
        public void CreateUser(string name, string login)
        {
            if (ObjectExists(login, "User"))
            {
                Console.WriteLine("使用者已存在");
                Console.ReadLine();
                return;
            }
            DirectoryEntry de = ADHelper.GetDirectoryEntry();
            DirectoryEntries users = de.Children;
            DirectoryEntry newuser = users.Add("CN=" + login, "user");
            SetProperty(newuser, "givenname", name);
            SetProperty(newuser, "SAMAccountName", login);
            SetProperty(newuser, "userPrincipalName", login + "@contoso.com");
            newuser.CommitChanges();
            SetPassword(newuser.Path);
            newuser.CommitChanges();     
            newuser.Close();
            de.Close();
        }

        /// <summary>
        /// 屬性設定
        /// </summary>
        /// <param name="de"></param>
        /// <param name="PropertyName"></param>
        /// <param name="PropertyValue"></param>
        public static void SetProperty(DirectoryEntry de, string PropertyName, string PropertyValue)
        {
            if (PropertyValue != null)
            {
                if (de.Properties.Contains(PropertyName))
                {
                    de.Properties[PropertyName][0] = PropertyValue;
                }
                else
                {
                    de.Properties[PropertyName].Add(PropertyValue);
                }
            }
        }

        /// <summary>
        /// 密碼設定
        /// </summary>
        /// <param name="path"></param>
        public void SetPassword(string path)
        {
            DirectoryEntry user = new DirectoryEntry();
            user.Path = path;
            user.AuthenticationType = AuthenticationTypes.Secure;
            object ret = user.Invoke("SetPassword",new object[] {"Password01!"});
            user.CommitChanges();
            user.Close();
        }
複製程式碼 CreateUser

新建使用者程式碼解釋:

  1. 利用ObjectExists判斷使用者是否存在,如果存在則提示使用者已存在
  2. 新建入口類例項,Add方法新增使用者
  3. SetProperty設定新使用者的屬性(顯示名、Pre-Windows 2000登入名、登入名),並提交更改
  4. SetPassword設定使用者初始密碼,提交更改,關閉連線

編寫測試程式碼:

複製程式碼
static void Main(string[] args)
        {
            ADManage manage=new ADManage();
            Console.WriteLine("Create User Start...");
            try
            {
                manage.CreateUser("Employee John", "Employee01");
                Console.WriteLine("Create User Finish...");
                Console.ReadLine();
            }
            catch (System.DirectoryServices.DirectoryServicesCOMException ex)
            {
                Console.WriteLine("Create User Error...");
                Console.WriteLine(ex);
                Console.ReadLine();
            }
        }
複製程式碼 測試程式碼

測試結果:

注意:此時新建的賬戶是停用狀態,後面的章節會介紹如何啟用/停用

 4、新增使用者到組或從組中刪除使用者

 在組中新增/刪除使用者使用到DirectorySearcher,用來查詢組,見程式碼:

複製程式碼
 /// <summary>
        /// 新增使用者到組
        /// </summary>
        /// <param name="de"></param>
        /// <param name="userDn"></param>
        /// <param name="GroupName"></param>
        public void AddUserToGroup(DirectoryEntry de, string userDn, string GroupName)
        {
            DirectorySearcher deSearch = new DirectorySearcher();
            deSearch.SearchRoot = de;
            deSearch.Filter = "(&(objectClass=group) (cn=" + GroupName + "))";
            SearchResult Groupresult = deSearch.FindOne();
            if (Groupresult != null)
            {
                DirectoryEntry user = ADHelper.GetDirectoryEntry("LDAP://AD伺服器/" + userDn);
                if (user != null)
                {
                    DirectoryEntry dirEntry = Groupresult.GetDirectoryEntry();
                    if (dirEntry.Properties["member"].Contains(userDn))
                    {
                        Console.WriteLine("使用者組中已存在該使用者,即將移除");
                        dirEntry.Properties["member"].Remove(userDn);
                        Console.WriteLine("使用者已從組中移除");
                    }
                    else
                    {
                        dirEntry.Properties["member"].Add(userDn);
                        Console.WriteLine("新增成功,使用者已新增到組");
                    }
                    dirEntry.CommitChanges();
                    dirEntry.Close();
                }
                else
                {
                    Console.WriteLine("使用者不存在");
                }
                user.Close();
            }
            else
            {
                Console.WriteLine("使用者組不存在");
            }
            return;
        }
複製程式碼 AddUserToGroup

程式碼解釋:

  1. 新建DirectorySearcher例項,為Filter賦值,根據傳入的引數de目錄查詢該安全組(注意:此組需要包含在DirectoryEntry中)
  2. 根據引數userDn判斷使用者是否存在(userDn是使用者的標識名如:“CN=Employee01,OU=CompanyA,DC=rzh,DC=com”)
  3. dirEntry.Properties["member"].Contains(userDn)判斷組中是否存在該使用者
  4. 如果該組不存在該使用者,則新增使用者到組。如果該組中存在該使用者,則將該使用者從組中移除

測試一下,這裡只測試新增,移除操作請自行測試:

複製程式碼
class Program
    {
        static void Main(string[] args)
        {
            ADManage manage=new ADManage();
            Console.WriteLine("Add user to group Start...");
            try
            {
                manage.AddUserToGroup(ADHelper.GetDirectoryEntry(), "CN=Employee01,OU=CompanyA,DC=contoso,DC=com", "NewGroup01");
                Console.WriteLine("Add user to group Finish...");
                Console.ReadLine();
            }
            catch (System.DirectoryServices.DirectoryServicesCOMException ex)
            {
                Console.WriteLine("Add user to group Error...");
                Console.WriteLine(ex);
                Console.ReadLine();
            }
        }
    }
複製程式碼 測試程式碼

測試結果:

5、使用者資訊更新

使用者資訊更新也比較簡單,直接上示例程式碼+測試程式碼,如果有疑問,隨時聯絡:

複製程式碼
 public void ModifyUser(DirectoryEntry de,string UserName,string company)
        {
            DirectorySearcher deSearch = new DirectorySearcher();
            deSearch.SearchRoot = de;
            deSearch.Filter = "(&(objectClass=user) (cn=" + UserName + "))";
            SearchResult result = deSearch.FindOne();
            if (result != null)
            {
                DirectoryEntry dey = ADHelper.GetDirectoryEntry(result.Path);
                SetProperty(dey, "company", company);
                dey.CommitChanges();
                dey.Close();
            }
            de.Close();
        }
複製程式碼 ModifyUser 複製程式碼
 static void Main(string[] args)
        {
            ADManage manage=new ADManage();
            Console.WriteLine("Modify user info Start...");
            try
            {
                manage.ModifyUser(ADHelper.GetDirectoryEntry(), "Employee01", "CompanyA");
                Console.WriteLine("Modify user info Finish...");
                Console.ReadLine();
            }
            catch (System.DirectoryServices.DirectoryServicesCOMException ex)
            {
                Console.WriteLine("Modify user info Error...");
                Console.WriteLine(ex);
                Console.ReadLine();
            }
        }
複製程式碼 測試程式碼

6、Enable/Disable使用者賬號

 Enable/Disable使用者賬號用到了新的屬性userAccountControl,可以對賬號密碼的是否過期,賬號是否可用等進行設定。

以下是設定userAccountControl時會用到值

複製程式碼
        SCRIPT 0x0001
        ACCOUNTDISABLE 0x0002
        HOMEDIR_REQUIRED 0x0008
        LOCKOUT 0x0010
        PASSWD_NOTREQD 0x0020
        PASSWD_CANT_CHANGE 0x0040
        ENCRYPTED_TEXT_PWD_ALLOWED 0x0080
        TEMP_DUPLICATE_ACCOUNT 0x0100
        NORMAL_ACCOUNT 0x0200
        INTERDOMAIN_TRUST_ACCOUNT 0x0800
        WORKSTATION_TRUST_ACCOUNT 0x1000
        SERVER_TRUST_ACCOUNT 0x2000
        DONT_EXPIRE_PASSWORD 0x10000
        MNS_LOGON_ACCOUNT 0x20000
        SMARTCARD_REQUIRED 0x40000
        TRUSTED_FOR_DELEGATION 0x80000
        NOT_DELEGATED 0x100000
        USE_DES_KEY_ONLY 0x200000
        DONT_REQ_PREAUTH 0x400000
        PASSWORD_EXPIRED 0x800000
        TRUSTED_TO_AUTH_FOR_DELEGATION 0x1000000
複製程式碼 UserAccountControl

具體程式碼如下:

複製程式碼
 /// <summary>
        /// 啟用賬號
        /// </summary>
        /// <param name="de"></param>
        public void EnableAccount(DirectoryEntry de)
        {
            //設定賬號密碼不過期
            int exp = (int)de.Properties["userAccountControl"].Value;
            de.Properties["userAccountControl"].Value = exp | 0x10000;
            de.CommitChanges();
            //啟用賬號
            int val = (int)de.Properties["userAccountControl"].Value;
            de.Properties["userAccountControl"].Value = val & ~0x0002;
            de.CommitChanges();
        }
複製程式碼 EnableAccount 複製程式碼
/// <summary>
        /// 停用賬號
        /// </summary>
        /// <param name="de"></param>
        public void DisableAccount(DirectoryEntry de)
        {
            //啟用賬號
            int val = (int)de.Properties["userAccountControl"].Value;
            de.Properties["userAccountControl"].Value = val | 0x0002;
            de.CommitChanges();
        }
複製程式碼 DisableAccount

AD一節的內容已經寫完了,對Exchange Server的操作請看下一篇。