PowerShell 技能连载 - 使用 AD 过滤器配合 cmdlet(第 1 部分)

ActiveDirectory Powershell 模块中包含了免费的 RSAT 工具。您可以使用这个模块中的 cmdlet 来获取 AD 信息,例如用户或者组名。例如 Get-ADUserGet-ADComputer 等 cmdlet 支持服务端过滤器。不过,它们的工作方式可能和您设想的略有不同。

对于简单的查询,这些过滤器使用起来很容易。例如,这行代码筛选出前五个名字以 “A” 开头的用户,而且过滤器的语法看起来很像 PowerShell 代码:

1
Get-ADUser -Filter { name -like 'A*' } -ResultSetSize 5

不过它还不是真的 PowerShell 语法:-Filter 参数接受的是纯文本,所以您也可以使用引号代替大括号:

1
Get-ADUser -Filter " name -like 'A*' " -ResultSetSize 5

使用大括号(脚本块)仍是一个好主意,因为大括号包括的是 PowerShell 代码,所以在大括号内写代码时可以获得代码高亮和语法错误特性。脚本块稍后可以方便地转换为字符串(Get-ADUser 会自动处理)。

PowerShell 技能连载 - 创建临时文件名

当您需要写入信息到磁盘时,使用唯一的临时文件名是合理的。如果您使用静态的文件名并且多次运行您的代码,会一次又一次地覆盖写入同一个文件。如果某个人打开该文件并锁定它,将导致脚本执行失败。

以下是一些简单的生成唯一的临时文件名的方法:

1
2
3
4
5
6
7
8
9
10
11
# use a random number (slight chance of duplicates)
$path = "$env:temp\liste$(Get-Random).csv"
"Path is: $path"

# use a GUID. Guaranteed to be unique but somewhat hard on the human eye
$path = "$env:temp\liste$([Guid]::NewGuid().toString()).csv"
"Path is: $path"

# use timestamp with milliseconds
$path = "$env:temp\liste$(Get-Date -format yyyy-MM-dd_HHmmss_ffff).csv"
"Path is: $path"

PowerShell 技能连载 - 文件系统压力测试

如果您想生成一个超大文件来做压力测试,您不需要浪费时间写入大量数据到一个文件中,使它体积增大。相反,只需要设置一个期望的文件大小来占据磁盘空间即可。

以下是创建一个 1GB 测试文件的方法:

1
2
3
4
5
6
7
8
9
10
# create a test file
$path = "$env:temp\dummyFile.txt"
$file = [System.IO.File]::Create($path)

# set the file size (file uses random content)
$file.SetLength(1gb)
$file.Close()

# view file properties
Get-Item $path

PowerShell 技能连载 - 通过参数传递命令

函数参数有一个很少见的用法:用户可以传入一个输出命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Get-ProcessList
{
param
(
[string]
[ValidateSet('GridView','String')]
$OutputMode = 'String'
)

Get-Process | & "Out-$OutputMode"

}

# output as a string
Get-ProcessList -OutputMode String
# output in a grid view window
Get-ProcessList -OutputMode GridView

PowerShell 技能连载 - 处理文件编码和 BOM

当您将文本内容写入到文件时,PowerShell cmdlet 可以指定文件编码。编码决定了字符的存储方式,并且当某个字符显示乱码时,通常是由于编码不匹配造成的。

然而,有一些编码设置是无法通过 cmdlet 参数控制的。以下是一个例子。将一个进程列表保存到 CSV 文件:

1
2
3
4
$Path = "$env:temp\export.csv"

Get-Process |
Export-CSV -NoTypeInformation -UseCulture -Encoding UTF8 -Path $Path

您现在可以在 Excel 或任意文本编辑器中打开生成的 CSV 文件。当您使用 Notepad++ 打开文件时,状态栏显示编码格式为:UTF-8-BOM。

这段 PowerShell 代码以 UTF-8 编码生成文件,所以这段没有问题。BOM 代表“字节顺序标记” (Byte Order Mark)。当使用 BOM 时,将在文件的起始处增加一个特定的字节顺序标识,这样程序可以自动识别使用的编码格式。

不过,一些编辑器和数据处理系统无法处理 BOM。要移除 BOM 并使用纯文本编码,请使用类似这样的 PowerShell 代码:

1
2
3
4
5
6
function Remove-BomFromFile($OldPath, $NewPath)
{
$Content = Get-Content $OldPath -Raw
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($NewPath, $Content, $Utf8NoBomEncoding)
}

在上述例子中,要将 UTF-8-BOM 转化为纯 UTF-8,请运行这段代码:

1
2
3
$Path = "$env:temp\export.csv"
$NewPath = "$env:temp\export_new.csv"
Remove-BomFromFile -OldPath $Path -NewPath $NewPath

PowerShell 技能连载 - 查找嵌套的 Active Directory 成员(第 3 部分)

在前一个技能中我们演示了如何使用 ActiveDirectory 模块中的 cmdlet 来查找某个 Active Directory 用户所有直接和间接的成员。如果您想知道当前用户的成员信息,还有一个更简单(而且更快)的方法:用当前用户的 access token 来获取当前有效的组成员:

1
2
3
4
5
$groups = [System.Security.Principal.WindowsIdentity]::GetCurrent().Groups.Translate([System.Security.Principal.NTAccount])

$groups

$groups.Count

PowerShell 技能连载 - 查找嵌套的 Active Directory 成员(第 2 部分)

在前一个技能中我们演示了如何使用 ActiveDirectory 模块中的 cmdlet 来查找某个 Active Directory 用户所有直接和间接的成员。

如果您没有权限操作 ActiveDirectory 模块,PowerShell 也可以使用纯 .NET 方法来获取成员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function Get-NestedGroupMember
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$distinguishedName
)

process
{

$DomainController = $env:logonserver.Replace("\\","")
$Domain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController")
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Domain)
$Searcher.PageSize = 1000
$Searcher.SearchScope = "subtree"
$Searcher.Filter = "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:=$distinguishedName))"
# attention: property names are case-sensitive!
$colProplist = "name","distinguishedName"
foreach ($i in $colPropList){$Searcher.PropertiesToLoad.Add($i) | Out-Null}
$all = $Searcher.FindAll()

$all.Properties | ForEach-Object {
[PSCustomObject]@{
# attention: property names on right side are case-sensitive!
Name = $_.name[0]
DN = $_.distinguishedname[0]
} }
}
}

# make sure you specify a valid distinguishedname for a user below
Get-NestedGroupMember -distinguishedName 'CN=UserName,DC=powershell,DC=local'

PowerShell 技能连载 - 查找嵌套的 Active Directory 成员(第 1 部分)

ActiveDirectory 模块(免费的 RSAT 工具的一部分)提供许多 AD cmdlet。其中一个可以读取整个直接组的成员,例如:

1
PS> Get-ADPrincipalGroupMembership  -Identity $env:username

然而,这个 cmdlet 无法列出间接组的成员,而且它还有一个 bug:在某些场景下,它只是报告“未知错误”。

这是一个简单的读取所有组成员(包括间接成员)的替代方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Get-NestedGroupMember
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
[string]
$Identity
)

process
{
$user = Get-ADUser -Identity $Identity
$userdn = $user.DistinguishedName
$strFilter = "(member:1.2.840.113556.1.4.1941:=$userdn)"
Get-ADGroup -LDAPFilter $strFilter -ResultPageSize 1000
}
}


Get-NestedGroupMember -Identity $env:username |
Select-Object -Property Name, DistinguishedName

PowerShell 技能连载 - 进度条定时器

这是一个使用 PowerShell 进度条的简单例子。这段代码显示一个休息倒计时的进度条。只需要调整您希望暂停的秒数即可。您可以使用这个例子在班级或会议中显示休息时间:

1
2
3
4
5
6
7
8
$seconds = 60
1..$seconds |
ForEach-Object { $percent = $_ * 100 / $seconds;

Write-Progress -Activity Break -Status "$($seconds - $_) seconds remaining..." -PercentComplete $percent;

Start-Sleep -Seconds 1
}

PowerShell 技能连载 - 自动生成文档和报告(第 4 部分)

Iain Brighton 创建了一个名为 “PScribo” 的免费的 PowerShell 模块,可以快速地创建文本、HTML 或 Word 格式的文档和报告。

要使用这个模块,只需要运行这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# https://github.com/iainbrighton/PScribo
# help about_document

# create a folder to store generated documents
$OutPath = "c:\temp\out"
$exists = Test-Path -Path $OutPath
if (!$exists) { $null = New-Item -Path $OutPath -ItemType Directory -Force }


Document 'Report' {
Paragraph -Style Heading1 "System Inventory for $env:computername"
Paragraph -Style Heading2 'BIOS Information'
Paragraph 'BIOS details:' -Bold
$bios = Get-WmiObject -Class Win32_BIOS | Out-String
Paragraph $bios.Trim()
} |
Export-Document -Path $OutPath -Format Word,Html,Text

# open the generated documents
explorer $OutPath

在前一个技能中,我们演示了如何通过将对象转换为纯文本的方式,将结果从 cmdlet 添加到文本报告:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# https://github.com/iainbrighton/PScribo
# help about_document

# create a folder to store generated documents
$OutPath = "c:\temp\out"
$exists = Test-Path -Path $OutPath
if (!$exists) { $null = New-Item -Path $OutPath -ItemType Directory -Force }


Document 'Report' {
Paragraph -Style Heading1 "System Inventory for $env:computername"
Paragraph -Style Heading2 'BIOS Information'
Paragraph 'BIOS details:' -Bold
$bios = Get-WmiObject -Class Win32_BIOS | Out-String
Paragraph $bios.Trim()
} |
Export-Document -Path $OutPath -Format Word,Html,Text

# open the generated documents
explorer $OutPath

这是很直接的方法,但是比较土。如果您希望向复杂的表格添加对象结果,请试试这种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# https://github.com/iainbrighton/PScribo
# help about_document

# create a folder to store generated documents
$OutPath = "c:\temp\out"
$exists = Test-Path -Path $OutPath
if (!$exists) { $null = New-Item -Path $OutPath -ItemType Directory -Force }

# generate document
Document 'BIOS' {
# get an object with rich information
$info = Get-WmiObject -Class Win32_BIOS

# find out the property names that have actual information
$properties = $info | Get-Member -MemberType *property |
Select-Object -ExpandProperty Name |
Where-Object {

$info.$_ -ne $null -and $info.$_ -ne ''

} |
Sort-Object

# turn each property into a separate object
$infos = $properties | ForEach-Object {
[PSCustomObject]@{
Name = $_
Value = $info.$_
}


}

Paragraph -Style Heading1 "BIOS Information"

# generate a table with one line per property
$infos |
# select the properties to display, and the header texts to use
Table -Columns Name,Value -Headers 'Item','Content' -Width 0

} |
Export-Document -Path $OutPath -Format Word

# open the generated documents
explorer $OutPath

它的基本思想史为每个对象属性创建新的对象,然后以表格的方式显示它们。这段代码显示 BIOS 信息的详细报告。