开发者问题收集

基于OU的Powershell安装脚本包装器

2019-11-15
363

非常感谢您提前阅读此文(这将是一个很长的问题)。

我是一个对 Powershell 了解不多的新手。我遇到一种情况,需要创建一个安装包装器脚本。

此脚本将在许多用户计算机上执行,这些计算机可能有/可能没有 Active Directory 访问权限。

但是,脚本应该采用以下方式:如果 Machine 对象是 Active Directory 中特定 OU 的一部分,则它应该相应地选择配置文件。

OU 信息是否会存储在 WMI 中的某个位置?什么是 ADSI 搜索器,它是否需要任何特殊访问权限或先决条件才能正常工作?

我已经在 Google 上搜索并使用 ADSI 获得了以下脚本,它似乎在我的虚拟机上运行,​​但在其他一些虚拟机上却不行(原因我不知道)。

$ComputerName = $env:ComputerName

$ADSISearcher = New-Object System.DirectoryServices.DirectorySearcher

$ADSISearcher.Filter = '(&(name=' + $ComputerName + ')(objectClass=computer))'

$ADSISearcher.SearchScope = 'Subtree'

$Computer = $ADSISearcher.FindAll()

$OU = $($Computer.Properties.Item('distinguishedName'))

我尝试在其他各种用户计算机(混合 AD 访问级别)上运行该脚本, $OU = $($Computer.Properties.Item('distinguishedName')) 出现错误。

You cannot call a method on a null-valued expression. At line:1 char:42 + $OU = $($Computer.Properties.Item < <<< ('distinguishedName')) + CategoryInfo : InvalidOperation: (Item:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull

$computer 不是空值。结果如下(我修改了 LDAP 信息。)

 Path                                                        Properties
----                                                        ----------
LDAP://CN=,OU=< ........>,OU=V< ...>,OU=N... {primarygroupid, iscriticalsystemobject, msds-supportede...

现在的问题是:我走对路了吗?ADSI 搜索器会从本地缓存的信息中查询此信息吗?

如果是,您能帮我解决这个问题吗?如果我完全错了,请纠正我。我已经为此苦苦思索了好几天,非常感谢您的帮助。

3个回答

您正在测试的机器是否有可能未加入域?

无论如何,虽然我确实建议您选择与您正在走的路径不同的解决方案,但此代码是否对您更有效?另请注意,您提到 DC 可能可访问,也可能不可访问。如果是这种情况,此查询将从本地计算机失败(并且从 sysvol 检索组策略也会失败)。

$LDAPDN = ([adsisearcher]"(&(name=$env:computername)(objectClass=computer))").findall().path

If ($LDAPDN) {
    $OU = $LDAPDN.Replace("LDAP://CN=$env:computername,","")
}

Else {
    Write-Warning "Unable to find $env:computername in AD or the adsi search failed"
}
MMorrison
2019-11-15

如果您使用与计算机不在同一域中的用户帐户运行它,我可以重现此问题。它会尝试搜索用户的域,但找不到计算机。因此,您需要明确告诉它连接到计算机的域,您可以使用以下命令找到它:

$ComputerDomain = (Get-WmiObject Win32_ComputerSystem).Domain

您还可以使用一些简短形式,例如 [adsi][adsisearcher] ,以使代码更简洁。

这里使用的 DirectorySearcher (又名 [adsisearcher] )是 .NET 的 DirectorySearcher ,它是 .NET Framework 的一部分,默认情况下会安装在 Windows 中。所以你不必担心它不可用。但计算机确实需要连接到网络才能正常工作。

$ComputerName = $env:ComputerName
$ComputerDomain = (Get-WmiObject Win32_ComputerSystem).Domain

$ADSISearcher = [adsisearcher]"(&(name=$ComputerName)(objectClass=computer))"
$ADSISearcher.PropertiesToLoad.Add("distinguishedName")
$ADSISearcher.SearchRoot = [adsi]"LDAP://$ComputerDomain"

$Computer = $ADSISearcher.FindOne()

$DN = $Computer.Properties["distinguishedName"][0]
$OU = $DN.Substring($DN.IndexOf("OU="))

请注意, SearchRoot 设置为计算机的域。

SearchScope 默认为 Subtree ,因此您无需设置它。

最好使用 PropertiesToLoad 来告诉它您希望它返回哪些属性。否则,它将返回每个带有值的属性。在这里可能不会有太大区别,但在其他情况下可能会有区别。

我还使用了 FindOne() ,它返回单个值,而不是 FindAll() ,它返回一个数组。实际上,无论如何都应该只有一个结果,这就是为什么您可以将数组视为不存在的原因。

请注意, distinguishedName 属性是对象的完整路径,而不仅仅是 OU。因此,如果您想要 OU,则需要提取 distinguishedName 的那部分,这就是我在这里所做的。(所有属性都以数组的形式呈现,这就是为什么您需要 [0]

Gabriel Luci
2019-11-15

非常感谢您今天的帮助!我确实学到了很多信息。我确实转了个弯,正在寻找另一条路线(与 ADSI 相比,这绝对是一个非常慢的查询)。 但正在处理跨域场景。 参考: 如何使用 WMI 获取计算机的当前 OU 并列出该 OU 中的所有其他计算机?

$ComputerName = $env:ComputerName;
$Computer = Get-WmiObject -Namespace 'root\directory\ldap' -Query "Select DS_distinguishedName from DS_computer where DS_cn = '$ComputerName'";
$OU = $Computer.DS_distinguishedName.Substring($Computer.DS_distinguishedName.IndexOf('OU='));

它确实很神奇,我从所有测试 VM 中得到了所需的结果

karockiad
2019-11-15