在目录搜索器的 ldap 过滤器中传递属性列表

pass list of properties in ldap filter for directorysearcher

所以目前,我知道您可以使用 ldap 过滤器获取一个帐户,例如 $adSearchFilter = "(&(objectCategory=User)(samAccountType:1.2.840.113556.1.4.803:=805306368)(SamAccountName='Account1'))。有没有办法让 ldap 过滤器允许您通过名称列表,而不是使用 = 我可以使用 -contains?

下面是代码,如您所见,它在 foreach 循环中一次搜索一个用户,以完成整个搜索过程...

Function GetUsersInfoFromDomain
{
    Param ([String]$searchPropertyName, [String[]]$searchPropertyValues, [String[]]$DcWithCred,[String]$domainShortName, [String[]]$userProperties)

    $queryTable = @()
    ForEach ($searchPropertyValue in $searchPropertyValues)
    {
        $adSearchFilter = "(&(objectCategory=User)(samAccountType:1.2.840.113556.1.4.803:=805306368)($searchPropertyName=$searchPropertyValue))"
        Write-Host "Searching domain $domainShortName with $searchPropertyName $searchPropertyValue"
        $searchDomainResultsTable = powershell -command {
            Param ([String]$adSearchFilter, [String[]]$userProperties,[String[]]$DcWithCred, [String]$domainShortName)
            [string]$DC = $DcWithCred[0]
            [string]$Username = $DcWithCred[1]
            [string]$Password = $DcWithCred[2]
            [string]$domain = "LDAP://$DC"

            $adDomain = New-Object System.DirectoryServices.DirectoryEntry($domain, $Username, $Password)
            $adSearcher = New-Object System.DirectoryServices.DirectorySearcher($adDomain)
            $adSearcher.Filter = $adSearchFilter
            $adSearcher.PageSize=1000
            $adSearcher.PropertiesToLoad.AddRange($userProperties) | out-Null
            $userRecords = $adSearcher.FindAll()
            $adSearcher.Dispose() | Out-Null
            [System.GC]::Collect() | Out-Null

            # The AD results are converted to an array of hashtables.
            $userPropertiesTable = @()
            foreach($record in $userRecords) {
                $hashUserProperty = @{}
                foreach($userProperty in $userProperties){
                    if (($userProperty -eq 'objectGUID') -or ($userProperty -eq 'objectSid') -or ($userProperty -eq 'msExchMasterAccountSid')) {
                        if ($record.Properties[$userProperty]) {
                            $hashUserProperty.$userProperty = $record.Properties[$userProperty][0]
                        } else {
                        $hashUserProperty.$userProperty = $null
                        }
                    } Else {
                        if ($record.Properties[$userProperty]) {
                            $hashUserProperty.$userProperty = ($record.Properties[$userProperty] -join '; ').trim('; ')
                        } else {
                        $hashUserProperty.$userProperty = $null
                        }
                    } #end Else
                } #end ForEach
                $userPropertiesTable += New-Object PSObject -Property $hashUserProperty
            } #end ForEach
            [System.GC]::Collect() | Out-Null

            # Fixes the property values to be a readable format before exporting to csv file
            $listOfBadDateValues = '9223372036854775807', '9223372036854770000', '0'
            $maxDateValue = '12/31/1600 5:00 PM'
            $valuesToFix = @('lastLogonTimestamp', 'AccountExpires', 'LastLogon', 'pwdLastSet', 'objectGUID', 'objectSid', 'msExchMasterAccountSid')
            $extraPropertyValues = @('Domain Name')
            $valuesToFixCounter = 0
            $extraPropertyValuesCounter = 0
            $valuesToFixFound = @($false, $false, $false, $false, $false, $false, $false)
            $extraPropertyValuesFound = @($false)

            ForEach ($valueToFix in $valuesToFix)
            {
                if ($userProperties -contains $valueToFix)
                {
                    $valuesToFixFound[$valuesToFixCounter] = $true
                }
                $valuesToFixCounter++
            }

            ForEach ($extraPropertyValue in $extraPropertyValues)
            {
                if ($userProperties -contains $extraPropertyValue)
                {
                    $extraPropertyValuesFound[$extraPropertyValuesCounter] = $true
                }
                $extraPropertyValuesCounter++
            }

            $tableFixedValues = $userPropertiesTable | % { 
                if ($valuesToFixFound[0]) {
                    if ($_.lastLogonTimestamp) {
                        $_.lastLogonTimestamp = ([datetime]::FromFileTime($_.lastLogonTimestamp)).ToString('g')
                    }
                }; if ($valuesToFixFound[1]) {
                    if (($_.AccountExpires) -and ($listOfBadDateValues -contains $_.AccountExpires)) {
                        $_.AccountExpires = ""
                    } else {
                        if (([datetime]::FromFileTime($_.AccountExpires)).ToString('g') -eq $maxDateValue) {
                            $_.AccountExpires = ""
                        } Else {
                            $_.AccountExpires = ([datetime]::FromFileTime($_.AccountExpires)).ToString('g')
                        }
                    }
                }; if ($valuesToFixFound[2]) {
                    if (($_.LastLogon) -and ($listOfBadDateValues -contains $_.LastLogon)) {
                        $_.LastLogon = ""
                    } else {
                        if (([datetime]::FromFileTime($_.LastLogon)).ToString('g') -eq $maxDateValue) {
                            $_.LastLogon = ""
                        } Else {
                            $_.LastLogon = ([datetime]::FromFileTime($_.LastLogon)).ToString('g')
                        }
                    }
                }; if ($valuesToFixFound[3]) {
                    if (($_.pwdLastSet) -and ($listOfBadDateValues -contains $_.pwdLastSet)) {
                        $_.pwdLastSet = ""
                    } else {
                        if (([datetime]::FromFileTime($_.pwdLastSet)).ToString('g') -eq $maxDateValue) {
                            $_.pwdLastSet = ""
                        } Else {
                            $_.pwdLastSet = ([datetime]::FromFileTime($_.pwdLastSet)).ToString('g')
                        }
                    }
                }; if ($valuesToFixFound[4]) {
                    if ($_.objectGUID) {
                        $_.objectGUID = ([guid]$_.objectGUID).Guid
                    } Else {
                        $_.objectGUID = ""
                    }
                }; if ($valuesToFixFound[5]) {
                    if ($_.objectSid) {
                        $_.objectSid  = (New-Object Security.Principal.SecurityIdentifier($_.objectSid, 0)).Value
                    } Else {
                        $_.objectSid = ""
                    }
                }; if ($valuesToFixFound[6]) {
                    if ($_.msExchMasterAccountSid) {
                        $_.msExchMasterAccountSid  = (New-Object Security.Principal.SecurityIdentifier($_.msExchMasterAccountSid, 0)).Value
                    } Else {
                        $_.msExchMasterAccountSid = ""
                    }
                }; If ($extraPropertyValuesFound[0]) {
                    If (!($_.'Domain Name')) {
                        $_.'Domain Name' = $domainShortName
                    }
                };$_}
                [System.GC]::Collect() | Out-Null

                $sortedTableColumns = $tableFixedValues | Select-Object $userProperties
                [System.GC]::Collect() | Out-Null

                return $sortedTableColumns
        } -args $adSearchFilter, $userProperties, $DcWithCred, $domainShortName
        [System.GC]::Collect() | Out-Null
        Write-Host "Search Complete."
        Write-Host ""

        if ($searchDomainResultsTable)
        {
            $queryTable += $searchDomainResultsTable
        }
    } # End ForEach Loop
    Write-Host 'Exporting domain search results to table...'
    Write-Output $queryTable
}

我想过做类似 $adSearchFilter += "($searchPropertyName=$searchPropertyValue)" 的事情。但是,由于 10mb 的限制 - What is the LDAP filter string length limit in Active Directory?,我不确定这是否是查找 200,000++ 用户时的最佳方法。

有谁知道每次搜索传递列表而不是 1 个字符串值的方法吗?

LDAP 没有类似 -contains 的语句,但您可以使用 OR 运算符 (|) 构造匹配多个精确值的筛选表达式:

(|(samaccountname=user1)(samaccountname=user2)(samaccountname=user3))

这就是我构建过滤器字符串的方式:

$FilterTemplate = '(&(objectCategory=User)(samAccountType:1.2.840.113556.1.4.803:=805306368){0})'
$ClauseTemplate = "($searchPropertyName={0})"
$AllClauses     = $searchPropertyValues |ForEach-Object { $ClauseTemplate -f $_ }
$adSearchFilter = $FilterTemplate -f $($AllClauses -join '')

话虽如此,为什么要在一次搜索中传递 200000 个特定值进行搜索? LDAP 支持通配符匹配(例如 (samaccountname=*))。

在任何情况下,您都可以通过对 $AllClauses 中最大的字符串调用 Encoding.GetByteCount 来计算字符串的最终大小,然后使用它来划分数组(让我们将其限制在9.5 MB 为了安全起见):

$LongestString = $AllClauses |Sort -Property Length |Select -Last 1
$LongestByteCount = [System.Text.Encoding]::Unicode.GetByteCount($LongestString)
if(($LongestByteCount * $AllClauses.Count) -gt 9.5MB)
{
    $MaxCount = [int](9.5MB / $LongestByteCount)
    for($i = 0; $i -lt $AllClauses.Count; $i += $MaxCount)
    {
        $ClauseSubset = $AllClauses[$i..$($i + $MaxCount - 1)]
        $adSearchFilter = $FilterTemplate -f $($ClauseSubset -join '')
        # Do your search
    }
}