C#操作AD及Exchange Server总结(一)

时间:2024-03-06 16:51:27

这篇博客的目的:根据亲身项目经历,总结对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的操作请看下一篇。

如果有什么疑问,请及时与我讨论