Active Directory组织单位(Organizational Unit)操作汇总
前言
本章聊Active Directory的组织单位(OU)的新增、修改、移动等操作,使用.NET Framework 为我们提供的System.DirectoryServices程序集。
不积跬步无以至千里。Active Directory开发系列将在本章开始侧重于代码(有部分人肯定很高兴了,可以COPY嘛),因为个人认为必要的理论知识和基础代码在前面已经记录了很多,这章开始若有不理解的地方请回看以前的文章。
封装基础ADHelper
这段ADHelper 是为了后面组织单位的新增,修改,移动而准备的代码段,在实际项目开发中当然是不够看的,不过讲本章涉及内容已经足够了。
1 public class ADHelper 2 { 3 #region 构造单例 4 private ADHelper() { } 5 public static readonly ADHelper Instance = new ADHelper(); 6 #endregion 7 #region 公共属性 8 private static string DomainServiceIP = "192.168.241.3"; 9 private static string UserName = @"Domain"; 10 private static string Password = "p@ssw0rd"; 11 private DirectoryEntry rootEntry = null; 12 #endregion 13 #region 公共方法 14 /// <summary> 15 /// 域节点DirectoryEntry对象 16 /// </summary> 17 public DirectoryEntry DomainRootEntry 18 { 19 get 20 { 21 if (rootEntry == null) 22 { 23 try 24 { 25 rootEntry = new DirectoryEntry("LDAP://" + DomainServiceIP, UserName, Password); 26 } 27 catch (Exception ex) 28 { 29 30 throw ex; 31 } 32 } 33 return rootEntry; 34 } 35 } 36 /// <summary> 37 /// AD查询器公共方法封装 38 /// </summary> 39 /// <typeparam name="T">DirectorySearcher或SearchResultCollection</typeparam> 40 /// <param name="SearchRoot">查询器的查询起点</param> 41 /// <param name="Filter">查询过滤器</param> 42 /// <param name="SearchScope">查询方式</param> 43 /// <returns></returns> 44 public T GetSearchResultOrSearchCollection<T>(DirectoryEntry SearchRoot, string Filter, SearchScope SearchScope) where T : class 45 { 46 DirectorySearcher Searcher = new DirectorySearcher(); 47 if (SearchRoot == null) 48 SearchRoot = DomainRootEntry; 49 using (SearchRoot) 50 { 51 try 52 { 53 Searcher.Filter = Filter; 54 Searcher.SearchRoot = SearchRoot; 55 Searcher.SearchScope = SearchScope; 56 if (typeof(T).Name == "SearchResult") 57 return Searcher.FindOne() as T; 58 else 59 return Searcher.FindAll() as T; 60 } 61 catch (Exception ex) 62 { 63 64 throw ex; 65 } 66 } 67 } 68 /// <summary> 69 /// 获取DirectoryEntry对象 70 /// </summary> 71 /// <param name="proterEntry">查询起点</param> 72 /// <param name="filter">查询条件</param> 73 /// <returns></returns> 74 public DirectoryEntry GetEntry(DirectoryEntry proterEntry, string filter) 75 { 76 SearchResult SearchResult = GetSearchResultOrSearchCollection<SearchResult>(proterEntry, filter, SearchScope.Subtree); 77 if (SearchResult != null) 78 return SearchResult.GetDirectoryEntry(); 79 else 80 return DomainRootEntry; 81 } 82 #endregion 83 }
这段ADHelper代码顾名思义其实和SQLHelper一样,是对AD操作的最基础层的一段代码,如果前面的文章有仔细了解过,那么这段代码我相信应该不成问题,只不过是做了封装而已,我们将主要精力放在后面实际的操作OU上吧。
我们还要再封装一个组织单位的实体类方便我们使用。
1 public class ADOrgUnit 2 { 3 #region 属性 4 /// <summary> 5 /// GUID 6 /// </summary> 7 public string ObjectGuid { get; set; } 8 /// <summary> 9 /// 标示名 10 /// </summary> 11 public string DistinguishedName { get; set; } 12 /// <summary> 13 /// OU 14 /// </summary> 15 public string Ou { get; set; } 16 /// <summary> 17 /// 描述 18 /// </summary> 19 public string Description { get; set; } 20 #endregion 21 }
我的测试域控服务器如下:
如果之前的文章有做过了解,看到图片上的数据排列,我们完全可以推算出某AD对象的DN。
新增组织单位(OU)
我们目标是在域节点创建新的OU,如果之前的文章有理解,那么我们可以直接判断出DomainDNS的DN为:DC=IFire47,DC=com
1 static void Main(string[] args) 2 { 3 ADOrgUnit adOrgUnit = new ADOrgUnit(); 4 //新增OU的名称 5 adOrgUnit.Ou = "IFire47"; 6 //新增OU的描述 7 adOrgUnit.Description = "IFire47测试组织单位"; 8 try 9 { 10 //我们在域节点创建新的组织单位,所以根据封装的ADHelper调用此方法。 11 using (DirectoryEntry parentEntry = ADHelper.Instance.DomainRootEntry) 12 { 13 //创建新OU,并接受新OU的DiectoryEntry对象 14 DirectoryEntry ouEntry = parentEntry.Children.Add("ou=" + adOrgUnit.Ou, "organizationalUnit"); 15 //为创建的新OU赋值属性 16 if (!String.IsNullOrEmpty(adOrgUnit.Description)) 17 ouEntry.Properties["description"].Value = adOrgUnit.Description; 18 //保存 19 ouEntry.CommitChanges(); 20 } 21 } 22 catch (Exception ex) 23 { 24 throw ex; 25 } 26 }
因为我们是在域节点创建的新OU我们看下新OU的DN属性
这跟之前文章讲解不谋而合,所以在这里我们将从实际代码测试中对LDAP和DN的概念有更进一步的认识与理解。对于这块代码的理解,这是我根据.NET的提示及个人理解做的整理:
1 // 摘要:获取 Active Directory 域服务层次结构中此节点的子项。 2 // 返回结果:一个 System.DirectoryServices.DirectoryEntries 对象,其中包含 Active Directory 域服务层次结构中此节点的子项。 3 [Browsable(false)] 4 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 5 [DSDescription("DSChildren")] 6 public DirectoryEntries Children { get; } 7 // 摘要: 在容器中创建一个新项。 8 // name:新项名称。 9 // schemaClassName:用于此新项的架构名称。即新创建的对象类别,详细查看上章 10 // 返回结果:新项的 System.DirectoryServices.DirectoryEntry 对象。 11 public DirectoryEntry Add(string name, string schemaClassName); 12 // 摘要:获取此 System.DirectoryServices.DirectoryEntry 对象的 Active Directory 域服务属性。 13 // 返回结果:一个 System.DirectoryServices.PropertyCollection 对象,包含此项所设属性。 14 [Browsable(false)] 15 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 16 [DSDescription("DSProperties")] 17 public PropertyCollection Properties { get; } 18 //摘要: 将目录项所作更改保存到基础目录存储中。 19 public void CommitChanges();
修改组织单位(OU)
我们修改刚才新添加的OU的名称及描述属性
1 static void Main(string[] args) 2 { 3 ADOrgUnit adOrgUnit = new ADOrgUnit(); 4 //修改OU的新名称 5 adOrgUnit.Ou = "IceFire47"; 6 //修改OU的新描述 7 adOrgUnit.Description = "IceFire47测试组织单位"; 8 //所要修改的OU的DN属性 9 adOrgUnit.DistinguishedName = "OU=IceFire47,DC=IFire47,DC=com"; 10 try 11 { 12 DirectoryEntry ouEntry = GetEntryByOrgDN(adOrgUnit.DistinguishedName); 13 if (!string.IsNullOrEmpty(adOrgUnit.Ou)) 14 { 15 ouEntry.Rename("ou=" + adOrgUnit.Ou); 16 ouEntry.CommitChanges(); 17 } 18 //修改描述属性,属性是数组类型的,要做一系列容错判断 19 if (ouEntry.Properties.Contains("description"))//判断是否存在这个属性 20 { 21 if (adOrgUnit.Description != null && !string.IsNullOrEmpty(adOrgUnit.Description))//描述是否为空 22 ouEntry.Properties["description"][0] = adOrgUnit.Description;//不空修改属性 23 else// 24 ouEntry.Properties["description"].RemoveAt(0);//为空移除描述属性 25 } 26 else 27 { 28 if (adOrgUnit.Description != null && !string.IsNullOrEmpty(adOrgUnit.Description))//不为空添加属性 29 ouEntry.Properties["description"].Add(adOrgUnit.Description); 30 } 31 //保存 32 ouEntry.CommitChanges(); 33 } 34 catch (Exception ex) 35 { 36 throw ex; 37 } 38 } 39 /// <summary> 40 /// 根据OU的DN属性值查询OU对象 41 /// </summary> 42 /// <param name="distinguishedName">DN属性值</param> 43 /// <returns></returns> 44 public static DirectoryEntry GetEntryByOrgDN(string distinguishedName) 45 { 46 string filter = "(&(objectClass=organizationalUnit)(distinguishedName=" + distinguishedName + "))"; 47 return ADHelper.Instance.GetEntry(null, filter); 48 }
这的逻辑根据自身的业务场景可以做调整,并不一定要根据DN去查询然后修改,也可以根据name等属性查询,只不过要修改Filter并修改传值。而且我们修改了这个OU对象后,它的DN也将自动修改,因为我们定义了新的OU名称。
这里的赋值做了一系列的判断,是因为AD对象的属性并不一定是String还有可能是String[],从属性编辑器中看下描述属性如下,所以直接赋值保存或做判断后赋值或修改时直接追加根据自身业务场景而定。
移动组织单位(OU)
我们将刚才修改的OU,移动到北京分公司的OU当中
1 static void Main(string[] args) 2 { 3 //需要移动的OU的DN 4 string OrgUnitDN = "OU=IceFire47,DC=IFire47,DC=com"; 5 //要移动到的容器的DN,这里不一定是OU也可以是域节点 6 string targetOrgDN = "OU=北京分公司,DC=IFire47,DC=com"; 7 using (DirectoryEntry ouEntry = GetEntryByOrgDN(OrgUnitDN)) 8 { 9 using (DirectoryEntry targetOU = GetEntryByOrgDN(targetOrgDN)) 10 { 11 try 12 { 13 ouEntry.MoveTo(targetOU); 14 ouEntry.CommitChanges(); 15 } 16 catch (Exception ex) 17 { 18 throw ex; 19 } 20 } 21 } 22 } 23 /// <summary> 24 /// 根据OU的DN查询OU对象 25 /// </summary> 26 /// <param name="distinguishedName">DN属性</param> 27 /// <returns></returns> 28 public static DirectoryEntry GetEntryByOrgDN(string distinguishedName) 29 { 30 string filter = "(&(objectClass=organizationalUnit)(distinguishedName=" + distinguishedName + "))"; 31 return ADHelper.Instance.GetEntry(null, filter); 32 }
在移动OU时,新OU的DN也将自动改变。同理,这里的一部分逻辑也并不是固定的,我们也可以根据自身业务场景做调整,关键还是在获取DirectoryEntry对象根据项目需求而定。而且OU的移动智能在域节点或者另一个OU中,不能将组织单位移动到某个组或者用户或者计算机中,而且需要注意的是,当OU移动时,移动的OU内的所有对象将一起被移动。
结语
本章主要是讲解AD中组织单位的一系列操作,删除操作调用DirectoryEntry.DeleteTree();DirectoryEntry.CommitChanges();即可,但是实际项目中删除操作要慎重,删除OU此容器内的所有对象将一并删除。
这里的所有示例都没有做容错处理,比如说新建OU时要判断当前节点下是否存在同名的OU存在若存在则不能创建等等,因为从之前的文章中已经讲解过DN在全局是唯一的,这就意味着在同一节点中不可能存在同名的OU,在不同OU中可以存在同名OU,因为就算同名但是他们的DN是不一样的。
在本章对属性赋值的判断,有些属性做判断而有些属性不需要可以直接赋值,这些都取决于Windowsd对具体AD对象的定义,我将会抽空在上一篇的常用属性的注释中做标注。而OU对象转换成.NET中的实体对象则在User具体操作章节一起讲解,因为都是操作DirectoryEntry对象,本质上都是一样。只不过可能加载的DirectoryEntry属性集会根据OU或User等有一些区别,但是整体的转换过程都是一样的。以上是本章全部内容,明天还要上班睡觉。
本章最后更新时间:2017年4月24日00:25:45
作者:IFire47 出处:http://www.cnblogs.com/IFire47/