PowerShell学习笔记(12):UC时代的用户对象创建(中)
第二节 组织结构树的实现
那么在开始这一节内容之前,你需要深呼吸一下,准备面对那些很复杂的代码?不,不必这样,放轻松,没有你想象的那么复杂,因为Quest公司的相关cmdlets为我们节省了很多时间。那么先让我们来看看诸位可能比较感兴趣的组织架构树。代码如下:
| function GetRootOUs { $objTNRootOUs = New-Object Windows.Forms.TreeNode $objTNSubOU = New-Object Windows.Forms.TreeNode $objRootOUs = Get-QADObject -SearchRoot 'contoso.com/用户帐号' -LdapFilter '(objectClass=organizationalUnit)' -SearchScope OneLevel foreach ($objSubOU in $objRootOUs) { $objTNSubOU = $objTNRootOUs.Nodes.Add($objSubOU.Name) $objTNSubOU.Tag = $objSubOU.DN } $objTNRootOUs.Text = "用户帐号" $objTNRootOUs.Tag = "ou=用户帐号,dc=contoso,dc=com" $objTNRootOUs.Expand() [void]$objTreeView.Nodes.Add($objTNRootOUs) } |
有点失望?是不是比预想的要简单?呵呵,有两方面的原因,一是由于这只是获得第一层OU的代码。二是要再次感谢Quest公司,因为他们为我们提供了非常棒的cmdlet:Get-QADObject。下面让我们详细分析下代码。
首先我们新建了Windows.Forms.TreeNode类的两个新实例。之所以这样做是因为$objTreeView对象的Nodes属性实质上是包含了TreeNode对象的集合(详细信息,可以参阅MSDN的相关文档)。其中$objTNRootOUs的作用是保存第一层所有OU的信息,最后将它作为$objTreeView.Nodes属性的属性值。而$objTNSubOU则起到的是一个中介的作用,因为通过$objTNRootNode.Nodes.Add($objSubOU.Name)语句得到的对象类型也是TreeNode,我们需要将每个OU的distinguishedName(DN)保存到$objTNSubOU的Tag属性中,供Get-QADobject cmdlet的SearchRoot参数使用。这也解释了foreach循环语句的作用。接着我们再回到Get-QADObject cmdlet那一行。这就是我们得到第一层OU信息的核心语句。首先通过SearchRoot参数指定存放用户帐号OU的LDAP路径,然后通过LdapFilter参数将所有类型是组织单元的对象筛选出来,最后通过SearchScope参数限制Get-QADObject cmdlet只将第一层的OU列出,而不是将该OU下的所有子OU都列出(将参数值设定为Subtree)。然后我们手动设置一下$objTNRootNode的相关属性并展开该节点,最后将它添加到$objTreeView.Nodes属性。最终效果就是我们刚开始运行脚本的效果:
下面一个问题是当用户点击树状结构中的某个节点时我们希望能展开下一层的OU。这将帮助帐号管理员确定新建用户最终是隶属于哪个OU。这部分的代码如下:
| function GetSubOUs { $objTNChildOU = New-Object Windows.Forms.TreeNode $objChildOUs = Get-QADObject -SearchRoot $objTreeView.SelectedNode.Tag -LdapFilter '(objectClass=organizationalUnit)' -SearchScope OneLevel if (($objChildOUs -ne $null) -and ($objTreeView.SelectedNode.Nodes.Count -eq 0) ) { foreach ($objChildOU in $objChildOUs) { $objTNChildOU = $objTreeView.SelectedNode.Nodes.Add($objChildOU.Name) $objTNChildOU.Tag = $objChildOU.DN } } $objTxtUserOU.Text = $objTreeView.SelectedNode.Text $Global:objCurrentOUDN = $objTreeView.SelectedNode.Tag $objTreeView.SelectedNode.Expand() if ($objCurrentOUDN -ne "OU=用户帐号,dc=contoso,dc=com") { $objTxtUserCenter.Text = ($objCurrentOUDN.Split(",")[-4]).Replace("OU=","") } else { $objTxtUserCenter.Text = "" } } |
首先我们再次新建了Windows.Forms.TreeNode的一个新实例,用来存放相关信息。接着我们执行Get-QADObject来获得该OU下的子OU。注意SearchRoot参数,和获得第一层OU时指定一个固定值不同的是,这次我们将使用选中节点的Tag属性来获得当前OU的DN。这时我们会遇到一个需要进行判断的地方,即根据该子OU下是否还有子OU需要进行处理。如果有的话,那么我们通过foreach循环将相关信息添加选中节点的Nodes属性。没有的话则不执行相关语句。同时我们也要对选中节点的Nodes属性进行判断,否者我们重复点击某一个节点时将会使得该节点所代表OU下的子OU被反复添加,这当然不是我们希望看到的,基于以上两点就构成了我们的判断依据:
| ($objcolSubOUs -ne $null) -and ($objTreeView.SelectedNode.Nodes.Count -eq 0) |
走完这个判断流程后,接着我们就来确定用户选中的是具体哪个OU,及该OU所在的中心(之所以需要确定中心是因为我们根据中心建立了用户组,建立用户时自然希望用户被添加到相关组,相关内容在第三节中还会详细说明)。所在的具体OU很好判断,就是用户选中的具体节点,因此只要让代表具体OU的文本框的Text属性值等于选中节点的Text属性值就可以了。而确定所在中心这是需要思考一下的问题,如何从一长串的DN中筛选出具体中的名称呢?下面我用三个DN来说明问题:
| OU=人力资源中心,OU=用户帐号,DC=Contoso,DC=Com OU=人事组,OU=人力资源中心,OU=用户帐号,DC=Contoso,DC=Com OU=网络与系统组,OU=信息管理中心,OU=用户帐号,DC=Contoso,DC=Com |
因为我将这三个DN进行了右对齐处理,所以大家很容易判断,代表中心的OU名称总是从右向左数的第四个字符串,且无论具体OU有多深,这个事实都不会改变。因此就引出了下面这行看似有点疯狂的代码:
| $objTxtUserCenter.Text = ($objCurrentOUDN.Split(",")[-4]).Replace("OU=","") |
这里首先需要补充说明的是变量$objCurrentOUDN。因为需要在不同函数(开启帐号时需使用)之间调用OU的DN信息,因此我们需要声明一个全局变量(《Windows PowerShell in Action》一书的第7.2.5及7.2.6两节有具体解释和示例)。这里我们只要记住$objCurrentOUDN中存放的是DN信息,并且该对象是字符串即可。正因为是字符串,因此我们可以使用Split方法以“,”作为分隔符来分割字符串。由于该方法的得到的结果是一个数组,因此我们可以使用[-4]来得到这个数组中的倒数第四项(有点疑惑?那么请阅读Windows PowerShell每周提示(3):访问数组中的值)。至此,我们得到的是类似“OU=信息管理中心”这样的字符串,我们还需要要做的是将“OU=”去除,因为对最终使用者而言,这个信息是多余的。去除的方法也很简单,因为得到的结果还是字符串,因此我们可以使用Replace方法将“OU=”替换成空字符串即可。
当然通过阅读代码我们会发现这句疯狂的代码实际上是包含在if语句中的,这是为了防止用户选择“用户帐号”这个节点后脚本报错,因为这个节点的DN通过Split分割后是没有第四项的。
至此组织架构树已基本实现,使用者可以轻松的在树形结构中选定新用户所在的OU。
注:文中代码仅供交流学习使用,尚有很大改善余地。