基于OU的Powershell安装脚本包装器
非常感谢您提前阅读此文(这将是一个很长的问题)。
我是一个对 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 搜索器会从本地缓存的信息中查询此信息吗?
如果是,您能帮我解决这个问题吗?如果我完全错了,请纠正我。我已经为此苦苦思索了好几天,非常感谢您的帮助。
您正在测试的机器是否有可能未加入域?
无论如何,虽然我确实建议您选择与您正在走的路径不同的解决方案,但此代码是否对您更有效?另请注意,您提到 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"
}
如果您使用与计算机不在同一域中的用户帐户运行它,我可以重现此问题。它会尝试搜索用户的域,但找不到计算机。因此,您需要明确告诉它连接到计算机的域,您可以使用以下命令找到它:
$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]
)
非常感谢您今天的帮助!我确实学到了很多信息。我确实转了个弯,正在寻找另一条路线(与 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 中得到了所需的结果