Powershell在Active Directory中查询用户

Powershell在Active Directory中查询用户,powershell,csv,active-directory,Powershell,Csv,Active Directory,我很难找到一种更有效的方式从广告中查询信息。目前,我从我们的学生信息系统导入了一个活动用户的.csv文件。然后,我想创建一个新的.csv文件,其中包含来自广告的活动用户信息。因此,我正在查询每个用户的广告(大约10000名学生)。我有一种感觉,我可以通过一次查询以某种方式完成此任务,但运气不佳。学生匹配存储在广告标题字段中的数字ID。代码确实可以运行,但是运行需要几个小时。以下是我使用的: $Users = Import-Csv "c:\DASLExport.csv" -Header @("a"

我很难找到一种更有效的方式从广告中查询信息。目前,我从我们的学生信息系统导入了一个活动用户的
.csv
文件。然后,我想创建一个新的
.csv
文件,其中包含来自广告的活动用户信息。因此,我正在查询每个用户的广告(大约10000名学生)。我有一种感觉,我可以通过一次查询以某种方式完成此任务,但运气不佳。学生匹配存储在广告标题字段中的数字ID。代码确实可以运行,但是运行需要几个小时。以下是我使用的:

$Users = Import-Csv "c:\DASLExport.csv" -Header @("a") | Select a
$usersarray = @()
ForEach ($Row in $Users) {
    $userSearchString = $Row.a
    $currentUser = (Get-ADUser -Filter {Title -eq $userSearchString} -Properties title, SamAccountName, extensionAttribute1)
    $UserObj = New-Object PSObject
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName) 
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
    $usersarray += $UserObj
}

If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}

我认为,与其用
getaduser
查询每个用户,不如一次获取所有具有title的用户并将其保存到一个变量中,然后查询这个变量

此外,常规数组的大小是固定的,这意味着每次插入新元素时,实际上都会创建新数组并将所有数据复制到其中,然后一次又一次地重复,这需要很多时间。因此,切换到打算增长的ArrayList,速度会快得多

自己检查一下:

$ArrayList = New-Object System.Collections.ArrayList
$RegularArray = @()

Measure-Command { 1..10000 | % {[void]$ArrayList.Add($_)} }
Measure-Command { 1..10000 | % {$RegularArray += $_ } }
例如,试试这个:

$Users = Import-Csv "c:\DASLExport.csv" -Header @("a") | Select a
$ADUsers = Get-ADUser -Filter {Title -ne "$null"} -Properties title, SamAccountName, extensionAttribute1
$Usersarray = New-Object System.Collections.ArrayList

ForEach ($Row in $Users) {
    $userSearchString = $Row.a
    $currentUser = $ADUsers | ? {$_.Title -eq $userSearchString}
    if (!$currentUser) {continue}
    $UserObj = New-Object PSObject
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName) 
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
    [void]$usersarray.Add($UserObj)
}

If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}

我认为,与其用
getaduser
查询每个用户,不如一次获取所有具有title的用户并将其保存到一个变量中,然后查询这个变量

此外,常规数组的大小是固定的,这意味着每次插入新元素时,实际上都会创建新数组并将所有数据复制到其中,然后一次又一次地重复,这需要很多时间。因此,切换到打算增长的ArrayList,速度会快得多

自己检查一下:

$ArrayList = New-Object System.Collections.ArrayList
$RegularArray = @()

Measure-Command { 1..10000 | % {[void]$ArrayList.Add($_)} }
Measure-Command { 1..10000 | % {$RegularArray += $_ } }
例如,试试这个:

$Users = Import-Csv "c:\DASLExport.csv" -Header @("a") | Select a
$ADUsers = Get-ADUser -Filter {Title -ne "$null"} -Properties title, SamAccountName, extensionAttribute1
$Usersarray = New-Object System.Collections.ArrayList

ForEach ($Row in $Users) {
    $userSearchString = $Row.a
    $currentUser = $ADUsers | ? {$_.Title -eq $userSearchString}
    if (!$currentUser) {continue}
    $UserObj = New-Object PSObject
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "ID" -Value $($currentUser.title)
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Username" -Value $($currentUser.SamAccountName) 
    Add-Member -InputObject $UserObj -MemberType NoteProperty -Name "Password" -Value $($currentUser.extensionAttribute1)
    [void]$usersarray.Add($UserObj)
}

If($usersarray.count -gt 0) {$usersarray | Export-Csv -Path 'c:\users.csv' -NoTypeInformation}

虽然@Avshalom的答案很有用,但可以改进:

[CmdletBinding()]
param
(
    [Parameter(Position = 0)]
    [ValidateScript({Test-Path -Path $PSItem -PathType Leaf})]
    [string]
    $Path = 'C:\DASLExport.csv',

    [Parameter(Position = 1)]
    [ValidateScript({Test-Path -Path $PSItem -PathType Leaf -IsValid})]
    [string]
    $Destination = 'C:\users.csv'
)

$csv = Import-Csv -Path $Path -Header a
$users = @(Get-ADUser -Filter 'Title -ne "$null"' -Properties Title, SamAccountName, extensionAttribute1)

$collection = foreach ($row in $csv)
{
    $title = $row.a
    $user = $users.Where{$PSItem.Title -eq $title}
    if (-not $user)
    {
        Write-Warning -Message "User $title not found."
        continue
    }

    [pscustomobject]@{
        ID       = $user.Title
        Username = $user.SamAccountName
        Password = $user.extensionAttribute1
    }
}

$collection | Export-Csv -Path $Destination -NoTypeInformation
您可以将
foreach
循环的输出直接分配给变量,从而避免管理列表对象的需要(尽管如果您选择列表,则应使用
System.Collections.Generic.list
,因为
ArrayList
已被弃用)。此外,您不需要使用
Select Object
语句,因为您的
csv
已经加载,在该场景中它只处理两次。最大的速度改进不是数千次查询AD,而是将其保存在单个对象中,而主要是不使用
[array]
/
@()


速度比较:

$L = 1..100000

~70毫秒

~110毫秒

Measure-Command {$col = @(); foreach ($i in $L) { $col += $i }}

~46秒

虽然@Avshalom的答案很有用,但可以改进:

[CmdletBinding()]
param
(
    [Parameter(Position = 0)]
    [ValidateScript({Test-Path -Path $PSItem -PathType Leaf})]
    [string]
    $Path = 'C:\DASLExport.csv',

    [Parameter(Position = 1)]
    [ValidateScript({Test-Path -Path $PSItem -PathType Leaf -IsValid})]
    [string]
    $Destination = 'C:\users.csv'
)

$csv = Import-Csv -Path $Path -Header a
$users = @(Get-ADUser -Filter 'Title -ne "$null"' -Properties Title, SamAccountName, extensionAttribute1)

$collection = foreach ($row in $csv)
{
    $title = $row.a
    $user = $users.Where{$PSItem.Title -eq $title}
    if (-not $user)
    {
        Write-Warning -Message "User $title not found."
        continue
    }

    [pscustomobject]@{
        ID       = $user.Title
        Username = $user.SamAccountName
        Password = $user.extensionAttribute1
    }
}

$collection | Export-Csv -Path $Destination -NoTypeInformation
您可以将
foreach
循环的输出直接分配给变量,从而避免管理列表对象的需要(尽管如果您选择列表,则应使用
System.Collections.Generic.list
,因为
ArrayList
已被弃用)。此外,您不需要使用
Select Object
语句,因为您的
csv
已经加载,在该场景中它只处理两次。最大的速度改进不是数千次查询AD,而是将其保存在单个对象中,而主要是不使用
[array]
/
@()


速度比较:

$L = 1..100000

~70毫秒

~110毫秒

Measure-Command {$col = @(); foreach ($i in $L) { $col += $i }}
约46秒