PowerShell 技能连载 - 获取 1000 个以上 Active Directory 结果

当您使用 ADSISearcher 时,默认情况下,Active Directory 只返回前 1000 个搜索结果。这是一个防止意外的 LDAP 查询导致域控制器负荷过重的安全保护机制。

如果您需要完整的搜索结果,并且明确地知道它将超过 1000 条记录,请设置 PageSize 为 1000。通过这种方式,ADSISearcher 每一批返回 1000 个搜索结果元素。

以下查询将会返回您域中的所有用户账户(在运行这个查询之前,您也许需要联系一下您的域管理员):

$searcher = [ADSISearcher]"sAMAccountType=$(0x30000000)"

# get all results, do not stop at 1000 results
$searcher.PageSize = 1000

$searcher.FindAll() |
  ForEach-Object { $_.GetDirectoryEntry() } |
  Select-Object -Property * |
  Out-GridView

PowerShell 技能连载 - 在智能感知中隐藏参数

从 PowerShell 4.0 开始,脚本作者可以决定隐藏某些参数,使之不在智能感知中出现。通过这种方式,可以在 ISE 的智能感知上下文菜单中隐藏不常用的参数。

function Test-Function
{
    param(
        $Name,
        [Parameter(DontShow)]
        [Switch]
        $IAmSecret
    )

    if ($IAmSecret)
    {
     "Doing secret things with $Name"
    }
    else
    {
      "Regular behavior with $Name"
    }
}

当您在 PowerShell 4.0 ISE 中运行这个函数时,只有 “Name” 参数会出现在智能感知上下文菜单中。然而,如果您事先知道这个隐藏参数,并且键入了第一个字母,然后按下 (TAB) 键,这个参数仍会显示:

在帮助窗口,隐藏参数总是可以显示,您可以通过类似这种方式打开:

PowerShell 技能连载 - 快速查找 Active Directory 用户账户

LDAP 查询条件越明确,查询速度就越快,占用的资源就越少,并且查询结果越清晰。

例如,许多人使用 objectClass 来限制查询结果为某个指定的对象类型。若只需要查询用户账户,他们常常使用 "objectClass=user" 的写法。许多人不知道计算机账户也共享这个对象类型。让我们来验证这一点:

这个例子将会查找所有 SamAccountName 以 “a” 开头,并且 objectClass=”user” 的账户。

# get all users with a SamAccountName that starts with "a"
$searcher = [ADSISearcher]"(&(objectClass=User)(sAMAccountName=a*))"

# see how long this takes
$result = Measure-Command {
  $all = $searcher.FindAll()
  $found = $all.Count
}

$seconds = $result.TotalSeconds

"The search returned $found objects and took $sec seconds."

然后使用这行来代替上面的代码:

$searcher = [ADSISearcher]"(&(sAMAccountType=$(0x30000000))(sAMAccountName=a*))"

当您换成这行代码以后,查询速度显著提升了。并且结果更清晰。这是因为普通用户账户和计算机账户的 SamAccountType 不同:

  • SAM_NORMAL_USER_ACCOUNT 0x30000000
  • SAM_MACHINE_ACCOUNT 0x30000001

两者的 objectClass 都属于 “User”。

PowerShell 技能连载 - 通过 SID 查找 Active Directory 账户

如果您已知账户的 SID 并且希望找到相应的 Active Directory 账户,那么 LDAP 查询并不适合这项工作。为了使它能工作,您需要将 SID 的格式改成符合 LDAP 规则的格式,这不是一个简单的过程。

以下是一个更简单的使用 LDAP 路径的办法。假设您使用 $SID 变量保存了一个 SID 字符串,并且您希望查找出和它关联的 Active Directory 账户。试试以下的代码:

$SID = '<enter SID here>'   # like S-1-5-21-1234567-...
$account = [ADSI]"LDAP://<SID=$SID>"
$account
$account.distinguishedName

PowerShell 技能连载 - 在不同的 Domain 中查找

当你那使用 ADSISearcher 类型加速器来查找 Active Directory 账户时,它缺省情况下在您当前登录的域中查找。如果您需要在一个不同的域中查找,请确保相应地定义了搜索的根路径。

This example will find all accounts with a SamAccountName that starts with “tobias”, and it searches the domain “powershell.local” (adjust to a real domain name, of course):
这个例子将查找所有 SamAccountName 以 “tobias” 开头的账户,并且它在 “powershell.local” 域中搜索(当然,请根据实际情况调整名字):

# get all users with a SamAccountName that starts with "tobias"
$searcher = [ADSISearcher]"(&(objectClass=User)(objectCategory=person)(sAMAccountName=tobias*))"

# use powershell.local for searching
$domain = New-Object System.DirectoryServices.DirectoryEntry('DC=powershell,DC=local')
$searcher.SearchRoot = $domain

# execute the query
$searcher.FindAll()

PowerShell 技能连载 - 从 DN 中获得 Domain

“DN” 指的是是 Active Directory 对象的路径,看起来大概如下:

'CN=Tobias,OU=Authors,DC=powershell,DC=local'

要获取 DN 中的域部分,请使用如下代码:

$DN = 'CN=Tobias,OU=Authors,DC=powershell,DC=local'
$pattern = '(?i)DC=\w{1,}?\b'

([RegEx]::Matches($DN, $pattern) | ForEach-Object { $_.Value }) -join ','

这段代码用一个正则表达式来查找 DN 的所有 DC= 部分然后将它们用逗号分隔符连接起来。

执行结果如下:

DC=powershell,DC=local

PowerShell 技能连载 - 将二进制 SID 转换为 SID 字符串

Active Directory 账户有一个二进制形式存储的 SID。要将字节数组转换为字符串的表达形式,可以用如下的 .NET 函数:

# get current user
$searcher = [ADSISearcher]"(&(objectClass=User)(objectCategory=person)(sAMAccountName=$env:username))"
$user = $searcher.FindOne().GetDirectoryEntry()

# get binary SID from AD account
$binarySID = $user.ObjectSid.Value

# convert to string SID
$stringSID = (New-Object System.Security.Principal.SecurityIdentifier($binarySID,0)).Value

$binarySID
$stringSID

在这个例子中,一个 ADSI 搜索器获取当前的用户账户(返回当前登录到一个域中的用户)。然后,将二进制的 SID 转换为 SID 字符串。

PowerShell 技能连载 - 查找当前的脚本文件夹

从 PowerShell 3.0 开始,有一个很简单的办法来确定一个脚本所在的文件夹:$PSScriptRoot。这个变量总是保存了指定脚本所存放的文件夹路径。

通过这种方法,可以很方便地加载额外的资源,比如说其它脚本。以下代码将读取位于同一个文件夹中,一个名为 myFunctions.ps1 的脚本文件:

"$PSScriptRoot\myFunctions.ps1"

别忘了用“dot-source”语法(在路径之前加点号)。否则只会输出该路径名(而不是执行该路径表示的脚本)。

PowerShell 技能连载 - 获取 Active Directory 账户信息

在上一段脚本中,您应该已经发现了可以多么轻易地用几行 PowerShell 代码来获取 Active Directory 账户。它的结果是一个搜索结果对象,而不是实际的账户对象。

要获取一个账户的更详细信息,请使用 GetDirectoryEntry() 将搜索结果转换为一个实际的账户对象:

# get 10 results max
$searcher.SizeLimit = 10

# find account location
$searcher.FindAll() |
  # get account object
  ForEach-Object { $_.GetDirectoryEntry() } |
  # display all properties
  Select-Object -Property * |
  # display in a grid view window (ISE needs to be installed for this step)
  Out-GridView

PowerShell 技能连载 - 查找 Active Directory 用户账号

有很多用于 Active Directory 的 Module 和 Cmdlet,但是有些时候用 .NET 代码来做反而更方便快捷。

比如说,如果您只是想知道,某个用户是否存在于您的 Active Directory中,那么实现查找一个用户是很容易的:

# sending LDAP query to Active Directory
$searcher = [ADSISearcher]'(&(objectClass=User)(objectCategory=person)(SamAccountName=tobias*))'
# finding first match
$searcher.FindOne()
# finding ALL matches
$searcher.FindAll()

这段代码将查找所有 SamAccountName 以 “tobias” 开头的用户账号。您可以接着用这个方法来便捷地找出这个用户所在的位置:

# find account location
$searcher.FindAll() | Select-Object -ExpandProperty Path