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代码解释:
- 新建一个DirectoryEntry类,也就是活动目录的入口类
- 指定要连接到的Path,在稍后的新建OU实例中会详细解释Path的组成
- 用来连接AD的管理员账号,此管理员账号必须有操作AD的权限
- 管理员的密码,同样是为了连接AD
- 重载的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已存在"); } }
新建OU代码解释:
- ObjectExists方法判断新增的OU是否已经存在,代码下面会附上
- 利用ADHelper生成目录入口,本例是在一个测试的CompanyA OU中
- Children属性获取所有子项,并使用Add方法添加OU
- 提交更改,发回服务器
/// <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("用户组已存在"); } }
/// <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; } }
操作方法写好后,我们就来测试一下
在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(); }
新建用户代码解释:
- 利用ObjectExists判断用户是否存在,如果存在则提示用户已存在
- 新建入口类实例,Add方法新增用户
- SetProperty设置新用户的属性(显示名、Pre-Windows 2000登录名、登录名),并提交更改
- 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; }
代码解释:
- 新建DirectorySearcher实例,为Filter赋值,根据传入的参数de目录查找该安全组(注意:此组需要包含在DirectoryEntry中)
- 根据参数userDn判断用户是否存在(userDn是用户的标识名如:“CN=Employee01,OU=CompanyA,DC=rzh,DC=com”)
- dirEntry.Properties["member"].Contains(userDn)判断组中是否存在该用户
- 如果该组不存在该用户,则添加用户到组。如果该组中存在该用户,则将该用户从组中移除
测试一下,这里只测试添加,移除操作请自行测试:
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(); }
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
具体代码如下:
/// <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(); }
/// <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(); }
AD一节的内容已经写完了,对Exchange Server的操作请看下一篇。
如果有什么疑问,请及时与我讨论