PowerShell 技能连载 - 捕获本地 EXE 的错误(第 2 部分)

适用于 PowerShell 所有版本

以下是检测控制台程序发出的错误的另一种方法:

$ErrorActionPreference = 'Continue'
$result = net.exe user UserDoesNotExist 2>&1

# $? is $false when something went wrong
if ($? -eq $false) {
    # read last error:
    $errMsg = $result.Exception.Message -join ','
    Write-Host "Something went wrong: $errMsg"
} else {
    Write-Host 'All is fine.'
}

请注意 $ErrorActionPreference 的用法:当它设置为‘Stop’时,错误将被转换为一个 .NET 异常。$ErrorActionPreference 的缺省设置是‘Continue’。通过这个设置,脚本可以通过 $err 获得错误信息。

如果最后一次调用失败了,内置的 $? 变量将会返回 $false。在这种情况下,代码将会返回一条错误信息(或者做其它事情,例如写日志文件)。

PowerShell 技能连载 - 用 SDDL 替换 NTFS 权限

适用于 PowerShell 所有版本

您可以通过 Get-Acl 命令将文件和文件夹的安全信息导出成 SDDL 格式(安全描述定义语言)的纯文本文件:

$FolderToRead = 'C:\folder1'

$securityDescriptor = Get-Acl -Path $FolderToRead
$securityDescriptor.GetSecurityDescriptorSddlForm('All')

您可以将 SDDL 通过管道输出到剪贴板,然后将它粘贴到另一个脚本中:

$FolderToRead = 'C:\folder1'

$securityDescriptor = Get-Acl -Path $FolderToRead
$securityDescriptor.GetSecurityDescriptorSddlForm('All') | clip.exe

类似这样将 SDDL 加入脚本中,例如(请注意 SDDL 总是只有一行,所以请不要添加换行符):

$sddl = 'O:S-1-5-21-2649034417-1209187175-3910605729-1000G:S-1-5-21-2649034417-1209187175-3910605729-513D:(A;ID;FA;;;BA)(A;OICIIOID;GA;;;BA)(A;ID;FA;;;SY)(A;OICIIOID;GA;;;SY)(A;OICIID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)(A;OICIIOID;SDGXGWGR;;;AU)'


$FolderToConfigure = 'C:\folder2'

$securityDescriptor = Get-Acl -Path $FolderToConfigure
$securityDescriptor.SetSecurityDescriptorSddlForm($sddl)
Set-Acl -Path $FolderToConfigure -AclObject $securityDescriptor

将 SDDL 插入脚本之后,您就不再需要生成 SDDL 用的模板文件夹了。您可以将安全信息应用到其它文件系统对象中,例如设置基本 NTFS 权限,或先编辑 SDDL 再应用它。

为您提供一些启示,在域迁移的场景中,您可以比如创建一个转换表,用于将旧的 SID 转换为新的 SID。然后,将旧的 SID 替换成新的 SID,然后将记录下的安全信息克隆到一个新的(或测试的)域中。

PowerShell 技能连载 - 克隆 NTFS 权限

适用于 PowerShell 所有版本

以下代码从一个文件夹读取 NTFS 权限并将该设置应用到另外一个文件夹上。请注意两个文件夹都必须存在:

$FolderToCopyFrom = 'C:\folder1'
$FolderToCopyTo = 'C:\folder2'

$securityDescriptor = Get-Acl -Path $FolderToCopyFrom
Set-Acl -Path $FolderToCopyTo -AclObject $securityDescriptor

复制安全描述符操作可能需要管理员权限。请注意第二个文件夹的所有安全规则都会被第一个文件夹的安全信息覆盖。

PowerShell 技能连载 - 捕获本地 EXE 的错误(第 1 部分)

适用于 PowerShell 所有版本

当您运行本地控制台命令,例如 robocopy.exeipconfig.exe 或类似的命令时,您可以处理这些命令中抛出的错误:

try
{
    $current = $ErrorActionPreference
    $ErrorActionPreference = 'Stop'
    # this will cause an EXE command to emit an error
    # (replace with any console-based EXE command)
    net.exe user nonexistentUser 2>&1
    $ErrorActionPreference = $current
}
catch
{
   Write-Host ('Error occured: ' + $_.Exception.Message)
}

要捕获错误,您需要临时将 $ErrorActionPreference 设为“Stop”。另外,您需要用“2>&1”将错误信息重定向到输出控制台。

这么做完之后,例如 .NET 错误等错误就可以被 PowerShell 处理了。

PowerShell 技能连载 - 使用 WMI 继承

适用于 PowerShell 所有版本

WMI 类是彼此继承的,我们可以利用这个特性。例如这行代码:

PS> Get-WmiObject -Class Win32_Printer

它将返回通过 WMI 获取到的所有打印机。打印机是从更多的通用类继承的,这段代码可以显示继承树:

PS> Get-WmiObject -Class Win32_Printer | Select-Object -ExpandProperty __derivation -First 1
CIM_Printer
CIM_LogicalDevice
CIM_LogicalElement
CIM_ManagedSystemElement

所以如果您不只是对打印机感兴趣,而是对更多的硬件感兴趣,那么选择更通用的父类,例如 CIM_LogicalDevice。这行代码可以获取所有的硬件清单:

PS> Get-WmiObject -Class CIM_LogicalDevice

Manufacturer        Name                Status                       StatusInfo
------------        ----                ------                       ----------
Realtek             Realtek High Def... OK                                    3
                    Kona                OK
Intel Corporation   Intel(R) 8 Serie... OK
Intel Corporation   Intel(R) Wireles...
Microsoft           Microsoft Kernel...
                    ASIX AX88772B US...
Microsoft           Virtueller Micro...
Microsoft           Bluetooth-Gerät ...
Microsoft           Microsoft-ISATAP...
                    Microsoft-ISATAP...
Microsoft           Teredo Tunneling...
Microsoft           Von Microsoft ge...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    ASIX AX88772B US...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Virtueller Micro...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
-Virtual Battery 0- CRB Battery 0
(...)

这段代码将返回有关的类,这样的您就可以清晰地看到 WMI 如何调用您的硬件类型:

PS> Get-WmiObject -Class CIM_LogicalDevice |
  Group-Object -Property __Class -NoElement

Count Name
----- ----
    1 Win32_SoundDevice
    1 Win32_Battery
    1 Win32_IDEController
   20 Win32_NetworkAdapter
    1 Win32_PortableBattery
   10 Win32_Printer
    1 Win32_Processor
    2 Win32_DiskDrive
    7 Win32_DiskPartition
    1 Win32_Fan
    2 Win32_Keyboard
    5 Win32_LogicalDisk
    2 Win32_MappedLogicalDisk
    1 Win32_MemoryArray
    2 Win32_MemoryDevice
    2 Win32_PointingDevice
    1 Win32_SCSIController
    2 Win32_USBController
    6 Win32_USBHub
    5 Win32_Volume
    4 Win32_CacheMemory
    1 Win32_DesktopMonitor
    1 Win32_VideoController
    1 Win32_VoltageProbe
    1 Win32_MotherboardDevice
    8 Win32_Bus
  134 Win32_PnPEntity

It basically takes all the instances derived from CIM_LogicalDevice and groups them by “__Class” which is their real class name.
它基本上获取从 CIM_LogicalDevice 继承的所有实例并按照“__Class”分组。这是它们的真实类名。

PowerShell 技能连载 - 获取系统启动时间

适用于 PowerShell 所有版本

这个简单的函数可以返回当前系统的启动时间:

function Get-Uptime
{
  $millisec = [Environment]::TickCount
  [Timespan]::FromMilliseconds($millisec)
}

PowerShell 技能连载 - 查找进程所有者

适用于 PowerShell 所有版本

要查看某个进程的所有者以及有多少个实例在运行,请试试以下这段代码:

$ProcessName = 'explorer.exe'

(Get-WmiObject -Query "select * from Win32_Process where name='$ProcessName'").GetOwner().User

请注意:有许多办法能够查看当前登录的用户,并且根据您使用环境的不同,这里展示的方法可能有一定的局限性。它假设当前用户使用图形界面登录。由于在 server core 的机器上,只能运行非图形界面的进程,所以该脚本在该情况下不能检测连接到主机的用户名。

这个例子返回这台机器上所有“explorer.exe”进程的所有者。如果您有管理员权限并且远程进行此操作,那么该用户列表将类似已交互式登录的用户,因为每个桌面用户都创建了一个 explorer 进程。

当加入一个 Sort-Object 命令之后,您就可以轻松地排除重复项:

$ProcessName = 'explorer.exe'

(Get-WmiObject –Query "select * from Win32_Process where name='$ProcessName'").GetOwner().User |
  Sort-Object -Unique

如果改变进程名,您会发现其它有趣的东西。这段代码将列出当前通过 PowerShell 远程操作访问您机器的所有用户:

$ProcessName = 'wsmprovhost.exe'

try
{

  (Get-WmiObject -Query "select * from Win32_Process where name='$ProcessName'").GetOwner().User |
  Sort-Object -Unique
}
catch
{
  Write-Warning "No user found."
}

PowerShell 技能连载 - 解析 DISM 日志文件

适用于 PowerShell 2.0 及更高版本

在您的 Windows 文件夹中,您会见到各种系统日志文件。其中一种是 DISM 日志文件,它包含了 Windows 的配置信息(特性状态等)。

以下是一个简单的实践,演示如何解析这类日志文件并得到可用 PowerShell cmdlet 操作的富对象:

$path = "$env:windir\logs\dism\dism.log"

Get-Content -Path $path |
ForEach-Object {
  $_ -replace '\s{2,}', ','
} |
ConvertFrom-Csv -Header (1..20) |
ForEach-Object {
  $array = @()
  $array += $_.1 -split ' '
  $array += $_.2
  $array += $_.3
  $array += $_.4
  $array += $_.5
  $array -join ','
} |
ConvertFrom-Csv -Header (1..20) |
Out-GridView

PowerShell 技能连载 - 设置 Active Directory 属性

需要 ActiveDirectory 模块

PowerShell 用哈希表来设置一个用户账户的 AD 属性这是一种多功能的指定任意键值对的方法。

这个简单的例子将设置用户“_testuser_”的“_l_”和“_mail_”属性。您可以向哈希表加入任意多的键值对,假设在您的 AD schema 中不存在该属性名,并且指定的数据类型是合法的:

$infos = @{}
$infos.l = 'Bahamas'
$infos.mail = 'sunny@offshore.com'

Set-ADUser -Identity testuser -Replace $infos

PowerShell 技能连载 - 根据 Excel 表批量创建 AD 用户

需要 ActiveDirectory Module

为了创建大量新的 Active Directory 用户,您可以从 CSV 文件中导入用户信息,这个 CSV 可以由 Excel 表导出。

下一步,这段代码将会把 CSV 数据转为真实的 Active Directory 用户账户。

Import-Csv -Path F:\userlist.csv -UseCulture -Encoding Default |
ForEach-Object {

  $_.AccountPassword = $_.AccountPassword |
                           ConvertTo-SecureString -Force -AsPlainText
  $_

} |
New-ADUser -WhatIf

CSV 文件所需的只是 New-ADUser 所需要的参数作为列名。一个详尽的列表可能包含以下列名:NameSamAccountNameDescriptionCompanyCityPathAccountPassword

请注意 CSV 文件天生只能包含字符串数据类型。由于 AccountPassword 属性需要一个 SecureString 数据类型的值,所以 PowerShell 代码将从 CSV 文件中读取字符串转换为 SecureString 之后再传递给 New-ADUser

这个技术可以用于创建用户前预处理任何原始数据。